181 lines
6.6 KiB
Kotlin
181 lines
6.6 KiB
Kotlin
package cash.z.ecc.android.sdk.demoapp.demos.listtransactions
|
|
|
|
import android.os.Bundle
|
|
import android.view.LayoutInflater
|
|
import android.view.Menu
|
|
import android.view.View
|
|
import android.view.ViewGroup
|
|
import androidx.lifecycle.lifecycleScope
|
|
import androidx.recyclerview.widget.LinearLayoutManager
|
|
import cash.z.ecc.android.bip39.Mnemonics
|
|
import cash.z.ecc.android.bip39.toSeed
|
|
import cash.z.ecc.android.sdk.Synchronizer
|
|
import cash.z.ecc.android.sdk.block.CompactBlockProcessor
|
|
import cash.z.ecc.android.sdk.demoapp.BaseDemoFragment
|
|
import cash.z.ecc.android.sdk.demoapp.R
|
|
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.internal.twig
|
|
import cash.z.ecc.android.sdk.model.Account
|
|
import cash.z.ecc.android.sdk.model.LightWalletEndpoint
|
|
import cash.z.ecc.android.sdk.model.TransactionOverview
|
|
import cash.z.ecc.android.sdk.model.ZcashNetwork
|
|
import cash.z.ecc.android.sdk.model.defaultForNetwork
|
|
import cash.z.ecc.android.sdk.tool.DerivationTool
|
|
import kotlinx.coroutines.runBlocking
|
|
|
|
/**
|
|
* List all transactions related to the given seed, since the given birthday. This begins by
|
|
* downloading any missing blocks and then validating and scanning their contents. Once scan is
|
|
* complete, the transactions are available in the database and can be accessed by any SQL tool.
|
|
* By default, the SDK uses a PagedTransactionRepository to provide transaction contents from the
|
|
* database in a paged format that works natively with RecyclerViews.
|
|
*/
|
|
@Suppress("TooManyFunctions")
|
|
class ListTransactionsFragment : BaseDemoFragment<FragmentListTransactionsBinding>() {
|
|
private lateinit var synchronizer: Synchronizer
|
|
private lateinit var adapter: TransactionAdapter
|
|
private lateinit var address: String
|
|
private var status: Synchronizer.Status? = null
|
|
private val isSynced get() = status == Synchronizer.Status.SYNCED
|
|
|
|
/**
|
|
* Initialize the required values that would normally live outside the demo but are repeated
|
|
* here for completeness so that each demo file can serve as a standalone example.
|
|
*/
|
|
private fun setup() {
|
|
// defaults to the value of `DemoConfig.seedWords` but can also be set by the user
|
|
var 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
|
|
val seed = Mnemonics.MnemonicCode(seedPhrase).toSeed()
|
|
val network = ZcashNetwork.fromResources(requireApplicationContext())
|
|
address = runBlocking {
|
|
DerivationTool.deriveUnifiedAddress(
|
|
seed,
|
|
ZcashNetwork.fromResources(requireApplicationContext()),
|
|
Account.DEFAULT
|
|
)
|
|
}
|
|
synchronizer = Synchronizer.newBlocking(
|
|
requireApplicationContext(),
|
|
network,
|
|
lightWalletEndpoint = LightWalletEndpoint.defaultForNetwork(network),
|
|
seed = seed,
|
|
birthday = sharedViewModel.birthdayHeight.value
|
|
)
|
|
}
|
|
|
|
private fun initTransactionUI() {
|
|
binding.recyclerTransactions.layoutManager =
|
|
LinearLayoutManager(activity, LinearLayoutManager.VERTICAL, false)
|
|
adapter = TransactionAdapter()
|
|
binding.recyclerTransactions.adapter = adapter
|
|
}
|
|
|
|
private fun monitorChanges() {
|
|
// the lifecycleScope is used to stop everything when the fragment dies
|
|
synchronizer.status.collectWith(lifecycleScope, ::onStatus)
|
|
synchronizer.processorInfo.collectWith(lifecycleScope, ::onProcessorInfoUpdated)
|
|
synchronizer.progress.collectWith(lifecycleScope, ::onProgress)
|
|
|
|
synchronizer.clearedTransactions.collectWith(lifecycleScope) {
|
|
onTransactionsUpdated(it)
|
|
}
|
|
}
|
|
|
|
//
|
|
// Change listeners
|
|
//
|
|
|
|
private fun onProcessorInfoUpdated(info: CompactBlockProcessor.ProcessorInfo) {
|
|
if (info.isScanning) binding.textInfo.text = "Scanning blocks...${info.scanProgress}%"
|
|
}
|
|
|
|
@Suppress("MagicNumber")
|
|
private fun onProgress(i: Int) {
|
|
if (i < 100) binding.textInfo.text = "Downloading blocks...$i%"
|
|
}
|
|
|
|
private fun onStatus(status: Synchronizer.Status) {
|
|
this.status = status
|
|
binding.textStatus.text = "Status: $status"
|
|
if (isSynced) onSyncComplete()
|
|
}
|
|
|
|
private fun onSyncComplete() {
|
|
binding.textInfo.visibility = View.INVISIBLE
|
|
}
|
|
|
|
private fun onTransactionsUpdated(transactions: List<TransactionOverview>) {
|
|
twig("got a new paged list of transactions")
|
|
adapter.submitList(transactions)
|
|
|
|
// show message when there are no transactions
|
|
if (isSynced) {
|
|
binding.textInfo.apply {
|
|
if (transactions.isEmpty()) {
|
|
visibility = View.VISIBLE
|
|
text =
|
|
"No transactions found. Try to either change the seed words " +
|
|
"or send funds to this address (tap the FAB to copy it):\n\n $address"
|
|
} else {
|
|
visibility = View.INVISIBLE
|
|
text = ""
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Android Lifecycle overrides
|
|
//
|
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
super.onCreate(savedInstanceState)
|
|
setHasOptionsMenu(true)
|
|
}
|
|
|
|
override fun onCreateView(
|
|
inflater: LayoutInflater,
|
|
container: ViewGroup?,
|
|
savedInstanceState: Bundle?
|
|
): View? {
|
|
val view = super.onCreateView(inflater, container, savedInstanceState)
|
|
setup()
|
|
return view
|
|
}
|
|
|
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
super.onViewCreated(view, savedInstanceState)
|
|
initTransactionUI()
|
|
}
|
|
|
|
override fun onPrepareOptionsMenu(menu: Menu) {
|
|
super.onPrepareOptionsMenu(menu)
|
|
// We rather hide options menu actions while actively using the Synchronizer
|
|
menu.setGroupVisible(R.id.main_menu_group, false)
|
|
}
|
|
|
|
override fun onResume() {
|
|
super.onResume()
|
|
// the lifecycleScope is used to dispose of the synchronizer when the fragment dies
|
|
synchronizer.start(lifecycleScope)
|
|
monitorChanges()
|
|
}
|
|
|
|
//
|
|
// Base Fragment overrides
|
|
//
|
|
|
|
override fun onActionButtonClicked() {
|
|
if (::address.isInitialized) copyToClipboard(address)
|
|
}
|
|
|
|
override fun inflateBinding(layoutInflater: LayoutInflater): FragmentListTransactionsBinding =
|
|
FragmentListTransactionsBinding.inflate(layoutInflater)
|
|
}
|