Everything functional before changing startup sequence

This commit is contained in:
Kevin Gorham 2019-02-12 19:09:06 -05:00 committed by Kevin Gorham
parent ba51a19b0b
commit ad708e69d5
11 changed files with 309 additions and 78 deletions

View File

@ -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<Any?, ByteArray> {
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<Any?, String> {
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"
}
}

View File

@ -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
}

View File

@ -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<Any?, ByteArray> {
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<Any?, String> {
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."
}

View File

@ -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<WalletTransaction>) {
}
(binding.recyclerTransactionsHistory.adapter as TransactionAdapter).submitList(transactions)
}
}
@Module

View File

@ -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()
}
}

View File

@ -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)

View File

@ -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<WalletTransaction>)
}
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<List<WalletTransaction>>) = 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<WalletTransaction>) {
Log.e("@TWIG", "binding ${transactions.size} walletTransactions")
view.setTransactions(transactions)
}
}

View File

@ -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<WalletTransaction>) = 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<ActiveTransaction, TransactionState>) = 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)
}

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Transactions -->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_transactions_history"
android:layout_width="0dp"
android:layout_height="0dp"
android:clipToPadding="false"
android:paddingBottom="72dp"
android:layout_margin="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/appbar"
tools:itemCount="15"
tools:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/item_transaction_history"
tools:orientation="vertical" />
<include
layout="@layout/include_app_bar"
tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container_transaction"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/home_transaction_item_background"
android:paddingEnd="8dp"
android:paddingRight="8dp"
android:elevation="1dp"
tools:ignore="RtlSymmetry">
<View
android:id="@+id/view_transaction_status"
android:layout_width="6dp"
android:layout_height="0dp"
android:layout_marginTop="1dp"
android:layout_marginBottom="1dp"
android:background="@color/colorPrimary"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/text_transaction_timestamp"
app:layout_constraintBottom_toBottomOf="@id/text_transaction_timestamp"/>
<TextView
android:id="@+id/text_transaction_timestamp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:paddingBottom="8dp"
android:paddingTop="8dp"
tools:text="8/23 3:24pm"
android:textSize="@dimen/text_size_body_2"
android:textColor="@color/text_dark_dimmed"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/view_transaction_status"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/text_transaction_amount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="+ 4.244"
android:textColor="@color/colorPrimary"
android:textSize="@dimen/text_size_body_2"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>