diff --git a/zcash-android-wallet-app/app/libs/zcash-android-wallet-sdk-1.6.0.aar b/zcash-android-wallet-app/app/libs/zcash-android-wallet-sdk-1.6.0.aar index 28ec3c6..90cc496 100644 Binary files a/zcash-android-wallet-app/app/libs/zcash-android-wallet-sdk-1.6.0.aar and b/zcash-android-wallet-app/app/libs/zcash-android-wallet-sdk-1.6.0.aar differ diff --git a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/di/module/SynchronizerModule.kt b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/di/module/SynchronizerModule.kt index d0ef543..4b3914f 100644 --- a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/di/module/SynchronizerModule.kt +++ b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/di/module/SynchronizerModule.kt @@ -1,24 +1,16 @@ package cash.z.android.wallet.di.module -import android.util.Log import cash.z.android.wallet.BuildConfig import cash.z.android.wallet.ZcashWalletApplication -import cash.z.android.wallet.di.module.Properties.CACHE_DB_NAME -import cash.z.android.wallet.di.module.Properties.COMPACT_BLOCK_PORT -import cash.z.android.wallet.di.module.Properties.COMPACT_BLOCK_SERVER -import cash.z.android.wallet.di.module.Properties.DATA_DB_NAME -import cash.z.android.wallet.di.module.Properties.SEED_PROVIDER -import cash.z.android.wallet.di.module.Properties.SPENDING_KEY_PROVIDER +import cash.z.android.wallet.sample.SampleProperties +import cash.z.android.wallet.sample.SampleProperties.COMPACT_BLOCK_PORT +import cash.z.android.wallet.sample.SampleProperties.COMPACT_BLOCK_SERVER 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 okio.ByteString -import java.nio.charset.Charset import javax.inject.Singleton -import kotlin.properties.ReadOnlyProperty -import kotlin.reflect.KProperty /** * Module that contributes all the objects necessary for the synchronizer, which is basically everything that has @@ -43,21 +35,21 @@ internal object SynchronizerModule { @Provides @Singleton fun provideProcessor(application: ZcashWalletApplication, converter: JniConverter, twigger: Twig): CompactBlockProcessor { - return CompactBlockProcessor(application, converter, CACHE_DB_NAME, DATA_DB_NAME, logger = twigger) + return CompactBlockProcessor(application, converter, SampleProperties.wallet.cacheDbName, SampleProperties.wallet.dataDbName, logger = twigger) } @JvmStatic @Provides @Singleton fun provideRepository(application: ZcashWalletApplication, converter: JniConverter, twigger: Twig): TransactionRepository { - return PollingTransactionRepository(application, DATA_DB_NAME, 10_000L, converter, twigger) + return PollingTransactionRepository(application, SampleProperties.wallet.dataDbName, 10_000L, converter, twigger) } @JvmStatic @Provides @Singleton fun provideWallet(application: ZcashWalletApplication, converter: JniConverter, twigger: Twig): Wallet { - return Wallet(converter, application.getDatabasePath(DATA_DB_NAME).absolutePath, "${application.cacheDir.absolutePath}/params", seedProvider = SEED_PROVIDER, spendingKeyProvider = SPENDING_KEY_PROVIDER, logger = twigger) + return Wallet(converter, application.getDatabasePath(SampleProperties.wallet.dataDbName).absolutePath, "${application.cacheDir.absolutePath}/params", seedProvider = SampleProperties.wallet.seedProvider, spendingKeyProvider = SampleProperties.wallet.spendingKeyProvider, logger = twigger) } @JvmStatic @@ -91,55 +83,3 @@ internal object SynchronizerModule { } } - - -// TODO: load most of these properties in later, perhaps from settings -object Properties { - val COMPACT_BLOCK_SERVER = Servers.EMULATOR.host - const val COMPACT_BLOCK_PORT = 9067 - const val CACHE_DB_NAME = "wallet_cache4821.db" - const val DATA_DB_NAME = "wallet_data4821.db" - val SEED_PROVIDER = SampleSeedProvider("dummyseed") - val SPENDING_KEY_PROVIDER = SampleSpendingKeyProvider("dummyseed") -} - -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") -} -class SampleImportedSeedProvider(private val seedHex: String) : ReadOnlyProperty { - override fun getValue(thisRef: Any?, property: KProperty<*>): ByteArray { - val bytes = ByteString.decodeHex(seedHex).toByteArray() - val stringBytes = String(bytes, Charset.forName("UTF-8")) - Log.e("TWIG-x", "byteString: $stringBytes") - return decodeHex(seedHex).also { Log.e("TWIG-x", "$it") } - } - - - fun decodeHex(hex: String): ByteArray { - val result = ByteArray(hex.length / 2) - for (i in result.indices) { - val d1 = decodeHexDigit(hex[i * 2]) shl 4 - val d2 = decodeHexDigit(hex[i * 2 + 1]) - result[i] = (d1 + d2).toByte() - } - return result - } - private fun decodeHexDigit(c: Char): Int { - if (c in '0'..'9') return c - '0' - if (c in 'a'..'f') return c - 'a' + 10 - if (c in 'A'..'F') return c - 'A' + 10 - throw IllegalArgumentException("Unexpected hex digit: $c") - } -} - - -class SampleSpendingKeyProvider2(private val seedValue: String) : ReadOnlyProperty { - override fun getValue(thisRef: Any?, property: KProperty<*>): String { - // dynamically generating keyes, based on seed is out of scope for this sample - return "secret-extended-key-test1q0ks5jkcqqqqpqywf2mh5g2aw5smt252mqscphjr8svrqyvgtgss0av3jh37jc05pngstr6qcqu5x64zuk8entc97pfla68jd7g9fyhwv5l8pdey662qy3lr07w9yddpgwdlwt3tjgzhpszatyw90kpn4zs7feu5cudwnxcpts5k0za96xy0wt59nu7hg3ntalck7gwhn0nuyztmf8yceuhp0fn3wmrtr9mk9v6fhg8hwvsxp0thr4cn9r8pc0w3zh45czmnr7e3mrctlzaq7" -// return "secret-extended-key-test1q0f0urnmqqqqpqxlree5urprcmg9pdgvr2c88qhm862etv65eu84r9zwannpz4g88299xyhv7wf9xkecag653jlwwwyxrymfraqsnz8qfgds70qjammscxxyl7s7p9xz9w906epdpy8ztsjd7ez7phcd5vj7syx68sjskqs8j9lef2uuacghsh8puuvsy9u25pfvcdznta33qe6xh5lrlnhdkgymnpdug4jm6tpf803cad6tqa9c0ewq9l03fqxatevm97jmuv8u0ccxjews5" - } -} \ No newline at end of file diff --git a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/sample/SampleConfig.kt b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/sample/SampleConfig.kt new file mode 100644 index 0000000..8941d97 --- /dev/null +++ b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/sample/SampleConfig.kt @@ -0,0 +1,44 @@ +package cash.z.android.wallet.sample + +import cash.z.wallet.sdk.data.SampleSeedProvider + +object AliceWallet { + const val name = "test.reference.alice" + val seedProvider = SampleSeedProvider(name) + val spendingKeyProvider = SampleSpendingKeySharedPref(name) + const val cacheDbName = "testalice_cache.db" + const val dataDbName = "testalice_data.db" +} + +object BobWallet { + const val name = "test.reference.bob" + val seedProvider = + SampleSeedProvider(name) + val spendingKeyProvider = SampleSpendingKeySharedPref(name) + const val cacheDbName = "testalice_cache.db" + const val dataDbName = "testalice_data.db" +} + +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" +} + +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") +} + + +// TODO: load most of these properties in later, perhaps from settings +object SampleProperties { + val COMPACT_BLOCK_SERVER = Servers.EMULATOR.host + const val COMPACT_BLOCK_PORT = 9067 + val wallet = AliceWallet +} \ No newline at end of file diff --git a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/sample/SampleProviders.kt b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/sample/SampleProviders.kt new file mode 100644 index 0000000..f7c69a2 --- /dev/null +++ b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/sample/SampleProviders.kt @@ -0,0 +1,73 @@ +package cash.z.android.wallet.sample + +import android.preference.PreferenceManager +import android.util.Log +import cash.z.android.wallet.ZcashWalletApplication +import okio.ByteString +import java.nio.charset.Charset +import kotlin.properties.ReadOnlyProperty +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty +import android.R.id.edit +import android.content.Context +import android.content.SharedPreferences +import java.lang.IllegalStateException + +@Deprecated(message = InsecureWarning.message) +class SampleImportedSeedProvider(private val seedHex: String) : ReadOnlyProperty { + override fun getValue(thisRef: Any?, property: KProperty<*>): ByteArray { + val bytes = ByteString.decodeHex(seedHex).toByteArray() + val stringBytes = String(bytes, Charset.forName("UTF-8")) + Log.e("TWIG-x", "byteString: $stringBytes") + return decodeHex(seedHex).also { Log.e("TWIG-x", "$it") } + } + + fun decodeHex(hex: String): ByteArray { + val result = ByteArray(hex.length / 2) + for (i in result.indices) { + val d1 = decodeHexDigit(hex[i * 2]) shl 4 + val d2 = decodeHexDigit(hex[i * 2 + 1]) + result[i] = (d1 + d2).toByte() + } + return result + } + + private fun decodeHexDigit(c: Char): Int { + if (c in '0'..'9') return c - '0' + if (c in 'a'..'f') return c - 'a' + 10 + if (c in 'A'..'F') return c - 'A' + 10 + throw IllegalArgumentException("Unexpected hex digit: $c") + } +} + +@Deprecated(message = InsecureWarning.message) +class SampleSpendingKeySharedPref(private val fileName: String) : ReadWriteProperty { + + private fun getPrefs() = ZcashWalletApplication.instance + .getSharedPreferences(fileName, Context.MODE_PRIVATE) + + 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 " + + "during the first run of the app, when accounts were created!" + ) + } + + override fun setValue(thisRef: Any?, property: KProperty<*>, value: String) { + Log.e("TWIG", "Spending key is being stored") + val preferences = getPrefs() + val editor = preferences.edit() + editor.putString("spending", value) + editor.apply() + } + +} + +internal object InsecureWarning { + const val message = "Do not use this because it is insecure and only intended for test code and samples. " + + "Instead, use the Android Keystore system or a 3rd party library that leverages it." +} \ No newline at end of file diff --git a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/fragment/HistoryFragment.kt b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/fragment/HistoryFragment.kt index b367ea4..99faf60 100644 --- a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/fragment/HistoryFragment.kt +++ b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/fragment/HistoryFragment.kt @@ -3,21 +3,23 @@ package cash.z.android.wallet.ui.fragment import android.os.Bundle import android.view.LayoutInflater import android.view.View -import cash.z.android.wallet.R import android.view.ViewGroup import androidx.databinding.DataBindingUtil +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import cash.z.android.wallet.R +import cash.z.android.wallet.databinding.FragmentHistoryBinding +import cash.z.android.wallet.ui.adapter.TransactionAdapter import cash.z.android.wallet.ui.presenter.HistoryPresenter +import cash.z.android.wallet.ui.util.AlternatingRowColorDecoration +import cash.z.wallet.sdk.dao.WalletTransaction import dagger.Module import dagger.android.ContributesAndroidInjector import kotlinx.coroutines.launch -import cash.z.android.wallet.databinding.FragmentHistoryBinding -import cash.z.wallet.sdk.dao.WalletTransaction class HistoryFragment : BaseFragment(), HistoryPresenter.HistoryView { - - override val titleResId: Int get() = R.string.destination_title_history lateinit var historyPresenter: HistoryPresenter lateinit var binding: FragmentHistoryBinding @@ -28,6 +30,25 @@ class HistoryFragment : BaseFragment(), HistoryPresenter.HistoryView { .root } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + mainActivity.let { mainActivity -> + mainActivity.setSupportActionBar(view.findViewById(R.id.toolbar)) + mainActivity.supportActionBar?.setDisplayHomeAsUpEnabled(true) + mainActivity.supportActionBar?.setTitle(R.string.destination_title_history) + } + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + historyPresenter = HistoryPresenter(this, mainActivity.synchronizer) + binding.recyclerTransactionsHistory.apply { + layoutManager = LinearLayoutManager(activity, RecyclerView.VERTICAL, false) + adapter = TransactionAdapter() + addItemDecoration(AlternatingRowColorDecoration()) + } + } + override fun onResume() { super.onResume() launch { @@ -41,7 +62,8 @@ class HistoryFragment : BaseFragment(), HistoryPresenter.HistoryView { } override fun setTransactions(transactions: List) { - } + (binding.recyclerTransactionsHistory.adapter as TransactionAdapter).submitList(transactions) + } } @Module diff --git a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/fragment/HomeFragment.kt b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/fragment/HomeFragment.kt index ba55d02..c7e1251 100644 --- a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/fragment/HomeFragment.kt +++ b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/fragment/HomeFragment.kt @@ -6,6 +6,7 @@ import android.text.SpannableString import android.text.Spanned import android.util.Log import android.view.LayoutInflater +import android.view.MotionEvent import android.view.View import android.view.ViewGroup import android.view.animation.AccelerateInterpolator @@ -44,6 +45,7 @@ import kotlinx.android.synthetic.main.include_home_header.* import kotlinx.coroutines.launch import kotlin.random.Random import kotlin.random.nextLong +import kotlin.system.measureTimeMillis /** @@ -117,7 +119,11 @@ class HomeFragment : BaseFragment(), HomePresenter.HomeView { } launch { - setFirstRunShown(mainActivity.synchronizer.isFirstRun()) + Log.e("TWIG", "deciding whether to show first run") + val extraDelay = measureTimeMillis { + setFirstRunShown(mainActivity.synchronizer.isFirstRun() || mainActivity.synchronizer.isOutOfSync()) + } + Log.e("TWIG", "done deciding whether to show first run in $extraDelay ms. Was that worth it? Or should we toggle a boolean in the application class?") } header_active_transaction.visibility = View.GONE @@ -161,7 +167,11 @@ class HomeFragment : BaseFragment(), HomePresenter.HomeView { layoutManager = LinearLayoutManager(activity, RecyclerView.VERTICAL, false) adapter = TransactionAdapter().also { transactionAdapter = it } addItemDecoration(AlternatingRowColorDecoration()) + } +// recycler_transactions.setOnClickListener { +// mainActivity.navController.navigate(R.id.nav_history_fragment) +// } } @@ -198,7 +208,7 @@ class HomeFragment : BaseFragment(), HomePresenter.HomeView { // var hasEmptyViews = group_empty_view_items.visibility == View.VISIBLE // if(!viewsInitialized) toggleViews(true) // - val message = if(progress >= 100) "Download complete! Processing blocks..." else "Downloading blocks ($progress%)" + val message = if(progress >= 100) "Download complete! Processing blocks..." else "Downloading remaining blocks ($progress%)" // text_wallet_message.text = message if (snackbar == null && progress <= 50) { @@ -209,7 +219,7 @@ class HomeFragment : BaseFragment(), HomePresenter.HomeView { snackbar?.show() } else { snackbar?.setText(message) - if(progress == 100 && snackbar?.isShownOrQueued != true) snackbar?.show() + if(snackbar?.isShownOrQueued != true) snackbar?.show() } } diff --git a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/fragment/ReceiveFragment.kt b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/fragment/ReceiveFragment.kt index c5dbde0..466313c 100644 --- a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/fragment/ReceiveFragment.kt +++ b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/fragment/ReceiveFragment.kt @@ -67,6 +67,7 @@ class ReceiveFragment : BaseFragment() { } private fun onAddressLoaded(address: String) { + Log.e("TWIG", "onAddressLoaded: $address") qrecycler.load(address) .withQuietZoneSize(3) .withCorrectionLevel(QRecycler.CorrectionLevel.MEDIUM) diff --git a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/presenter/HistoryPresenter.kt b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/presenter/HistoryPresenter.kt new file mode 100644 index 0000000..99b31bd --- /dev/null +++ b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/presenter/HistoryPresenter.kt @@ -0,0 +1,57 @@ +package cash.z.android.wallet.ui.presenter + +import android.util.Log +import cash.z.android.wallet.ui.presenter.Presenter.PresenterView +import cash.z.wallet.sdk.dao.WalletTransaction +import cash.z.wallet.sdk.data.ActiveSendTransaction +import cash.z.wallet.sdk.data.ActiveTransaction +import cash.z.wallet.sdk.data.Synchronizer +import cash.z.wallet.sdk.data.TransactionState +import kotlinx.coroutines.* +import kotlinx.coroutines.Dispatchers.Main +import kotlinx.coroutines.channels.ReceiveChannel +import kotlin.coroutines.CoroutineContext + +class HistoryPresenter( + private val view: HistoryView, + private val synchronizer: Synchronizer +) : Presenter, CoroutineScope { + + private val job = Job() + override val coroutineContext: CoroutineContext get() = Dispatchers.Main + job + + interface HistoryView : PresenterView { + fun setTransactions(transactions: List) + } + + override suspend fun start() { + Log.e("@TWIG", "historyPresenter starting!") + launchTransactionBinder(synchronizer.repository.allTransactions()) + } + + override fun stop() { + Log.e("@TWIG", "historyPresenter stopping!") + job.cancel() + } + + private fun CoroutineScope.launchTransactionBinder(channel: ReceiveChannel>) = launch { + Log.e("@TWIG", "transaction binder starting!") + for (walletTransactionList in channel) { + Log.e("@TWIG", "received ${walletTransactionList.size} transactions for presenting") + bind(walletTransactionList) + } + Log.e("@TWIG", "transaction binder exiting!") + } + + + // + // View Callbacks on Main Thread + // + + private fun bind(transactions: List) { + Log.e("@TWIG", "binding ${transactions.size} walletTransactions") + view.setTransactions(transactions) + } + +} + diff --git a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/presenter/HomePresenter.kt b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/presenter/HomePresenter.kt index 4f4e13b..eae45c0 100644 --- a/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/presenter/HomePresenter.kt +++ b/zcash-android-wallet-app/app/src/main/java/cash/z/android/wallet/ui/presenter/HomePresenter.kt @@ -82,22 +82,23 @@ class HomePresenter( // private fun bind(old: Long?, new: Long) = onMain { - Log.e("@TWIG-t", "binding balance of $new") + Log.e("@TWIG-b", "binding balance of $new") view.updateBalance(old ?: 0L, new) } private fun bind(transactions: List) = onMain { - Log.e("@TWIG-t", "binding ${transactions.size} walletTransactions") + Log.e("@TWIG-b", "binding ${transactions.size} walletTransactions") view.setTransactions(transactions) } private fun bind(progress: Int) = onMain { + Log.e("@TWIG-b", "binding progress of $progress") view.showProgress(progress) } private fun bind(activeTransactionMap: Map) = onMain { - Log.e("@TWIG-v", "binding a.t. map of size ${activeTransactionMap.size}") + Log.e("@TWIG-b", "binding a.t. map of size ${activeTransactionMap.size}") if (activeTransactionMap.isNotEmpty()) view.setActiveTransactions(activeTransactionMap) } diff --git a/zcash-android-wallet-app/app/src/main/res/layout/fragment_history.xml b/zcash-android-wallet-app/app/src/main/res/layout/fragment_history.xml new file mode 100644 index 0000000..7dab5c2 --- /dev/null +++ b/zcash-android-wallet-app/app/src/main/res/layout/fragment_history.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/zcash-android-wallet-app/app/src/main/res/layout/item_transaction_history.xml b/zcash-android-wallet-app/app/src/main/res/layout/item_transaction_history.xml new file mode 100644 index 0000000..39f7623 --- /dev/null +++ b/zcash-android-wallet-app/app/src/main/res/layout/item_transaction_history.xml @@ -0,0 +1,51 @@ + + + + + + + + + + \ No newline at end of file