settings: added functionality to persist settings changes

also removed the request section of the app and put history in its place.
This commit is contained in:
Kevin Gorham 2019-02-27 04:44:44 -07:00 committed by Kevin Gorham
parent f37da93adc
commit 49017cb1d1
20 changed files with 329 additions and 100 deletions

View File

@ -14,8 +14,8 @@ android {
applicationId "cash.z.android.wallet"
minSdkVersion buildConfig.minSdkVersion
targetSdkVersion buildConfig.targetSdkVersion
versionCode 18 // todo: change this to 1_00_04 format, once we graduate beyond zero for the major version number because leading zeros indicate on octal number.
versionName "0.5.0-alpha"
versionCode 19 // todo: change this to 1_00_04 format, once we graduate beyond zero for the major version number because leading zeros indicate on octal number.
versionName "0.5.1-alpha"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
multiDexEnabled true

View File

@ -1,15 +1,20 @@
package cash.z.android.wallet.di.module
import android.content.SharedPreferences
import android.preference.PreferenceManager
import cash.z.android.wallet.BuildConfig
import cash.z.android.wallet.ZcashWalletApplication
import cash.z.android.wallet.sample.SampleProperties
import cash.z.android.wallet.sample.*
import cash.z.android.wallet.sample.SampleProperties.COMPACT_BLOCK_PORT
import cash.z.android.wallet.sample.SampleProperties.COMPACT_BLOCK_SERVER
import cash.z.android.wallet.sample.SampleProperties.PREFS_SERVER_NAME
import cash.z.android.wallet.sample.SampleProperties.PREFS_WALLET_DISPLAY_NAME
import cash.z.wallet.sdk.data.*
import cash.z.wallet.sdk.jni.JniConverter
import cash.z.wallet.sdk.secure.Wallet
import dagger.Module
import dagger.Provides
import javax.inject.Named
import javax.inject.Singleton
/**
@ -19,6 +24,40 @@ import javax.inject.Singleton
@Module
internal object SynchronizerModule {
@JvmStatic
@Provides
@Singleton
fun providePrefs(): SharedPreferences {
return PreferenceManager.getDefaultSharedPreferences(ZcashWalletApplication.instance)
}
@JvmStatic
@Provides
@Singleton
fun provideWalletConfig(prefs: SharedPreferences): WalletConfig {
val walletName = prefs.getString(PREFS_WALLET_DISPLAY_NAME, null)
twig("FOUND WALLET DISPLAY NAME : $walletName")
return when(walletName) {
AliceWallet.displayName -> AliceWallet
BobWallet.displayName, null -> BobWallet // Default wallet
CarolWallet.displayName -> CarolWallet
DaveWallet.displayName -> DaveWallet
else -> WalletConfig.create(walletName)
}
}
@JvmStatic
@Provides
@Singleton
@Named(PREFS_SERVER_NAME)
fun provideServer(prefs: SharedPreferences): String {
val serverName = prefs.getString(PREFS_SERVER_NAME, null)
// in theory, the actual stored value itself could be null so provide the default this way to be safe
val server = Servers.values().firstOrNull { it.displayName == serverName }?.host ?: COMPACT_BLOCK_SERVER //TODO: validate that this is a hostname or IP. For now use default, instead
twig("FOUND SERVER DISPLAY NAME : $serverName ($server)")
return server
}
@JvmStatic
@Provides
@Singleton
@ -27,35 +66,35 @@ internal object SynchronizerModule {
@JvmStatic
@Provides
@Singleton
fun provideDownloader(twigger: Twig): CompactBlockStream {
return CompactBlockStream(COMPACT_BLOCK_SERVER, COMPACT_BLOCK_PORT, twigger)
fun provideDownloader(@Named(PREFS_SERVER_NAME) server: String, twigger: Twig): CompactBlockStream {
return CompactBlockStream(server, COMPACT_BLOCK_PORT, twigger)
}
@JvmStatic
@Provides
@Singleton
fun provideProcessor(application: ZcashWalletApplication, converter: JniConverter, twigger: Twig): CompactBlockProcessor {
return CompactBlockProcessor(application, converter, SampleProperties.wallet.cacheDbName, SampleProperties.wallet.dataDbName, logger = twigger)
fun provideProcessor(application: ZcashWalletApplication, converter: JniConverter, walletConfig: WalletConfig, twigger: Twig): CompactBlockProcessor {
return CompactBlockProcessor(application, converter, walletConfig.cacheDbName, walletConfig.dataDbName, logger = twigger)
}
@JvmStatic
@Provides
@Singleton
fun provideRepository(application: ZcashWalletApplication, converter: JniConverter): TransactionRepository {
return PollingTransactionRepository(application, SampleProperties.wallet.dataDbName, 10_000L)
fun provideRepository(application: ZcashWalletApplication, walletConfig: WalletConfig, converter: JniConverter): TransactionRepository {
return PollingTransactionRepository(application, walletConfig.dataDbName, 10_000L)
}
@JvmStatic
@Provides
@Singleton
fun provideWallet(application: ZcashWalletApplication, converter: JniConverter): Wallet {
fun provideWallet(application: ZcashWalletApplication, walletConfig: WalletConfig, converter: JniConverter): Wallet {
return Wallet(
context = application,
converter = converter,
dataDbPath = application.getDatabasePath(SampleProperties.wallet.dataDbName).absolutePath,
dataDbPath = application.getDatabasePath(walletConfig.dataDbName).absolutePath,
paramDestinationDir = "${application.cacheDir.absolutePath}/params",
seedProvider = SampleProperties.wallet.seedProvider,
spendingKeyProvider = SampleProperties.wallet.spendingKeyProvider
seedProvider = walletConfig.seedProvider,
spendingKeyProvider = walletConfig.spendingKeyProvider
)
}

View File

@ -1,71 +1,116 @@
package cash.z.android.wallet.sample
import android.provider.Settings
import cash.z.wallet.sdk.data.SampleSeedProvider
import java.math.BigDecimal
import java.math.MathContext
import kotlin.properties.ReadOnlyProperty
import kotlin.properties.ReadWriteProperty
object AliceWallet {
const val name = "test.reference.alice"
val seedProvider = SampleSeedProvider(name)
val spendingKeyProvider = SampleSpendingKeySharedPref(name)
const val cacheDbName = "testalice_cache_emulator7.db"
const val dataDbName = "testalice_data_emulator7.db"
const val defaultSendAddress = "ztestsapling1wcp9fu5d3q945nwwyqxtf0dtn6pv22hmjxa39z0034ap734mvxkqz8kug4r2u2df2keekcne322" // bob's address
interface WalletConfig {
val displayName: String
val seedName: String
val seedProvider: ReadOnlyProperty<kotlin.Any?, kotlin.ByteArray>
val spendingKeyProvider: ReadWriteProperty<Any?, String>
val cacheDbName: String
val dataDbName: String
val defaultSendAddress: String
companion object {
fun create(name: String): WalletConfig {
return object : WalletConfig {
override val displayName = name
override val seedName = "test.reference.${name}_${Settings.Secure.ANDROID_ID}".sanitizeName()
override val seedProvider = SampleSeedProvider(seedName)
override val spendingKeyProvider = SampleSpendingKeySharedPref(seedName)
override val cacheDbName = "test_cache_${name.sanitizeName()}.db"
override val dataDbName = "test_data_${name.sanitizeName()}.db"
override val defaultSendAddress = BobWallet.defaultSendAddress // send to Alice by default, in other words, behave like Bob, which is the default wallet
}
}
}
}
object BobWallet {
const val name = "test.reference.bob"
val seedProvider =
SampleSeedProvider(name)
val spendingKeyProvider = SampleSpendingKeySharedPref(name)
const val cacheDbName = "testbob_cache_pixel1.db"
const val dataDbName = "testbob_data_pixel1.db"
const val defaultSendAddress = "ztestsapling1yv696xtjn3jykdej2pqx0999eydvvyfphnw97ddk2h5luyedpqzud3r87aq0d7qna3jzjqqdcvw" // alice's address
internal inline fun String.sanitizeName(): String {
return this.toLowerCase().filter {
it in 'a'..'z' || it in '0'..'9'
}
}
object CarolWallet {
const val name = "test.reference.carol"
val seedProvider =
SampleSeedProvider(name)
val spendingKeyProvider = SampleSpendingKeySharedPref(name)
const val cacheDbName = "testcarol_cache1.db"
const val dataDbName = "testcarol_data1.db"
const val defaultSendAddress = "ztestsapling1jq4dz0uurs494g0n8nywuurhyy68d6g9na8th7muuvznlux3kmsyehl89xjtu0gx58u26f4xv3d" // dave's address
object AliceWallet : WalletConfig {
override val displayName = "Alice"
override val seedName = "test.reference.$displayName".sanitizeName()
override val seedProvider = SampleSeedProvider(seedName)
override val spendingKeyProvider = SampleSpendingKeySharedPref(seedName)
override val cacheDbName = "test_cache_${displayName.sanitizeName()}.db"
override val dataDbName = "test_data_${displayName.sanitizeName()}.db"
override val defaultSendAddress =
"ztestsapling1wrjqt8et9elq7p0ejlgfpt4j9m7r7d4qlt7cke7ppp7dwrpev3yln30c37mrnzzekceajk66h0n" // bob's address
}
object DaveWallet {
const val name = "test.reference.dave"
val seedProvider =
SampleSeedProvider(name)
val spendingKeyProvider = SampleSpendingKeySharedPref(name)
const val cacheDbName = "testdave_cache.db"
const val dataDbName = "testdave_data.db"
const val defaultSendAddress = "ztestsapling1gl8rn5u3p0j9xk2vulre5fhe4rq58p4euzuxdqpgrlv7f0qxgtt2lkzd2gzqjnuhmj9yzmpp270" // carol's address
object BobWallet : WalletConfig {
override val displayName = "Bob"
override val seedName = "test.reference.$displayName".sanitizeName()
override val seedProvider = SampleSeedProvider(seedName)
override val spendingKeyProvider = SampleSpendingKeySharedPref(seedName)
override val cacheDbName = "test_cache_${displayName.sanitizeName()}.db"
override val dataDbName = "test_data_${displayName.sanitizeName()}.db"
override val defaultSendAddress =
"ztestsapling12pxv67r0kdw58q8tcn8kxhfy9n4vgaa7q8vp0dg24aueuz2mpgv2x7mw95yetcc37efc6q3hewn" // alice's address
}
object MyWallet {
const val name = "mine"
val seedProvider =
SampleImportedSeedProvider("295761fce7fdc89fa1095259f5be6375c4a36f7a214767d668f9ef6e17aa6314")
val spendingKeyProvider = SampleSpendingKeySharedPref(name)
const val cacheDbName = "wallet_cache1202.db"
const val dataDbName = "wallet_data1202.db"
const val defaultSendAddress = "ztestsapling1snmqdnfqnc407pvqw7sld8w5zxx6nd0523kvlj4jf39uvxvh7vn0hs3q38n07806dwwecqwke3t" // dummyseed
object CarolWallet : WalletConfig {
override val displayName = "Carol"
override val seedName = "test.reference.$displayName".sanitizeName()
override val seedProvider = SampleSeedProvider(seedName)
override val spendingKeyProvider = SampleSpendingKeySharedPref(seedName)
override val cacheDbName = "test_cache_${displayName.sanitizeName()}.db"
override val dataDbName = "test_data_${displayName.sanitizeName()}.db"
override val defaultSendAddress =
"ztestsapling1y480yqw6h7lwmvw9wsn3h2xxg0np93cv8nq0j3m6g8edc79faevq5adrtzyxgsmu9jfc2hdf6al" // dave's address
}
enum class Servers(val host: String) {
EMULATOR("10.0.2.2"),
WLAN("10.0.0.26"),
BOLT_TESTNET("ec2-34-228-10-162.compute-1.amazonaws.com"),
ZCASH_TESTNET("lightwalletd.z.cash")
object DaveWallet : WalletConfig {
override val displayName = "Dave"
override val seedName = "test.reference.$displayName".sanitizeName()
override val seedProvider = SampleSeedProvider(seedName)
override val spendingKeyProvider = SampleSpendingKeySharedPref(seedName)
override val cacheDbName = "test_cache_${displayName.sanitizeName()}.db"
override val dataDbName = "test_data_${displayName.sanitizeName()}.db"
override val defaultSendAddress =
"ztestsapling1efxqj5256ywqdc3zntfa0hw6yn4f83k2h7fgngwmxr3h3w7zydlencvh30730ez6p8fwg56htgz" // carol's address
}
object MyWallet : WalletConfig {
override val displayName = "MyWallet"
override val seedName = "test.reference.$displayName".sanitizeName()
override val seedProvider = SampleImportedSeedProvider("295761fce7fdc89fa1095259f5be6375c4a36f7a214767d668f9ef6e17aa6314")
override val spendingKeyProvider = SampleSpendingKeySharedPref(seedName)
override val cacheDbName = "test_cache_${displayName.sanitizeName()}.db"
override val dataDbName = "test_data_${displayName.sanitizeName()}.db"
override val defaultSendAddress =
"ztestsapling1snmqdnfqnc407pvqw7sld8w5zxx6nd0523kvlj4jf39uvxvh7vn0hs3q38n07806dwwecqwke3t" // dummyseed
}
enum class Servers(val host: String, val displayName: String) {
EMULATOR("10.0.2.2", "Localhost"),
// WLAN("10.0.0.26"),
WLAN1("10.0.2.24", "WLAN Conference"),
WLAN2("192.168.1.235", "WLAN Office"),
BOLT_TESTNET("ec2-34-228-10-162.compute-1.amazonaws.com", "Bolt Labs Testnet"),
ZCASH_TESTNET("lightwalletd.z.cash", "Zcash Testnet")
}
// TODO: load most of these properties in later, perhaps from settings
object SampleProperties {
const val PREFS_WALLET_DISPLAY_NAME = "prefs_wallet_name"
const val PREFS_SERVER_NAME = "prefs_server_name"
val COMPACT_BLOCK_SERVER = Servers.ZCASH_TESTNET.host
const val COMPACT_BLOCK_PORT = 9067
val wallet = DaveWallet
// TODO: placeholder until we have a network service for this
val USD_PER_ZEC = BigDecimal("49.07", MathContext.DECIMAL128)
val USD_PER_ZEC = BigDecimal("52.86", MathContext.DECIMAL128)
}

View File

@ -48,8 +48,6 @@ class SampleSpendingKeySharedPref(private val fileName: String) : ReadWritePrope
override fun getValue(thisRef: Any?, property: KProperty<*>): String {
val preferences = getPrefs()
PreferenceManager.getDefaultSharedPreferences(ZcashWalletApplication.instance)
return preferences.getString("spending", null)
?: throw IllegalStateException(
"Spending key was not there when we needed it! Make sure it was saved " +

View File

@ -30,6 +30,7 @@ import cash.z.android.wallet.BuildConfig
import cash.z.android.wallet.R
import cash.z.android.wallet.ZcashWalletApplication
import cash.z.android.wallet.databinding.ActivityMainBinding
import cash.z.android.wallet.sample.WalletConfig
import dagger.Module
import dagger.android.ContributesAndroidInjector
import dagger.android.support.DaggerAppCompatActivity
@ -50,6 +51,9 @@ class MainActivity : BaseActivity() {
@Inject
lateinit var synchronizer: Synchronizer
@Inject
lateinit var walletConfig: WalletConfig
lateinit var binding: ActivityMainBinding
lateinit var loadMessages: List<String>
@ -126,7 +130,7 @@ class MainActivity : BaseActivity() {
binding.navView.itemIconTintList = null
binding.navView.doOnLayout {
binding.navView.findViewById<TextView>(R.id.text_nav_header_subtitle).text = "Version ${BuildConfig.VERSION_NAME}"
binding.navView.findViewById<TextView>(R.id.text_nav_header_subtitle).text = "Version ${BuildConfig.VERSION_NAME} (${walletConfig.displayName})"
}
}

View File

@ -203,15 +203,16 @@ class HomeFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener, HomeP
toggleViews(!isShown)
}
private val stopAnimation = Runnable {
setRefreshAnimationPlaying(false).also { twig("refresh false from onRefresh") }
}
override fun onRefresh() {
setRefreshAnimationPlaying(true).also { twig("refresh true from onRefresh") }
with(binding.includeContent.refreshLayout) {
isRefreshing = false
val fauxRefresh = Random.nextLong(750L..3000L)
postDelayed({
setRefreshAnimationPlaying(false).also { twig("refresh false from onRefresh") }
}, fauxRefresh)
postDelayed(stopAnimation, fauxRefresh)
}
}
@ -279,7 +280,7 @@ class HomeFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener, HomeP
binding.includeContent.lottieActiveTransaction.setAnimation(R.raw.lottie_send_success)
binding.includeContent.lottieActiveTransaction.playAnimation()
title = "ZEC Sent"
subtitle = "awaiting network confirmation..."
subtitle = "Waiting to be mined..."
binding.includeContent.textActiveTransactionValue.text = transaction.value.convertZatoshiToZecString(3)
binding.includeContent.textActiveTransactionValue.visibility = View.VISIBLE
binding.includeContent.buttonActiveTransactionCancel.visibility = View.GONE
@ -341,6 +342,9 @@ class HomeFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener, HomeP
Toaster.short("Error: unable to find transaction to cancel!")
}
}
binding.lottieZcashBadge.setOnClickListener {
binding.lottieZcashBadge.playAnimation()
}
binding.includeContent.refreshLayout.setProgressViewEndTarget(false, (38f * resources.displayMetrics.density).toInt())
@ -514,12 +518,19 @@ class HomeFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener, HomeP
@IdRes val destination:Int
) {
/* ordered by when they need to be added to the speed dial (i.e. reverse display order) */
REQUEST(
R.id.fab_request,
R.drawable.ic_receipt_24dp,
// REQUEST(
// R.id.fab_request,
// R.drawable.ic_receipt_24dp,
// R.color.icon_request,
// R.string.destination_menu_label_request,
// R.id.nav_request_fragment
// ),
HISTORY(
R.id.fab_history,
R.drawable.ic_history_24dp,
R.color.icon_request,
R.string.destination_menu_label_request,
R.id.nav_request_fragment
R.string.destination_menu_label_history,
R.id.nav_history_fragment
),
RECEIVE(
R.id.fab_receive,

View File

@ -1,19 +1,28 @@
package cash.z.android.wallet.ui.fragment
import android.content.SharedPreferences
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ImageView
import androidx.appcompat.app.AlertDialog
import androidx.databinding.DataBindingUtil
import cash.z.android.wallet.R
import dagger.Module
import dagger.android.ContributesAndroidInjector
import cash.z.android.wallet.databinding.FragmentSettingsBinding
import cash.z.android.wallet.extention.Toaster
import cash.z.android.wallet.extention.alert
import cash.z.android.wallet.sample.SampleProperties
import dagger.Module
import dagger.android.ContributesAndroidInjector
import javax.inject.Inject
class SettingsFragment : BaseFragment() {
@Inject
lateinit var prefs: SharedPreferences
lateinit var binding: FragmentSettingsBinding
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
@ -25,7 +34,7 @@ class SettingsFragment : BaseFragment() {
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
mainActivity?.setToolbarShown(true)
mainActivity?.setToolbarShown(false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@ -36,11 +45,65 @@ class SettingsFragment : BaseFragment() {
mainActivity?.navController?.navigateUp()
}
}
binding.includeToolbar.toolbarApplyOrClose.findViewById<ImageView>(R.id.image_close).apply {
setOnClickListener {
mainActivity?.navController?.navigateUp()
}
}
binding.includeToolbar.toolbarApplyOrClose.findViewById<ImageView>(R.id.image_apply).apply {
setOnClickListener {
val userName = binding.spinnerDemoUser.selectedItem.toString()
val server = binding.spinnerServers.selectedItem.toString()
view.context.alert("Are you sure you want to apply these changes?\n\nUser: $userName\nServer: $server\n\nTHIS WILL EXIT THE APP.") {
onApplySettings(userName, server)
view.postDelayed({ mainActivity?.finish() }, 2000L)
}
}
}
binding.spinnerServers.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onNothingSelected(parent: AdapterView<*>?) {
setCustomServerUiShown(false)
}
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
val item = binding.spinnerDemoUser.selectedItem.toString()
setCustomServerUiShown(item.startsWith("Custom"))
}
}
binding.spinnerDemoUser.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onNothingSelected(parent: AdapterView<*>?) {
setCustomUserUiShown(false)
}
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
val item = binding.spinnerDemoUser.selectedItem.toString()
setCustomUserUiShown(item.startsWith("Custom"))
}
}
}
private fun setCustomServerUiShown(isShown: Boolean) {
if (isShown) Toaster.short("Custom servers are not yet implemented")
}
private fun setCustomUserUiShown(isShown: Boolean) {
if (isShown) Toaster.short("Custom users are not yet implemented")
}
private fun onApplySettings(userName: String, server: String) {
AlertDialog.Builder(mainActivity!!).setMessage("Changing everything...").show()
prefs.edit().apply {
putString(SampleProperties.PREFS_SERVER_NAME, server)
putString(SampleProperties.PREFS_WALLET_DISPLAY_NAME, userName)
}.apply()
}
}
@Module
@Module
abstract class SettingsFragmentModule {
@ContributesAndroidInjector
abstract fun contributeSettingsFragment(): SettingsFragment

View File

@ -5,20 +5,23 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.navigation.NavOptions
import androidx.navigation.fragment.FragmentNavigatorExtras
import androidx.transition.TransitionInflater
import cash.z.android.wallet.BuildConfig
import cash.z.android.wallet.R
import cash.z.android.wallet.databinding.FragmentWelcomeBinding
import cash.z.android.wallet.sample.WalletConfig
import dagger.Module
import dagger.android.ContributesAndroidInjector
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import javax.inject.Inject
class WelcomeFragment : ProgressFragment(R.id.progress_welcome) {
@Inject
lateinit var walletConfig: WalletConfig
private lateinit var binding: FragmentWelcomeBinding
//
@ -40,8 +43,10 @@ class WelcomeFragment : ProgressFragment(R.id.progress_welcome) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val userName = walletConfig.displayName.substringAfterLast('.').capitalize()
val network = if (resources.getBoolean(R.bool.is_testnet)) "Testnet 2.0.1" else "Mainnet 2.0.1"
var buildInfo = "PoC v${BuildConfig.VERSION_NAME} $network\nZcash Company - For demo purposes only"
var buildInfo = "PoC v${BuildConfig.VERSION_NAME} $network\n" +
"Zcash Company - For demo purposes only\nUser: $userName"
binding.textWelcomeBuildInfo.text = buildInfo
}

View File

@ -48,7 +48,7 @@ class SendPresenter @Inject constructor(
*/
private val minersFee = 10_000L
private var balanceJob: Job? = null
private var requiresValidation = true
private var requiresValidation = false
var sendUiModel = SendUiModel()
// TODO: find the best set of characters here. Possibly add something to the rust layer to help with this.

View File

@ -22,7 +22,7 @@ class LottieLooper(private val lottie: LottieAnimationView, private val loopRang
progress = 0f
repeatCount = 0
addAnimatorListener(this@LottieLooper)
playAnimation()
lottie.post { playAnimation() }
}
isPlaying = true
}
@ -39,6 +39,7 @@ class LottieLooper(private val lottie: LottieAnimationView, private val loopRang
}
override fun onAnimationRepeat(animation: Animator?) {
onAnimationEnd(animation)
}
override fun onAnimationEnd(animation: Animator?) {
@ -81,7 +82,8 @@ class LottieLooper(private val lottie: LottieAnimationView, private val loopRang
lottie.progress = 1.0f
// wait around a bit to see if my listeners detect any movement, then quietly make my getaway
lottie.postDelayed({
lottie.removeAnimatorListener(this)
lottie.removeAllAnimatorListeners()
// lottie.removeAnimatorListener(this)
lottie.setMinAndMaxFrame(1, lastFrame)
}, 500L)
}

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
</vector>

View File

@ -4,6 +4,6 @@
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:fillColor="@color/zcashBlue"
android:pathData="M13,3c-4.97,0 -9,4.03 -9,9L1,12l3.89,3.89 0.07,0.14L9,12L6,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0 -3.68,-0.79 -4.94,-2.06l-1.42,1.42C8.27,19.99 10.51,21 13,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zM12,8v5l4.28,2.54 0.72,-1.21 -3.5,-2.08L13.5,8L12,8z"/>
</vector>

View File

@ -109,8 +109,8 @@
android:text="@string/cancel"
android:textColor="@color/zcashRed"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="@id/transition_active_transaction_bg"
app:layout_constraintTop_toBottomOf="@id/transition_active_transaction_bg" />
app:layout_constraintEnd_toEndOf="@+id/camera_placeholder"
app:layout_constraintTop_toBottomOf="@+id/camera_placeholder" />
<TextView
android:id="@+id/text_amount_background"
@ -349,8 +349,8 @@
android:id="@+id/camera_placeholder"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="@id/transition_active_transaction_bg"
app:layout_constraintEnd_toEndOf="@id/transition_active_transaction_bg"
app:layout_constraintBottom_toBottomOf="@id/input_zcash_address"
app:layout_constraintEnd_toEndOf="@id/input_zcash_address"
app:layout_constraintStart_toStartOf="@id/transition_active_transaction_bg"
app:layout_constraintTop_toTopOf="@id/transition_active_transaction_bg"
tools:visibility="gone" />
@ -498,7 +498,13 @@
text_dollar_symbol_header,
text_amount_background,
text_value_header,
image_swap_currency" />
image_swap_currency,
text_to_label,
input_zcash_address,
text_address_error,
image_address_shortcut,
text_value_error,
image_scan_qr" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>

View File

@ -9,9 +9,8 @@
android:background="@color/fragment_history_background">
<include
android:id="@+id/main_app_bar"
layout="@layout/include_main_app_bar"
android:visibility="invisible"
android:id="@+id/include_toolbar"
layout="@layout/include_apply_or_close_app_bar"
tools:ignore="MissingConstraints" />
<!-- -->
@ -42,7 +41,7 @@
android:textSize="@dimen/text_size_caption"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="@id/guideline_content_start"
app:layout_constraintTop_toBottomOf="@id/main_app_bar" />
app:layout_constraintTop_toBottomOf="@id/include_toolbar" />
<androidx.appcompat.widget.AppCompatSpinner
android:id="@+id/spinner_demo_user"

View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/main_app_bar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/ZcashTheme.AppBarOverlay"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar_apply_or_close"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:paddingEnd="16dp"
android:paddingStart="16dp"
app:popupTheme="@style/ZcashTheme.PopupOverlay">
<ImageView
android:id="@+id/image_close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/ic_close_black_24dp"
android:tint="@color/zcashWhite" />
<ImageView
android:id="@+id/image_apply"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/ic_check_black_24dp"
android:layout_gravity="right"
android:foregroundGravity="right"
android:tint="@color/zcashWhite" />
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.AppBarLayout>
</layout>

View File

@ -14,19 +14,20 @@
android:id="@+id/nav_receive_fragment"
android:icon="@drawable/ic_qrcode_24dp"
android:title="@string/destination_menu_label_receive" />
<item
android:id="@+id/nav_request_fragment"
android:icon="@drawable/ic_receipt_24dp"
android:title="@string/destination_menu_label_request" />
android:id="@+id/nav_history_fragment"
android:icon="@drawable/ic_history_24dp"
android:title="@string/destination_menu_label_history" />
<!--<item-->
<!--android:id="@+id/nav_request_fragment"-->
<!--android:icon="@drawable/ic_receipt_24dp"-->
<!--android:title="@string/destination_menu_label_request" />-->
</group>
<group
android:id="@+id/misc_actions"
android:checkableBehavior="single">
<item
android:id="@+id/nav_history_fragment"
android:icon="@drawable/ic_history_24dp"
android:title="@string/destination_menu_label_history" />
<item
android:id="@+id/nav_about_fragment"
android:icon="@drawable/ic_chat_24dp"

View File

@ -2,5 +2,6 @@
<resources>
<item name="fab_send" type="id"/>
<item name="fab_receive" type="id" />
<item name="fab_request" type="id" />
<item name="fab_history" type="id" />
<!--<item name="fab_request" type="id" />-->
</resources>

View File

@ -98,10 +98,16 @@
<string-array name="demo_user_names">
<item>Alice</item>
<item>Bob</item>
<item>Carol</item>
<item>Dave</item>
<item>Custom...</item>
</string-array>
<string-array name="server_names">
<item>Localhost</item>
<item>Zcash Testnet</item>
<item>Bolt Labs Testnet</item>
<item>WLAN Office</item>
<item>WLAN Conference</item>
<item>Localhost</item>
<item>Custom...</item>
</string-array>
</resources>

View File

@ -15,8 +15,8 @@ import javax.inject.Singleton
@Module
internal object SynchronizerModule {
// const val MOCK_LOAD_DURATION = 3_000L
const val MOCK_LOAD_DURATION = 12_000L
const val MOCK_LOAD_DURATION = 3_000L
// const val MOCK_LOAD_DURATION = 12_000L
const val MOCK_TX_INTERVAL = 20_000L
// const val MOCK_TX_INTERVAL = 5_000L
const val MOCK_ACTIVE_TX_STATE_CHANGE_INTERVAL = 7_000L