[#260] Depend on sdk-lib instead of published library

Because the demo app was using an old version of the SDK, this also implements a variety of changes to accomodate the new SDK API
This commit is contained in:
Carter Jernigan 2021-09-04 07:05:41 -04:00
parent a03e3e054f
commit 3afeb0f688
25 changed files with 165 additions and 147 deletions

View File

@ -53,8 +53,8 @@ android {
dependencies {
// SDK
zcashmainnetImplementation 'cash.z.ecc.android:zcash-android-sdk-mainnet:1.2.1-beta02'
zcashtestnetImplementation 'cash.z.ecc.android:zcash-android-sdk-testnet:1.2.1-beta02'
implementation(project(":sdk-lib"))
//implementation('cash.z.ecc.android:zcash-android-sdk:1.3.0-beta17')
// sample mnemonic plugin
implementation 'com.github.zcash:zcash-android-wallet-plugins:1.0.1'

View File

@ -6,15 +6,9 @@ import cash.z.ecc.android.sdk.ext.Twig
class App : Application() {
var defaultConfig = DemoConfig()
override fun onCreate() {
instance = this
super.onCreate()
Twig.plant(TroubleshootingTwig())
}
companion object {
lateinit var instance: App
}
}

View File

@ -17,8 +17,10 @@ import androidx.navigation.ui.navigateUp
import androidx.navigation.ui.setupActionBarWithNavController
import androidx.navigation.ui.setupWithNavController
import androidx.viewbinding.ViewBinding
import cash.z.ecc.android.sdk.demoapp.util.fromResources
import cash.z.ecc.android.sdk.service.LightWalletGrpcService
import cash.z.ecc.android.sdk.service.LightWalletService
import cash.z.ecc.android.sdk.type.ZcashNetwork
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.navigation.NavigationView
@ -65,7 +67,7 @@ class MainActivity : AppCompatActivity(), ClipboardManager.OnPrimaryClipChangedL
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
drawerLayout.addDrawerListener(this)
initService()
}
@ -73,7 +75,7 @@ class MainActivity : AppCompatActivity(), ClipboardManager.OnPrimaryClipChangedL
super.onDestroy()
lightwalletService?.shutdown()
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
// Inflate the menu; this adds items to the action bar if it is present.
menuInflater.inflate(R.menu.main, menu)
@ -99,21 +101,19 @@ class MainActivity : AppCompatActivity(), ClipboardManager.OnPrimaryClipChangedL
//
// Private functions
//
private fun initService() {
if (lightwalletService != null) {
lightwalletService?.shutdown()
}
with(App.instance.defaultConfig) {
lightwalletService = LightWalletGrpcService(App.instance, host, port)
}
lightwalletService = LightWalletGrpcService(applicationContext, ZcashNetwork.fromResources(applicationContext))
}
private fun onFabClicked(view: View) {
fabListener?.onActionButtonClicked()
}
//
// Helpers
//

View File

@ -10,8 +10,7 @@ import kotlinx.coroutines.flow.StateFlow
*/
class SharedViewModel : ViewModel() {
private val config = App.instance.defaultConfig
private val _seedPhrase = MutableStateFlow(config.initialSeedWords)
private val _seedPhrase = MutableStateFlow(DemoConstants.initialSeedWords)
// publicly, this is read-only
val seedPhrase: StateFlow<String> get() = _seedPhrase

View File

@ -6,7 +6,11 @@ import cash.z.ecc.android.bip39.Mnemonics
import cash.z.ecc.android.bip39.toSeed
import cash.z.ecc.android.sdk.demoapp.BaseDemoFragment
import cash.z.ecc.android.sdk.demoapp.databinding.FragmentGetAddressBinding
import cash.z.ecc.android.sdk.demoapp.ext.requireApplicationContext
import cash.z.ecc.android.sdk.demoapp.util.fromResources
import cash.z.ecc.android.sdk.tool.DerivationTool
import cash.z.ecc.android.sdk.type.UnifiedViewingKey
import cash.z.ecc.android.sdk.type.ZcashNetwork
/**
* Displays the address associated with the seed defined by the default config. To modify the seed
@ -14,7 +18,7 @@ import cash.z.ecc.android.sdk.tool.DerivationTool
*/
class GetAddressFragment : BaseDemoFragment<FragmentGetAddressBinding>() {
private lateinit var viewingKey: String
private lateinit var viewingKey: UnifiedViewingKey
private lateinit var seed: ByteArray
/**
@ -23,21 +27,20 @@ class GetAddressFragment : BaseDemoFragment<FragmentGetAddressBinding>() {
*/
private fun setup() {
// defaults to the value of `DemoConfig.seedWords` but can also be set by the user
var seedPhrase = sharedViewModel.seedPhrase.value
val seedPhrase = sharedViewModel.seedPhrase.value
// Use a BIP-39 library to convert a seed phrase into a byte array. Most wallets already
// have the seed stored
seed = Mnemonics.MnemonicCode(seedPhrase).toSeed()
// the derivation tool can be used for generating keys and addresses
viewingKey = DerivationTool.deriveViewingKeys(seed).first()
viewingKey = DerivationTool.deriveUnifiedViewingKeys(seed, ZcashNetwork.fromResources(requireApplicationContext())).first()
}
private fun displayAddress() {
// alternatively, `deriveAddress` can take the seed as a parameter instead
// although, a full fledged app would just get the address from the synchronizer
val zaddress = DerivationTool.deriveShieldedAddress(viewingKey)
val taddress = DerivationTool.deriveTransparentAddress(seed)
// a full fledged app would just get the address from the synchronizer
val zaddress = DerivationTool.deriveShieldedAddress(seed, ZcashNetwork.fromResources(requireApplicationContext()))
val taddress = DerivationTool.deriveTransparentAddress(seed, ZcashNetwork.fromResources(requireApplicationContext()))
binding.textInfo.text = "z-addr:\n$zaddress\n\n\nt-addr:\n$taddress"
}
@ -47,8 +50,8 @@ class GetAddressFragment : BaseDemoFragment<FragmentGetAddressBinding>() {
// Android Lifecycle overrides
//
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setup()
}
@ -64,7 +67,7 @@ class GetAddressFragment : BaseDemoFragment<FragmentGetAddressBinding>() {
override fun onActionButtonClicked() {
copyToClipboard(
DerivationTool.deriveShieldedAddress(viewingKey),
DerivationTool.deriveShieldedAddress(viewingKey.extfvk, ZcashNetwork.fromResources(requireApplicationContext())),
"Shielded address copied to clipboard!"
)
}

View File

@ -12,9 +12,13 @@ import cash.z.ecc.android.sdk.block.CompactBlockProcessor
import cash.z.ecc.android.sdk.demoapp.App
import cash.z.ecc.android.sdk.demoapp.BaseDemoFragment
import cash.z.ecc.android.sdk.demoapp.databinding.FragmentGetBalanceBinding
import cash.z.ecc.android.sdk.demoapp.ext.requireApplicationContext
import cash.z.ecc.android.sdk.demoapp.util.fromResources
import cash.z.ecc.android.sdk.ext.collectWith
import cash.z.ecc.android.sdk.ext.convertZatoshiToZecString
import cash.z.ecc.android.sdk.tool.DerivationTool
import cash.z.ecc.android.sdk.type.WalletBalance
import cash.z.ecc.android.sdk.type.ZcashNetwork
/**
* Displays the available balance && total balance associated with the seed defined by the default config.
@ -27,8 +31,8 @@ class GetBalanceFragment : BaseDemoFragment<FragmentGetBalanceBinding>() {
override fun inflateBinding(layoutInflater: LayoutInflater): FragmentGetBalanceBinding =
FragmentGetBalanceBinding.inflate(layoutInflater)
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setup()
}
@ -41,17 +45,15 @@ class GetBalanceFragment : BaseDemoFragment<FragmentGetBalanceBinding>() {
val seed = Mnemonics.MnemonicCode(seedPhrase).toSeed()
// converting seed into viewingKey
val viewingKey = DerivationTool.deriveViewingKeys(seed).first()
val viewingKey = DerivationTool.deriveUnifiedViewingKeys(seed, ZcashNetwork.fromResources(requireApplicationContext())).first()
// using the ViewingKey to initialize
App.instance.defaultConfig.let { config ->
Initializer(App.instance) {
it.importWallet(viewingKey, config.birthdayHeight)
it.server(config.host, config.port)
Initializer(requireApplicationContext()) {
it.setNetwork(ZcashNetwork.fromResources(requireApplicationContext()))
it.importWallet(viewingKey, network = ZcashNetwork.fromResources(requireApplicationContext()))
}.let { initializer ->
synchronizer = Synchronizer(initializer)
}
}
}
override fun onResume() {
@ -65,10 +67,10 @@ class GetBalanceFragment : BaseDemoFragment<FragmentGetBalanceBinding>() {
synchronizer.status.collectWith(lifecycleScope, ::onStatus)
synchronizer.progress.collectWith(lifecycleScope, ::onProgress)
synchronizer.processorInfo.collectWith(lifecycleScope, ::onProcessorInfoUpdated)
synchronizer.balances.collectWith(lifecycleScope, ::onBalance)
synchronizer.saplingBalances.collectWith(lifecycleScope, ::onBalance)
}
private fun onBalance(balance: CompactBlockProcessor.WalletBalance) {
private fun onBalance(balance: WalletBalance) {
binding.textBalance.text = """
Available balance: ${balance.availableZatoshi.convertZatoshiToZecString(12)}
Total balance: ${balance.totalZatoshi.convertZatoshiToZecString(12)}
@ -79,10 +81,10 @@ class GetBalanceFragment : BaseDemoFragment<FragmentGetBalanceBinding>() {
private fun onStatus(status: Synchronizer.Status) {
binding.textStatus.text = "Status: $status"
if (CompactBlockProcessor.WalletBalance().none()) {
if (WalletBalance().none()) {
binding.textBalance.text = "Calculating balance..."
} else {
onBalance(synchronizer.latestBalance)
onBalance(synchronizer.saplingBalances.value)
}
}
@ -95,9 +97,9 @@ class GetBalanceFragment : BaseDemoFragment<FragmentGetBalanceBinding>() {
/**
* Extension function which checks if the balance has been updated or its -1
*/
private fun CompactBlockProcessor.WalletBalance.none(): Boolean{
if(synchronizer.latestBalance.totalZatoshi == -1L
&& synchronizer.latestBalance.availableZatoshi == -1L) return true
private fun WalletBalance.none(): Boolean{
if(synchronizer.saplingBalances.value.totalZatoshi == -1L
&& synchronizer.saplingBalances.value.availableZatoshi == -1L) return true
return false
}

View File

@ -6,6 +6,7 @@ import android.view.LayoutInflater
import android.view.View
import cash.z.ecc.android.sdk.demoapp.BaseDemoFragment
import cash.z.ecc.android.sdk.demoapp.databinding.FragmentGetBlockBinding
import cash.z.ecc.android.sdk.demoapp.ext.requireApplicationContext
import cash.z.ecc.android.sdk.demoapp.util.mainActivity
import cash.z.ecc.android.sdk.demoapp.util.toHtml
import cash.z.ecc.android.sdk.demoapp.util.toRelativeTime
@ -27,7 +28,7 @@ class GetBlockFragment : BaseDemoFragment<FragmentGetBlockBinding>() {
binding.textInfo.text = Html.fromHtml(
"""
<b>block height:</b> ${block?.height.withCommas()}
<br/><b>block time:</b> ${block?.time.toRelativeTime()}
<br/><b>block time:</b> ${block?.time.toRelativeTime(requireApplicationContext())}
<br/><b>number of shielded TXs:</b> ${block?.vtxCount}
<br/><b>hash:</b> ${block?.hash?.toByteArray()?.toHex()}
<br/><b>prevHash:</b> ${block?.prevHash?.toByteArray()?.toHex()}

View File

@ -7,6 +7,7 @@ import android.view.View
import cash.z.ecc.android.sdk.demoapp.BaseDemoFragment
import cash.z.ecc.android.sdk.demoapp.R
import cash.z.ecc.android.sdk.demoapp.databinding.FragmentGetBlockRangeBinding
import cash.z.ecc.android.sdk.demoapp.ext.requireApplicationContext
import cash.z.ecc.android.sdk.demoapp.util.mainActivity
import cash.z.ecc.android.sdk.demoapp.util.toRelativeTime
import cash.z.ecc.android.sdk.demoapp.util.withCommas
@ -48,7 +49,7 @@ class GetBlockRangeFragment : BaseDemoFragment<FragmentGetBlockRangeBinding>() {
<b>total blocks:</b> ${count.withCommas()}
<br/><b>fetch time:</b> ${if (fetchDelta > 1000) "%.2f sec".format(fetchDelta/1000.0) else "%d ms".format(fetchDelta)}
<br/><b>process time:</b> ${if (processTime > 1000) "%.2f sec".format(processTime/1000.0) else "%d ms".format(processTime)}
<br/><b>block time range:</b> ${first().time.toRelativeTime()}<br/>&nbsp;&nbsp to ${last().time.toRelativeTime()}
<br/><b>block time range:</b> ${first().time.toRelativeTime(requireApplicationContext())}<br/>&nbsp;&nbsp to ${last().time.toRelativeTime(requireApplicationContext())}
<br/><b>total empty blocks:</b> ${emptyCount.withCommas()}
<br/><b>total TXs:</b> ${txCount.withCommas()}
<br/><b>total outputs:</b> ${outCount.withCommas()}

View File

@ -6,7 +6,10 @@ import cash.z.ecc.android.bip39.Mnemonics
import cash.z.ecc.android.bip39.toSeed
import cash.z.ecc.android.sdk.demoapp.BaseDemoFragment
import cash.z.ecc.android.sdk.demoapp.databinding.FragmentGetPrivateKeyBinding
import cash.z.ecc.android.sdk.demoapp.ext.requireApplicationContext
import cash.z.ecc.android.sdk.demoapp.util.fromResources
import cash.z.ecc.android.sdk.tool.DerivationTool
import cash.z.ecc.android.sdk.type.ZcashNetwork
/**
* Displays the viewing key and spending key associated with the seed used during the demo. The
@ -34,10 +37,10 @@ class GetPrivateKeyFragment : BaseDemoFragment<FragmentGetPrivateKeyBinding>() {
private fun displayKeys() {
// derive the keys from the seed:
// demonstrate deriving spending keys for five accounts but only take the first one
val spendingKey = DerivationTool.deriveSpendingKeys(seed, 5).first()
val spendingKey = DerivationTool.deriveSpendingKeys(seed, ZcashNetwork.fromResources(requireApplicationContext()),5).first()
// derive the key that allows you to view but not spend transactions
val viewingKey = DerivationTool.deriveViewingKey(spendingKey)
val viewingKey = DerivationTool.deriveViewingKey(spendingKey, ZcashNetwork.fromResources(requireApplicationContext()))
// display the keys in the UI
binding.textInfo.setText("Spending Key:\n$spendingKey\n\nViewing Key:\n$viewingKey")
@ -65,7 +68,7 @@ class GetPrivateKeyFragment : BaseDemoFragment<FragmentGetPrivateKeyBinding>() {
override fun onActionButtonClicked() {
copyToClipboard(
DerivationTool.deriveViewingKeys(seed, 1).first(),
DerivationTool.deriveUnifiedViewingKeys(seed, ZcashNetwork.fromResources(requireApplicationContext())).first().extpub,
"ViewingKey copied to clipboard!"
)
}

View File

@ -4,7 +4,6 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import androidx.lifecycle.lifecycleScope
import androidx.paging.PagedList
import androidx.recyclerview.widget.LinearLayoutManager
import cash.z.ecc.android.bip39.Mnemonics
import cash.z.ecc.android.bip39.toSeed
@ -16,9 +15,12 @@ import cash.z.ecc.android.sdk.db.entity.ConfirmedTransaction
import cash.z.ecc.android.sdk.demoapp.App
import cash.z.ecc.android.sdk.demoapp.BaseDemoFragment
import cash.z.ecc.android.sdk.demoapp.databinding.FragmentListTransactionsBinding
import cash.z.ecc.android.sdk.demoapp.ext.requireApplicationContext
import cash.z.ecc.android.sdk.demoapp.util.fromResources
import cash.z.ecc.android.sdk.ext.collectWith
import cash.z.ecc.android.sdk.ext.twig
import cash.z.ecc.android.sdk.tool.DerivationTool
import cash.z.ecc.android.sdk.type.ZcashNetwork
/**
* List all transactions related to the given seed, since the given birthday. This begins by
@ -28,7 +30,7 @@ import cash.z.ecc.android.sdk.tool.DerivationTool
* database in a paged format that works natively with RecyclerViews.
*/
class ListTransactionsFragment : BaseDemoFragment<FragmentListTransactionsBinding>() {
private lateinit var initializer: SdkSynchronizer.SdkInitializer
private lateinit var initializer: Initializer
private lateinit var synchronizer: Synchronizer
private lateinit var adapter: TransactionAdapter<ConfirmedTransaction>
private lateinit var address: String
@ -48,13 +50,11 @@ class ListTransactionsFragment : BaseDemoFragment<FragmentListTransactionsBindin
// have the seed stored
val seed = Mnemonics.MnemonicCode(seedPhrase).toSeed()
App.instance.defaultConfig.let { config ->
initializer = Initializer(App.instance) {
it.importWallet(seed, config.birthdayHeight)
it.server(config.host, config.port)
}
address = DerivationTool.deriveShieldedAddress(seed)
initializer = Initializer(requireApplicationContext()) {
it.importWallet(seed, network = ZcashNetwork.fromResources(requireApplicationContext()))
it.setNetwork(ZcashNetwork.fromResources(requireApplicationContext()))
}
address = DerivationTool.deriveShieldedAddress(seed, ZcashNetwork.fromResources(requireApplicationContext()))
synchronizer = Synchronizer(initializer)
}
@ -96,7 +96,7 @@ class ListTransactionsFragment : BaseDemoFragment<FragmentListTransactionsBindin
binding.textInfo.visibility = View.INVISIBLE
}
private fun onTransactionsUpdated(transactions: PagedList<ConfirmedTransaction>) {
private fun onTransactionsUpdated(transactions: List<ConfirmedTransaction>) {
twig("got a new paged list of transactions")
adapter.submitList(transactions)

View File

@ -2,8 +2,8 @@ package cash.z.ecc.android.sdk.demoapp.demos.listtransactions
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.paging.PagedListAdapter
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import cash.z.ecc.android.sdk.demoapp.R
import cash.z.ecc.android.sdk.db.entity.ConfirmedTransaction
@ -11,7 +11,7 @@ import cash.z.ecc.android.sdk.db.entity.ConfirmedTransaction
* Simple adapter implementation that knows how to bind a recyclerview to ClearedTransactions.
*/
class TransactionAdapter<T : ConfirmedTransaction> :
PagedListAdapter<T, TransactionViewHolder<T>>(
ListAdapter<T, TransactionViewHolder<T>>(
object : DiffUtil.ItemCallback<T>() {
override fun areItemsTheSame(
oldItem: T,
@ -21,7 +21,7 @@ class TransactionAdapter<T : ConfirmedTransaction> :
override fun areContentsTheSame(
oldItem: T,
newItem: T
) = oldItem.equals(newItem)
) = oldItem == newItem
}
) {

View File

@ -35,7 +35,7 @@ class TransactionViewHolder<T : ConfirmedTransaction>(itemView: View) : Recycler
icon.rotation = if (isInbound) 0f else 180f
icon.rotation = if (isInbound) 0f else 180f
icon.setColorFilter(ContextCompat.getColor(App.instance, if (isInbound) R.color.tx_inbound else R.color.tx_outbound))
icon.setColorFilter(ContextCompat.getColor(itemView.context, if (isInbound) R.color.tx_inbound else R.color.tx_outbound))
}
private fun getMemoString(transaction: T?): String {

View File

@ -3,9 +3,7 @@ package cash.z.ecc.android.sdk.demoapp.demos.listutxos
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.widget.Toast
import androidx.lifecycle.lifecycleScope
import androidx.paging.PagedList
import androidx.recyclerview.widget.LinearLayoutManager
import cash.z.ecc.android.bip39.Mnemonics
import cash.z.ecc.android.bip39.toSeed
@ -16,16 +14,15 @@ import cash.z.ecc.android.sdk.block.CompactBlockProcessor
import cash.z.ecc.android.sdk.db.entity.ConfirmedTransaction
import cash.z.ecc.android.sdk.demoapp.App
import cash.z.ecc.android.sdk.demoapp.BaseDemoFragment
import cash.z.ecc.android.sdk.demoapp.DemoConstants
import cash.z.ecc.android.sdk.demoapp.databinding.FragmentListUtxosBinding
import cash.z.ecc.android.sdk.demoapp.ext.requireApplicationContext
import cash.z.ecc.android.sdk.demoapp.util.fromResources
import cash.z.ecc.android.sdk.demoapp.util.mainActivity
import cash.z.ecc.android.sdk.ext.ZcashSdk
import cash.z.ecc.android.sdk.ext.collectWith
import cash.z.ecc.android.sdk.ext.twig
import cash.z.ecc.android.sdk.service.LightWalletGrpcService
import cash.z.ecc.android.sdk.service.LightWalletService
import cash.z.ecc.android.sdk.rpc.LocalRpcTypes
import cash.z.ecc.android.sdk.tool.DerivationTool
import cash.z.ecc.android.sdk.tool.WalletBirthdayTool
import cash.z.ecc.android.sdk.type.ZcashNetwork
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@ -45,9 +42,8 @@ import kotlinx.coroutines.withContext
* database in a paged format that works natively with RecyclerViews.
*/
class ListUtxosFragment : BaseDemoFragment<FragmentListUtxosBinding>() {
private val config = App.instance.defaultConfig
private lateinit var seed: ByteArray
private lateinit var initializer: SdkSynchronizer.SdkInitializer
private lateinit var initializer: Initializer
private lateinit var synchronizer: Synchronizer
private lateinit var adapter: UtxoAdapter<ConfirmedTransaction>
private val address: String = "t1RwbKka1CnktvAJ1cSqdn7c6PXWG4tZqgd"
@ -66,22 +62,22 @@ class ListUtxosFragment : BaseDemoFragment<FragmentListUtxosBinding>() {
// Use a BIP-39 library to convert a seed phrase into a byte array. Most wallets already
// have the seed stored
seed = Mnemonics.MnemonicCode(sharedViewModel.seedPhrase.value).toSeed()
initializer = Initializer(App.instance) {
it.importWallet(seed, config.birthdayHeight)
initializer = Initializer(requireApplicationContext()) {
it.importWallet(seed, network = ZcashNetwork.fromResources(requireApplicationContext()))
it.alias = "Demo_Utxos"
}
synchronizer = Synchronizer(initializer)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setup()
}
fun initUi() {
binding.inputAddress.setText(address)
binding.inputRangeStart.setText(ZcashSdk.SAPLING_ACTIVATION_HEIGHT.toString())
binding.inputRangeEnd.setText(config.utxoEndHeight.toString())
binding.inputRangeStart.setText(ZcashNetwork.fromResources(requireApplicationContext()).saplingActivationHeight.toString())
binding.inputRangeEnd.setText(DemoConstants.utxoEndHeight.toString())
binding.buttonLoad.setOnClickListener {
mainActivity()?.hideKeyboard()
@ -97,8 +93,8 @@ class ListUtxosFragment : BaseDemoFragment<FragmentListUtxosBinding>() {
binding.textStatus.post {
binding.textStatus.requestFocus()
val addressToUse = binding.inputAddress.text.toString()
val startToUse = binding.inputRangeStart.text.toString().toIntOrNull() ?: ZcashSdk.SAPLING_ACTIVATION_HEIGHT
val endToUse = binding.inputRangeEnd.text.toString().toIntOrNull() ?: config.utxoEndHeight
val startToUse = binding.inputRangeStart.text.toString().toIntOrNull() ?: ZcashNetwork.fromResources(requireApplicationContext()).saplingActivationHeight
val endToUse = binding.inputRangeEnd.text.toString().toIntOrNull() ?: DemoConstants.utxoEndHeight
var allStart = now
twig("loading transactions in range $startToUse..$endToUse")
val txids = lightwalletService?.getTAddressTransactions(addressToUse, startToUse..endToUse)
@ -114,11 +110,13 @@ class ListUtxosFragment : BaseDemoFragment<FragmentListUtxosBinding>() {
}
}
}?.let { txData ->
val parseStart = now
val tList = LocalRpcTypes.TransactionDataList.newBuilder().addAllData(txData).build()
val parsedTransactions = initializer.rustBackend.parseTransactionDataList(tList)
delta = now - parseStart
updateStatus("parsed txs in ${delta}ms.")
// Disabled during migration to newer SDK version; this appears to have been
// leveraging non-public APIs in the SDK so perhaps should be removed
// val parseStart = now
// val tList = LocalRpcTypes.TransactionDataList.newBuilder().addAllData(txData).build()
// val parsedTransactions = initializer.rustBackend.parseTransactionDataList(tList)
// delta = now - parseStart
// updateStatus("parsed txs in ${delta}ms.")
}
(synchronizer as SdkSynchronizer).refreshTransactions()
// val finalCount = (synchronizer as SdkSynchronizer).getTransactionCount()
@ -158,7 +156,7 @@ class ListUtxosFragment : BaseDemoFragment<FragmentListUtxosBinding>() {
super.onResume()
resetInBackground()
val seed = Mnemonics.MnemonicCode(sharedViewModel.seedPhrase.value).toSeed()
binding.inputAddress.setText(DerivationTool.deriveTransparentAddress(seed))
binding.inputAddress.setText(DerivationTool.deriveTransparentAddress(seed, ZcashNetwork.fromResources(requireApplicationContext())))
}
@ -168,6 +166,7 @@ class ListUtxosFragment : BaseDemoFragment<FragmentListUtxosBinding>() {
try {
lifecycleScope.launch {
withContext(Dispatchers.IO) {
synchronizer.prepare()
initialCount = (synchronizer as SdkSynchronizer).getTransactionCount()
}
}
@ -232,7 +231,7 @@ class ListUtxosFragment : BaseDemoFragment<FragmentListUtxosBinding>() {
binding.textStatus.visibility = View.INVISIBLE
}
private fun onTransactionsUpdated(transactions: PagedList<ConfirmedTransaction>) {
private fun onTransactionsUpdated(transactions: List<ConfirmedTransaction>) {
twig("got a new paged list of transactions of size ${transactions.size}")
adapter.submitList(transactions)
}

View File

@ -2,16 +2,16 @@ package cash.z.ecc.android.sdk.demoapp.demos.listutxos
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.paging.PagedListAdapter
import androidx.recyclerview.widget.DiffUtil
import cash.z.ecc.android.sdk.demoapp.R
import androidx.recyclerview.widget.ListAdapter
import cash.z.ecc.android.sdk.db.entity.ConfirmedTransaction
import cash.z.ecc.android.sdk.demoapp.R
/**
* Simple adapter implementation that knows how to bind a recyclerview to ClearedTransactions.
*/
class UtxoAdapter<T : ConfirmedTransaction> :
PagedListAdapter<T, UtxoViewHolder<T>>(
ListAdapter<T, UtxoViewHolder<T>>(
object : DiffUtil.ItemCallback<T>() {
override fun areItemsTheSame(
oldItem: T,
@ -21,7 +21,7 @@ class UtxoAdapter<T : ConfirmedTransaction> :
override fun areContentsTheSame(
oldItem: T,
newItem: T
) = oldItem.equals(newItem)
) = oldItem == newItem
}
) {

View File

@ -11,12 +11,16 @@ import cash.z.ecc.android.sdk.Initializer
import cash.z.ecc.android.sdk.Synchronizer
import cash.z.ecc.android.sdk.block.CompactBlockProcessor
import cash.z.ecc.android.sdk.db.entity.*
import cash.z.ecc.android.sdk.demoapp.App
import cash.z.ecc.android.sdk.demoapp.BaseDemoFragment
import cash.z.ecc.android.sdk.demoapp.DemoConstants
import cash.z.ecc.android.sdk.demoapp.databinding.FragmentSendBinding
import cash.z.ecc.android.sdk.demoapp.ext.requireApplicationContext
import cash.z.ecc.android.sdk.demoapp.util.fromResources
import cash.z.ecc.android.sdk.demoapp.util.mainActivity
import cash.z.ecc.android.sdk.ext.*
import cash.z.ecc.android.sdk.tool.DerivationTool
import cash.z.ecc.android.sdk.type.WalletBalance
import cash.z.ecc.android.sdk.type.ZcashNetwork
/**
* Demonstrates sending funds to an address. This is the most complex example that puts all of the
@ -49,22 +53,20 @@ class SendFragment : BaseDemoFragment<FragmentSendBinding>() {
// have the seed stored
val seed = Mnemonics.MnemonicCode(seedPhrase).toSeed()
App.instance.defaultConfig.let { config ->
Initializer(App.instance) {
it.importWallet(seed, config.birthdayHeight)
it.server(config.host, config.port)
}.let { initializer ->
synchronizer = Synchronizer(initializer)
}
spendingKey = DerivationTool.deriveSpendingKeys(seed).first()
Initializer(requireApplicationContext()) {
it.importWallet(seed, network = ZcashNetwork.fromResources(requireApplicationContext()))
it.setNetwork(ZcashNetwork.fromResources(requireApplicationContext()))
}.let { initializer ->
synchronizer = Synchronizer(initializer)
}
spendingKey = DerivationTool.deriveSpendingKeys(seed, ZcashNetwork.fromResources(requireApplicationContext())).first()
}
//
// Observable properties (done without livedata or flows for simplicity)
//
private var balance = CompactBlockProcessor.WalletBalance()
private var balance = WalletBalance()
set(value) {
field = value
onUpdateSendButton()
@ -87,13 +89,11 @@ class SendFragment : BaseDemoFragment<FragmentSendBinding>() {
//
private fun initSendUi() {
App.instance.defaultConfig.let { config ->
amountInput = binding.inputAmount.apply {
setText(config.sendAmount.toZecString())
}
addressInput = binding.inputAddress.apply {
setText(config.toAddress)
}
amountInput = binding.inputAmount.apply {
setText(DemoConstants.sendAmount.toZecString())
}
addressInput = binding.inputAddress.apply {
setText(DemoConstants.toAddress)
}
binding.buttonSend.setOnClickListener(::onSend)
}
@ -102,7 +102,7 @@ class SendFragment : BaseDemoFragment<FragmentSendBinding>() {
synchronizer.status.collectWith(lifecycleScope, ::onStatus)
synchronizer.progress.collectWith(lifecycleScope, ::onProgress)
synchronizer.processorInfo.collectWith(lifecycleScope, ::onProcessorInfoUpdated)
synchronizer.balances.collectWith(lifecycleScope, ::onBalance)
synchronizer.saplingBalances.collectWith(lifecycleScope, ::onBalance)
}
@ -133,7 +133,7 @@ class SendFragment : BaseDemoFragment<FragmentSendBinding>() {
if (info.isScanning) binding.textStatus.text = "Scanning blocks...${info.scanProgress}%"
}
private fun onBalance(balance: CompactBlockProcessor.WalletBalance) {
private fun onBalance(balance: WalletBalance) {
this.balance = balance
if (!isSyncing) {
binding.textBalance.text = """

View File

@ -0,0 +1,9 @@
package cash.z.ecc.android.sdk.demoapp.ext
import androidx.fragment.app.Fragment
/**
* A safer alternative to [Fragment.requireContext], as it avoids leaking Fragment or Activity context
* when Application context is often sufficient.
*/
fun Fragment.requireApplicationContext() = requireContext().applicationContext

View File

@ -1,5 +1,6 @@
package cash.z.ecc.android.sdk.demoapp.util
import android.content.Context
import android.text.format.DateUtils
import androidx.fragment.app.Fragment
import cash.z.ecc.android.sdk.demoapp.App
@ -22,10 +23,10 @@ fun Number?.withCommas() = this?.let { "%,d".format(it) } ?: "Unknown"
/**
* Convert date time in seconds to relative time like (4 days ago).
*/
fun Int?.toRelativeTime() =
fun Int?.toRelativeTime(context: Context) =
this?.let { timeInSeconds ->
DateUtils.getRelativeDateTimeString(
App.instance,
context,
timeInSeconds * 1000L,
DateUtils.SECOND_IN_MILLIS,
DateUtils.WEEK_IN_MILLIS,

View File

@ -0,0 +1,8 @@
package cash.z.ecc.android.sdk.demoapp.util
import android.content.Context
import cash.z.ecc.android.sdk.demoapp.R
import cash.z.ecc.android.sdk.type.ZcashNetwork
fun ZcashNetwork.Companion.fromResources(context: Context) = ZcashNetwork.valueOf(context.getString(
R.string.network_name))

View File

@ -9,10 +9,10 @@ import cash.z.ecc.android.sdk.demoapp.App
"show how to bridge to an existing key storage mechanism. Instead, use the Android " +
"Keystore system or a 3rd party library that leverages it."
)
class SampleStorage {
class SampleStorage(context: Context) {
private val prefs =
App.instance.getSharedPreferences("ExtremelyInsecureStorage", Context.MODE_PRIVATE)
private val prefs =
context.applicationContext.getSharedPreferences("ExtremelyInsecureStorage", Context.MODE_PRIVATE)
fun saveSensitiveString(key: String, value: String) {
prefs.edit().putString(key, value).apply()
@ -33,8 +33,8 @@ class SampleStorage {
* the SDK. This class delegates to the storage object. For demo purposes, we're using an insecure
* SampleStorage implementation but this can easily be swapped for a truly secure storage solution.
*/
class SampleStorageBridge() {
private val delegate = SampleStorage()
class SampleStorageBridge(context: Context) {
private val delegate = SampleStorage(context.applicationContext)
/**
* Just a sugar method to help with being explicit in sample code. We want to show developers

View File

@ -1,16 +0,0 @@
package cash.z.ecc.android.sdk.demoapp
data class DemoConfig(
val alias: String = "SdkDemo",
val host: String = "mainnet.lightwalletd.com",
val port: Int = 9067,
val birthdayHeight: Int = 968000,
val utxoEndHeight: Int = 968085,
val sendAmount: Double = 0.000018,
// corresponds to address: zs15tzaulx5weua5c7l47l4pku2pw9fzwvvnsp4y80jdpul0y3nwn5zp7tmkcclqaca3mdjqjkl7hx
val initialSeedWords: String = "wish puppy smile loan doll curve hole maze file ginger hair nose key relax knife witness cannon grab despair throw review deal slush frame",
// corresponds to seed: urban kind wise collect social marble riot primary craft lucky head cause syrup odor artist decorate rhythm phone style benefit portion bus truck top
val toAddress: String = "zs1lcdmue7rewgvzh3jd09sfvwq3sumu6hkhpk53q94kcneuffjkdg9e3tyxrugkmpza5c3c5e6eqh"
)

View File

@ -0,0 +1,14 @@
package cash.z.ecc.android.sdk.demoapp
object DemoConstants {
val utxoEndHeight: Int = 968085
val sendAmount: Double = 0.000018
// corresponds to address: zs15tzaulx5weua5c7l47l4pku2pw9fzwvvnsp4y80jdpul0y3nwn5zp7tmkcclqaca3mdjqjkl7hx
val initialSeedWords: String =
"wish puppy smile loan doll curve hole maze file ginger hair nose key relax knife witness cannon grab despair throw review deal slush frame"
// corresponds to seed: urban kind wise collect social marble riot primary craft lucky head cause syrup odor artist decorate rhythm phone style benefit portion bus truck top
val toAddress: String =
"zs1lcdmue7rewgvzh3jd09sfvwq3sumu6hkhpk53q94kcneuffjkdg9e3tyxrugkmpza5c3c5e6eqh"
}

View File

@ -1,4 +1,5 @@
<resources>
<string name="app_name">Mainnet Demo</string>
<string name="nav_header_title">Android SDK Demo : MAINNET</string>
<string name="network_name">Mainnet</string>
</resources>

View File

@ -1,16 +0,0 @@
package cash.z.ecc.android.sdk.demoapp
data class DemoConfig(
val alias: String = "SdkDemo",
val host: String = "testnet.lightwalletd.com",
val port: Int = 9067,
val birthdayHeight: Int = 954_500,
val utxoEndHeight: Int = 1075590,
val sendAmount: Double = 0.00017,
// corresponds to address: ztestsapling1zhqvuq8zdwa8nsnde7074kcfsat0w25n08jzuvz5skzcs6h9raxu898l48xwr8fmkny3zqqrgd9
val initialSeedWords: String = "wish puppy smile loan doll curve hole maze file ginger hair nose key relax knife witness cannon grab despair throw review deal slush frame",
// corresponds to seed: urban kind wise collect social marble riot primary craft lucky head cause syrup odor artist decorate rhythm phone style benefit portion bus truck top
val toAddress: String = "ztestsapling1ddttvrm6ueug4vwlczs8daqjaul60aur4udnvcz9qdnjt9ekt2tsxheqvv3mn50wvhmzj4ge9rl"
)

View File

@ -0,0 +1,14 @@
package cash.z.ecc.android.sdk.demoapp
object DemoConstants {
val utxoEndHeight: Int = 1075590
val sendAmount: Double = 0.00017
// corresponds to address: ztestsapling1zhqvuq8zdwa8nsnde7074kcfsat0w25n08jzuvz5skzcs6h9raxu898l48xwr8fmkny3zqqrgd9
val initialSeedWords: String =
"wish puppy smile loan doll curve hole maze file ginger hair nose key relax knife witness cannon grab despair throw review deal slush frame"
// corresponds to seed: urban kind wise collect social marble riot primary craft lucky head cause syrup odor artist decorate rhythm phone style benefit portion bus truck top
val toAddress: String =
"ztestsapling1ddttvrm6ueug4vwlczs8daqjaul60aur4udnvcz9qdnjt9ekt2tsxheqvv3mn50wvhmzj4ge9rl"
}

View File

@ -1,4 +1,5 @@
<resources>
<string name="app_name">Testnet Demo</string>
<string name="nav_header_title">Android SDK Demo : TESTNET</string>
<string name="network_name">Testnet</string>
</resources>