Merge pull request #275 from ccjernigan/260-jcenter
Fix demo app crashes; update dependencies
This commit is contained in:
commit
e01671c288
|
@ -53,12 +53,12 @@ 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'
|
||||
implementation 'cash.z.ecc.android:kotlin-bip39:1.0.1'
|
||||
implementation 'cash.z.ecc.android:kotlin-bip39:1.0.2'
|
||||
|
||||
// Android
|
||||
implementation 'androidx.core:core-ktx:1.3.2'
|
||||
|
@ -69,4 +69,9 @@ dependencies {
|
|||
testImplementation 'junit:junit:4.13'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
|
||||
|
||||
implementation "io.grpc:grpc-android:1.39.0"
|
||||
implementation "io.grpc:grpc-okhttp:1.39.0"
|
||||
implementation "io.grpc:grpc-protobuf-lite:1.39.0"
|
||||
implementation "io.grpc:grpc-stub:1.39.0"
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
//
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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!"
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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()}
|
||||
|
|
|
@ -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/>   to ${last().time.toRelativeTime()}
|
||||
<br/><b>block time range:</b> ${first().time.toRelativeTime(requireApplicationContext())}<br/>   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()}
|
||||
|
|
|
@ -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!"
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
) {
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
) {
|
||||
|
||||
|
|
|
@ -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 = """
|
||||
|
|
|
@ -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
|
|
@ -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,
|
||||
|
|
|
@ -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))
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
)
|
|
@ -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"
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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"
|
||||
)
|
|
@ -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"
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -13,7 +13,6 @@ dependencyResolutionManagement {
|
|||
google()
|
||||
mavenCentral()
|
||||
maven("https://jitpack.io")
|
||||
jcenter() // still needed by one dependency in the demo-app project
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue