checkpoint: stable non-mock release
This commit is contained in:
parent
69e159ff2b
commit
e2c454907d
|
@ -14,8 +14,8 @@ android {
|
||||||
applicationId "cash.z.android.wallet"
|
applicationId "cash.z.android.wallet"
|
||||||
minSdkVersion buildConfig.minSdkVersion
|
minSdkVersion buildConfig.minSdkVersion
|
||||||
targetSdkVersion buildConfig.targetSdkVersion
|
targetSdkVersion buildConfig.targetSdkVersion
|
||||||
versionCode 17 // 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.
|
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.4.9-alpha"
|
versionName "0.5.0-alpha"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
multiDexEnabled true
|
multiDexEnabled true
|
||||||
|
|
Binary file not shown.
|
@ -41,8 +41,8 @@ internal object SynchronizerModule {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideRepository(application: ZcashWalletApplication, converter: JniConverter, twigger: Twig): TransactionRepository {
|
fun provideRepository(application: ZcashWalletApplication, converter: JniConverter): TransactionRepository {
|
||||||
return PollingTransactionRepository(application, SampleProperties.wallet.dataDbName, 10_000L, converter, twigger)
|
return PollingTransactionRepository(application, SampleProperties.wallet.dataDbName, 10_000L)
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
|
@ -52,7 +52,7 @@ internal object SynchronizerModule {
|
||||||
return Wallet(
|
return Wallet(
|
||||||
context = application,
|
context = application,
|
||||||
converter = converter,
|
converter = converter,
|
||||||
dbDataPath = application.getDatabasePath(SampleProperties.wallet.dataDbName).absolutePath,
|
dataDbPath = application.getDatabasePath(SampleProperties.wallet.dataDbName).absolutePath,
|
||||||
paramDestinationDir = "${application.cacheDir.absolutePath}/params",
|
paramDestinationDir = "${application.cacheDir.absolutePath}/params",
|
||||||
seedProvider = SampleProperties.wallet.seedProvider,
|
seedProvider = SampleProperties.wallet.seedProvider,
|
||||||
spendingKeyProvider = SampleProperties.wallet.spendingKeyProvider
|
spendingKeyProvider = SampleProperties.wallet.spendingKeyProvider
|
||||||
|
|
|
@ -13,15 +13,15 @@ internal fun Context.alert(
|
||||||
@StringRes messageResId: Int,
|
@StringRes messageResId: Int,
|
||||||
@StringRes positiveButtonResId: Int = android.R.string.ok,
|
@StringRes positiveButtonResId: Int = android.R.string.ok,
|
||||||
@StringRes negativeButtonResId: Int = android.R.string.cancel,
|
@StringRes negativeButtonResId: Int = android.R.string.cancel,
|
||||||
positiveAction: () -> Unit = NO_ACTION,
|
negativeAction: () -> Unit = NO_ACTION,
|
||||||
negativeAction: () -> Unit = NO_ACTION
|
positiveAction: () -> Unit = NO_ACTION
|
||||||
) {
|
) {
|
||||||
alert(
|
alert(
|
||||||
message = getString(messageResId),
|
message = getString(messageResId),
|
||||||
positiveButtonResId = positiveButtonResId,
|
positiveButtonResId = positiveButtonResId,
|
||||||
negativeButtonResId = negativeButtonResId,
|
negativeButtonResId = negativeButtonResId,
|
||||||
positiveAction = positiveAction,
|
negativeAction = negativeAction,
|
||||||
negativeAction = negativeAction
|
positiveAction = positiveAction
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,8 +33,8 @@ internal fun Context.alert(
|
||||||
message: String,
|
message: String,
|
||||||
@StringRes positiveButtonResId: Int = android.R.string.ok,
|
@StringRes positiveButtonResId: Int = android.R.string.ok,
|
||||||
@StringRes negativeButtonResId: Int = android.R.string.cancel,
|
@StringRes negativeButtonResId: Int = android.R.string.cancel,
|
||||||
positiveAction: (() -> Unit) = NO_ACTION,
|
negativeAction: (() -> Unit) = NO_ACTION,
|
||||||
negativeAction: (() -> Unit) = NO_ACTION
|
positiveAction: (() -> Unit) = NO_ACTION
|
||||||
) {
|
) {
|
||||||
val builder = AlertDialog.Builder(this)
|
val builder = AlertDialog.Builder(this)
|
||||||
.setMessage(message)
|
.setMessage(message)
|
||||||
|
|
|
@ -8,8 +8,8 @@ object AliceWallet {
|
||||||
const val name = "test.reference.alice"
|
const val name = "test.reference.alice"
|
||||||
val seedProvider = SampleSeedProvider(name)
|
val seedProvider = SampleSeedProvider(name)
|
||||||
val spendingKeyProvider = SampleSpendingKeySharedPref(name)
|
val spendingKeyProvider = SampleSpendingKeySharedPref(name)
|
||||||
const val cacheDbName = "testalice_cache_emulator4.db"
|
const val cacheDbName = "testalice_cache_emulator7.db"
|
||||||
const val dataDbName = "testalice_data_emulator3.db"
|
const val dataDbName = "testalice_data_emulator7.db"
|
||||||
const val defaultSendAddress = "ztestsapling1wcp9fu5d3q945nwwyqxtf0dtn6pv22hmjxa39z0034ap734mvxkqz8kug4r2u2df2keekcne322" // bob's address
|
const val defaultSendAddress = "ztestsapling1wcp9fu5d3q945nwwyqxtf0dtn6pv22hmjxa39z0034ap734mvxkqz8kug4r2u2df2keekcne322" // bob's address
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,11 +18,31 @@ object BobWallet {
|
||||||
val seedProvider =
|
val seedProvider =
|
||||||
SampleSeedProvider(name)
|
SampleSeedProvider(name)
|
||||||
val spendingKeyProvider = SampleSpendingKeySharedPref(name)
|
val spendingKeyProvider = SampleSpendingKeySharedPref(name)
|
||||||
const val cacheDbName = "testbob_cache_pixel.db"
|
const val cacheDbName = "testbob_cache_pixel1.db"
|
||||||
const val dataDbName = "testbob_data_pixel.db"
|
const val dataDbName = "testbob_data_pixel1.db"
|
||||||
const val defaultSendAddress = "ztestsapling1yv696xtjn3jykdej2pqx0999eydvvyfphnw97ddk2h5luyedpqzud3r87aq0d7qna3jzjqqdcvw" // alice's address
|
const val defaultSendAddress = "ztestsapling1yv696xtjn3jykdej2pqx0999eydvvyfphnw97ddk2h5luyedpqzud3r87aq0d7qna3jzjqqdcvw" // alice's address
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 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 MyWallet {
|
object MyWallet {
|
||||||
const val name = "mine"
|
const val name = "mine"
|
||||||
val seedProvider =
|
val seedProvider =
|
||||||
|
@ -43,9 +63,9 @@ enum class Servers(val host: String) {
|
||||||
|
|
||||||
// TODO: load most of these properties in later, perhaps from settings
|
// TODO: load most of these properties in later, perhaps from settings
|
||||||
object SampleProperties {
|
object SampleProperties {
|
||||||
val COMPACT_BLOCK_SERVER = Servers.WLAN.host
|
val COMPACT_BLOCK_SERVER = Servers.ZCASH_TESTNET.host
|
||||||
const val COMPACT_BLOCK_PORT = 9067
|
const val COMPACT_BLOCK_PORT = 9067
|
||||||
val wallet = AliceWallet
|
val wallet = DaveWallet
|
||||||
// TODO: placeholder until we have a network service for this
|
// TODO: placeholder until we have a network service for this
|
||||||
val USD_PER_ZEC = BigDecimal("49.07", MathContext.DECIMAL128)
|
val USD_PER_ZEC = BigDecimal("49.07", MathContext.DECIMAL128)
|
||||||
}
|
}
|
|
@ -44,14 +44,16 @@ class TransactionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
|
||||||
private val formatter = SimpleDateFormat("M/d h:mma", Locale.getDefault())
|
private val formatter = SimpleDateFormat("M/d h:mma", Locale.getDefault())
|
||||||
|
|
||||||
fun bind(tx: WalletTransaction) {
|
fun bind(tx: WalletTransaction) {
|
||||||
|
val isHistory = icon != null
|
||||||
val sign = if (tx.isSend) "-" else "+"
|
val sign = if (tx.isSend) "-" else "+"
|
||||||
val amountColor = if (tx.isSend) R.color.text_dark_dimmed else R.color.colorPrimary
|
val amountColor = if (tx.isSend) R.color.text_dark_dimmed else R.color.colorPrimary
|
||||||
val transactionColor = if (tx.isSend) R.color.send_associated else R.color.receive_associated
|
val transactionColor = if (tx.isSend) R.color.send_associated else R.color.receive_associated
|
||||||
val transactionIcon = if (tx.isSend) R.drawable.ic_sent_transaction else R.drawable.ic_received_transaction
|
val transactionIcon = if (tx.isSend) R.drawable.ic_sent_transaction else R.drawable.ic_received_transaction
|
||||||
val zecAbsoluteValue = tx.value.absoluteValue.convertZatoshiToZec(3)
|
val zecAbsoluteValue = tx.value.absoluteValue.convertZatoshiToZec(6)
|
||||||
val toOrFrom = if (tx.isSend) "to" else "from"
|
val toOrFrom = if (tx.isSend) "to" else "from"
|
||||||
val srcOrDestination = tx.address?.truncate() ?: "shielded mystery person"
|
val srcOrDestination = tx.address?.truncate() ?: "shielded mystery person"
|
||||||
timestamp.text = if (!tx.isMined || tx.timeInSeconds == 0L) "Pending" else (tx.timeInSeconds * 1000L).toRelativeTimeString() //formatter.format(tx.timeInSeconds * 1000)
|
timestamp.text = if (!tx.isMined || tx.timeInSeconds == 0L) "Pending"
|
||||||
|
else (if (isHistory) formatter.format(tx.timeInSeconds * 1000) else (tx.timeInSeconds * 1000L).toRelativeTimeString())
|
||||||
amount.text = "$sign$zecAbsoluteValue"
|
amount.text = "$sign$zecAbsoluteValue"
|
||||||
amount.setTextColor(amountColor.toAppColor())
|
amount.setTextColor(amountColor.toAppColor())
|
||||||
|
|
||||||
|
|
|
@ -122,7 +122,7 @@ class SendFragment : BaseFragment(), SendPresenter.SendView, ScanFragment.Barcod
|
||||||
binding.groupDialogSend.visibility = View.VISIBLE
|
binding.groupDialogSend.visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateBalance(new: Long) {
|
override fun updateAvailableBalance(new: Long) {
|
||||||
// TODO: use a formatted string resource here
|
// TODO: use a formatted string resource here
|
||||||
val availableTextSpan = "${new.convertZatoshiToZecString(8)} $zec Available".toSpannable()
|
val availableTextSpan = "${new.convertZatoshiToZecString(8)} $zec Available".toSpannable()
|
||||||
availableTextSpan.setSpan(ForegroundColorSpan(R.color.colorPrimary.toAppColor()), availableTextSpan.length - "Available".length, availableTextSpan.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
availableTextSpan.setSpan(ForegroundColorSpan(R.color.colorPrimary.toAppColor()), availableTextSpan.length - "Available".length, availableTextSpan.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||||
|
|
|
@ -107,7 +107,8 @@ class WelcomeFragment : ProgressFragment(R.id.progress_welcome) {
|
||||||
mainActivity?.navController?.navigate(
|
mainActivity?.navController?.navigate(
|
||||||
destination,
|
destination,
|
||||||
null,
|
null,
|
||||||
NavOptions.Builder().setPopUpTo(R.id.mobile_navigation, true).build(),
|
null,
|
||||||
|
// NavOptions.Builder().setPopUpTo(R.id.mobile_navigation, true).build(),
|
||||||
extras
|
extras
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import cash.z.android.wallet.ui.fragment.HomeFragment
|
||||||
import cash.z.android.wallet.ui.presenter.Presenter.PresenterView
|
import cash.z.android.wallet.ui.presenter.Presenter.PresenterView
|
||||||
import cash.z.wallet.sdk.dao.WalletTransaction
|
import cash.z.wallet.sdk.dao.WalletTransaction
|
||||||
import cash.z.wallet.sdk.data.*
|
import cash.z.wallet.sdk.data.*
|
||||||
|
import cash.z.wallet.sdk.secure.Wallet
|
||||||
import dagger.Binds
|
import dagger.Binds
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
@ -46,12 +47,12 @@ class HomePresenter @Inject constructor(
|
||||||
job?.cancel()?.also { job = null }
|
job?.cancel()?.also { job = null }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CoroutineScope.launchBalanceBinder(channel: ReceiveChannel<Long>) = launch {
|
private fun CoroutineScope.launchBalanceBinder(channel: ReceiveChannel<Wallet.WalletBalance>) = launch {
|
||||||
var old: Long? = null
|
var old: Long? = null
|
||||||
twig("balance binder starting!")
|
twig("balance binder starting!")
|
||||||
for (new in channel) {
|
for (new in channel) {
|
||||||
twig("polled a balance item")
|
twig("polled a balance item")
|
||||||
bind(old, new).also { old = new }
|
bind(old, new.total).also { old = new.total }
|
||||||
}
|
}
|
||||||
twig("balance binder exiting!")
|
twig("balance binder exiting!")
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,9 +7,10 @@ import cash.z.android.wallet.sample.SampleProperties
|
||||||
import cash.z.android.wallet.ui.fragment.SendFragment
|
import cash.z.android.wallet.ui.fragment.SendFragment
|
||||||
import cash.z.android.wallet.ui.presenter.Presenter.PresenterView
|
import cash.z.android.wallet.ui.presenter.Presenter.PresenterView
|
||||||
import cash.z.wallet.sdk.data.Synchronizer
|
import cash.z.wallet.sdk.data.Synchronizer
|
||||||
import cash.z.wallet.sdk.data.twig
|
|
||||||
import cash.z.wallet.sdk.data.Twig
|
import cash.z.wallet.sdk.data.Twig
|
||||||
|
import cash.z.wallet.sdk.data.twig
|
||||||
import cash.z.wallet.sdk.ext.*
|
import cash.z.wallet.sdk.ext.*
|
||||||
|
import cash.z.wallet.sdk.secure.Wallet
|
||||||
import dagger.Binds
|
import dagger.Binds
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
@ -26,7 +27,7 @@ class SendPresenter @Inject constructor(
|
||||||
) : Presenter {
|
) : Presenter {
|
||||||
|
|
||||||
interface SendView : PresenterView {
|
interface SendView : PresenterView {
|
||||||
fun updateBalance(new: Long)
|
fun updateAvailableBalance(new: Long)
|
||||||
fun setHeaders(isUsdSelected: Boolean, headerString: String, subheaderString: String)
|
fun setHeaders(isUsdSelected: Boolean, headerString: String, subheaderString: String)
|
||||||
fun setHeaderValue(usdString: String)
|
fun setHeaderValue(usdString: String)
|
||||||
fun setSubheaderValue(usdString: String, isUsdSelected: Boolean)
|
fun setSubheaderValue(usdString: String, isUsdSelected: Boolean)
|
||||||
|
@ -75,7 +76,7 @@ class SendPresenter @Inject constructor(
|
||||||
balanceJob?.cancel()?.also { balanceJob = null }
|
balanceJob?.cancel()?.also { balanceJob = null }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun CoroutineScope.launchBalanceBinder(channel: ReceiveChannel<Long>) = launch {
|
fun CoroutineScope.launchBalanceBinder(channel: ReceiveChannel<Wallet.WalletBalance>) = launch {
|
||||||
twig("send balance binder starting!")
|
twig("send balance binder starting!")
|
||||||
for (new in channel) {
|
for (new in channel) {
|
||||||
twig("send polled a balance item")
|
twig("send polled a balance item")
|
||||||
|
@ -161,6 +162,8 @@ class SendPresenter @Inject constructor(
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the user has completed their update to the header value, typically on focus change.
|
* Called when the user has completed their update to the header value, typically on focus change.
|
||||||
|
*
|
||||||
|
* @return true when the given amount is parsable, positive and less than the available amount.
|
||||||
*/
|
*/
|
||||||
fun inputHeaderUpdated(amountString: String): Boolean {
|
fun inputHeaderUpdated(amountString: String): Boolean {
|
||||||
if (!validateAmount(amountString)) return false
|
if (!validateAmount(amountString)) return false
|
||||||
|
@ -188,18 +191,33 @@ class SendPresenter @Inject constructor(
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the user has updated the toAddress, typically on focus change.
|
||||||
|
*
|
||||||
|
* @return true when the given address' length and content are valid
|
||||||
|
*/
|
||||||
fun inputAddressUpdated(newAddress: String): Boolean {
|
fun inputAddressUpdated(newAddress: String): Boolean {
|
||||||
if (!validateAddress(newAddress)) return false
|
if (!validateAddress(newAddress)) return false
|
||||||
updateModel(sendUiModel.copy(toAddress = newAddress))
|
updateModel(sendUiModel.copy(toAddress = newAddress))
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the user has updated the memo field, typically after pressing the 'done' key.
|
||||||
|
*
|
||||||
|
* @return true when the given memo's content does not contain invalid characters
|
||||||
|
*/
|
||||||
fun inputMemoUpdated(newMemo: String): Boolean {
|
fun inputMemoUpdated(newMemo: String): Boolean {
|
||||||
if (!validateMemo(newMemo)) return false
|
if (!validateMemo(newMemo)) return false
|
||||||
updateModel(sendUiModel.copy(memo = newMemo))
|
updateModel(sendUiModel.copy(memo = newMemo))
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called after the user has pressed the send button and should be shown a confirmation dialog, next.
|
||||||
|
*
|
||||||
|
* @return true when all input fields contained valid data
|
||||||
|
*/
|
||||||
fun inputSendPressed(): Boolean {
|
fun inputSendPressed(): Boolean {
|
||||||
// double sanity check. Make sure view and model agree and are each valid and if not, highlight the error.
|
// double sanity check. Make sure view and model agree and are each valid and if not, highlight the error.
|
||||||
if (!view.checkAllInput() || !validateAll()) return false
|
if (!view.checkAllInput() || !validateAll()) return false
|
||||||
|
@ -215,12 +233,12 @@ class SendPresenter @Inject constructor(
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bind(newZatoshiBalance: Long) {
|
fun bind(balanceInfo: Wallet.WalletBalance) {
|
||||||
val available = newZatoshiBalance// - minersFee
|
val available = balanceInfo.available
|
||||||
if (available >= 0) {
|
if (available >= 0) {
|
||||||
twig("binding balance of $available")
|
twig("binding balance of $available")
|
||||||
view.updateBalance(available)
|
view.updateAvailableBalance(available)
|
||||||
// updateModel(sendUiModel.copy(availableBalance = available))
|
updateModel(sendUiModel.copy(availableBalance = available))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,12 +335,12 @@ class SendPresenter @Inject constructor(
|
||||||
view.setAmountError("Please specify a larger amount")
|
view.setAmountError("Please specify a larger amount")
|
||||||
requiresValidation = true
|
requiresValidation = true
|
||||||
false
|
false
|
||||||
// } else if (sendUiModel.availableBalance != null
|
} else if (sendUiModel.availableBalance != null
|
||||||
// && zatoshiValue >= sendUiModel.availableBalance!!) {
|
&& zatoshiValue >= sendUiModel.availableBalance!!) {
|
||||||
// view.setAmountError("Exceeds available balance of " +
|
view.setAmountError("Exceeds available balance of " +
|
||||||
// "${sendUiModel.availableBalance.convertZatoshiToZecString(3)}")
|
"${sendUiModel.availableBalance.convertZatoshiToZecString(3)}")
|
||||||
// requiresValidation = true
|
requiresValidation = true
|
||||||
// false
|
false
|
||||||
} else {
|
} else {
|
||||||
view.setAmountError(null)
|
view.setAmountError(null)
|
||||||
true
|
true
|
||||||
|
|
Loading…
Reference in New Issue