General fixes and cleanup.
- Allow tiny transaction amounts and improve display - show toAddress and memo when we know it - Bugfix: self transactions are not duplicated - Turned Developer logs back on and cleaned up output a bit
This commit is contained in:
parent
899e48b9f3
commit
a357afe09a
|
@ -11,7 +11,7 @@ apply plugin: 'com.google.firebase.firebase-perf'
|
|||
|
||||
archivesBaseName = 'zcash-android-wallet'
|
||||
group = 'cash.z.ecc.android'
|
||||
version = '1.0.0-alpha11'
|
||||
version = '1.0.0-dev12'
|
||||
|
||||
android {
|
||||
compileSdkVersion Deps.compileSdkVersion
|
||||
|
@ -21,7 +21,7 @@ android {
|
|||
applicationId 'cash.z.ecc.android'
|
||||
minSdkVersion Deps.minSdkVersion
|
||||
targetSdkVersion Deps.targetSdkVersion
|
||||
versionCode = 1_00_00_011
|
||||
versionCode = 1_00_00_112
|
||||
// last digits are alpha(0XX) beta(2XX) rc(4XX) release(8XX). Ex: 1_08_04_401 is an release candidate build of version 1.8.4 and 1_08_04_800 would be the final release.
|
||||
versionName = "$version"
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
|
@ -43,6 +43,17 @@ android {
|
|||
matchingFallbacks = ['zcashmainnet', 'release']
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: delete this test code
|
||||
variantFilter { variant ->
|
||||
def names = variant.flavors*.name
|
||||
// To check for a certain build type, use variant.buildType.name == "<buildType>"
|
||||
if (names.contains("zcashtestnet") || names.contains("Zcashtestnet") || variant.buildType.name == "release") {
|
||||
// Gradle ignores any variants that satisfy the conditions above.
|
||||
setIgnore(true)
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled true
|
||||
|
|
|
@ -14,6 +14,8 @@ class TransactionAdapter<T : ConfirmedTransaction> :
|
|||
oldItem: T,
|
||||
newItem: T
|
||||
) = oldItem.minedHeight == newItem.minedHeight && oldItem.noteId == newItem.noteId
|
||||
// bugfix: distinguish between self-transactions so they don't overwrite each other in the UI // TODO confirm that this is working, as intended
|
||||
&& ((oldItem.raw == null && newItem.raw == null) || (oldItem.raw != null && newItem.raw != null && oldItem.raw!!.contentEquals(newItem.raw!!)))
|
||||
|
||||
override fun areContentsTheSame(
|
||||
oldItem: T,
|
||||
|
|
|
@ -8,6 +8,7 @@ import cash.z.ecc.android.ext.goneIf
|
|||
import cash.z.ecc.android.ext.toAppColor
|
||||
import cash.z.ecc.android.ui.MainActivity
|
||||
import cash.z.wallet.sdk.entity.ConfirmedTransaction
|
||||
import cash.z.wallet.sdk.ext.ZcashSdk
|
||||
import cash.z.wallet.sdk.ext.convertZatoshiToZecString
|
||||
import cash.z.wallet.sdk.ext.isShielded
|
||||
import cash.z.wallet.sdk.ext.toAbbreviatedAddress
|
||||
|
@ -60,8 +61,13 @@ class TransactionViewHolder<T : ConfirmedTransaction>(itemView: View) : Recycler
|
|||
lineTwo = "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
// sanitize amount
|
||||
if (value < ZcashSdk.MINERS_FEE_ZATOSHI) amount = "< 0.001"
|
||||
else if (amount.length > 8) amount = "tap to view"
|
||||
}
|
||||
|
||||
|
||||
topText.text = lineOne
|
||||
bottomText.text = lineTwo
|
||||
amountText.text = amount
|
||||
|
@ -74,7 +80,10 @@ class TransactionViewHolder<T : ConfirmedTransaction>(itemView: View) : Recycler
|
|||
private fun onTransactionClicked(transaction: ConfirmedTransaction) {
|
||||
val txId = transaction.rawTransactionId.toTxId()
|
||||
val detailsMessage: String = "Zatoshi amount: ${transaction.value}\n\n" +
|
||||
"Transaction: $txId"
|
||||
"Transaction: $txId" +
|
||||
"${if (transaction.toAddress != null) "\nto: ${transaction.toAddress}" else ""}" +
|
||||
"${if (transaction.memo != null) "\nmemo: ${transaction.toAddress}" else ""}"
|
||||
|
||||
MaterialAlertDialogBuilder(itemView.context)
|
||||
.setMessage(detailsMessage)
|
||||
.setTitle("Transaction Details")
|
||||
|
|
|
@ -137,9 +137,7 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
|
|||
super.onResume()
|
||||
twig("HomeFragment.onResume resumeScope.isActive: ${resumedScope.isActive} $resumedScope")
|
||||
viewModel.initializeMaybe()
|
||||
twig("onResume (A)")
|
||||
onClearAmount()
|
||||
twig("onResume (B)")
|
||||
viewModel.uiModels.scanReduce { old, new ->
|
||||
onModelUpdated(old, new)
|
||||
new
|
||||
|
@ -149,18 +147,14 @@ twig("onResume (B)")
|
|||
twig("exception while processing uiModels $e")
|
||||
throw e
|
||||
}.launchIn(resumedScope)
|
||||
twig("onResume (C)")
|
||||
|
||||
// TODO: see if there is a better way to trigger a refresh of the uiModel on resume
|
||||
// the latest one should just be in the viewmodel and we should just "resubscribe"
|
||||
// but for some reason, this doesn't always happen, which kind of defeats the purpose
|
||||
// of having a cold stream in the view model
|
||||
resumedScope.launch {
|
||||
twig("onResume (pre-fresh)")
|
||||
viewModel.refreshBalance()
|
||||
twig("onResume (post-fresh)")
|
||||
}
|
||||
twig("onResume (D)")
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
|
@ -280,22 +274,14 @@ twig("onResume (D)")
|
|||
twig("onModelUpdated: $new")
|
||||
if (binding.lottieButtonLoading.visibility != View.VISIBLE) binding.lottieButtonLoading.visibility = View.VISIBLE
|
||||
uiModel = new
|
||||
twig("onModelUpdated (A)")
|
||||
if (old?.pendingSend != new.pendingSend) {
|
||||
twig("onModelUpdated (B)")
|
||||
setSendAmount(new.pendingSend)
|
||||
twig("onModelUpdated (C)")
|
||||
}
|
||||
twig("onModelUpdated (D)")
|
||||
// TODO: handle stopped and disconnected flows
|
||||
setProgress(uiModel) // TODO: we may not need to separate anymore
|
||||
twig("onModelUpdated (E)")
|
||||
// if (new.status = SYNCING) onSyncing(new) else onSynced(new)
|
||||
if (new.status == SYNCED) onSynced(new) else onSyncing(new)
|
||||
twig("onModelUpdated (F)")
|
||||
setSendEnabled(new.isSendEnabled)
|
||||
twig("onModelUpdated (G) sendEnabled? ${new.isSendEnabled}")
|
||||
twig("DONE onModelUpdated")
|
||||
}
|
||||
|
||||
private fun onSyncing(uiModel: HomeViewModel.UiModel) {
|
||||
|
@ -359,7 +345,7 @@ twig("onModelUpdated (G) sendEnabled? ${new.isSendEnabled}")
|
|||
//
|
||||
|
||||
enum class BannerAction(val action: String) {
|
||||
FUND_NOW("Fund Now"),
|
||||
FUND_NOW(""),
|
||||
CANCEL("Cancel"),
|
||||
NONE(""),
|
||||
CLEAR("clear");
|
||||
|
|
|
@ -96,13 +96,10 @@ class HomeViewModel @Inject constructor() : ViewModel() {
|
|||
if (lastDownloadRange.isEmpty()) {
|
||||
100
|
||||
} else {
|
||||
twig("NUMERATOR: $lastDownloadedHeight - ${lastDownloadRange.first} + 1 = ${lastDownloadedHeight - lastDownloadRange.first + 1} block(s) downloaded")
|
||||
twig("DENOMINATOR: ${lastDownloadRange.last} - ${lastDownloadRange.first} + 1 = ${lastDownloadRange.last - lastDownloadRange.first + 1} block(s) to download")
|
||||
val progress =
|
||||
(((lastDownloadedHeight - lastDownloadRange.first + 1).coerceAtLeast(0).toFloat() / (lastDownloadRange.last - lastDownloadRange.first + 1)) * 100.0f).coerceAtMost(
|
||||
100.0f
|
||||
).roundToInt()
|
||||
twig("RESULT: $progress")
|
||||
progress
|
||||
}
|
||||
}
|
||||
|
@ -112,10 +109,7 @@ class HomeViewModel @Inject constructor() : ViewModel() {
|
|||
if (lastScanRange.isEmpty()) {
|
||||
100
|
||||
} else {
|
||||
twig("NUMERATOR: ${lastScannedHeight - lastScanRange.first + 1} block(s) scanned")
|
||||
twig("DENOMINATOR: ${lastScanRange.last - lastScanRange.first + 1} block(s) to scan")
|
||||
val progress = (((lastScannedHeight - lastScanRange.first + 1).coerceAtLeast(0).toFloat() / (lastScanRange.last - lastScanRange.first + 1)) * 100.0f).coerceAtMost(100.0f).roundToInt()
|
||||
twig("RESULT: $progress")
|
||||
progress
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,9 +15,7 @@ class MagicSnakeLoader(
|
|||
|
||||
var isSynced: Boolean = false
|
||||
set(value) {
|
||||
twig("ZZZ isSynced=$value isStarted=$isStarted")
|
||||
if (value && !isStarted) {
|
||||
twig("ZZZ isSynced=$value TURBO sync")
|
||||
lottie.progress = 1.0f
|
||||
field = value
|
||||
return
|
||||
|
@ -25,19 +23,16 @@ class MagicSnakeLoader(
|
|||
|
||||
// it is started but it hadn't reached the synced state yet
|
||||
if (value && !field) {
|
||||
twig("ZZZ synced was $field but now is $value so playing to completion since we are now synced")
|
||||
field = value
|
||||
playToCompletion()
|
||||
} else {
|
||||
field = value
|
||||
twig("ZZZ isSynced=$value and lottie.progress=${lottie.progress}")
|
||||
}
|
||||
}
|
||||
|
||||
var scanProgress: Int = 0
|
||||
set(value) {
|
||||
field = value
|
||||
twig("ZZZ scanProgress=$value")
|
||||
if (value > 0) {
|
||||
startMaybe()
|
||||
onScanUpdated()
|
||||
|
@ -47,7 +42,6 @@ class MagicSnakeLoader(
|
|||
var downloadProgress: Int = 0
|
||||
set(value) {
|
||||
field = value
|
||||
twig("ZZZ downloadProgress=$value")
|
||||
if (value > 0) startMaybe()
|
||||
}
|
||||
|
||||
|
@ -56,25 +50,12 @@ class MagicSnakeLoader(
|
|||
if (!isSynced && !isStarted) lottie.postDelayed({
|
||||
// after some delay, if we're still not synced then we better start animating (unless we already are)!
|
||||
if (!isSynced && isPaused) {
|
||||
twig("ZZZ yes start!")
|
||||
lottie.resumeAnimation()
|
||||
isPaused = false
|
||||
isStarted = true
|
||||
} else {
|
||||
twig("ZZZ I would have started but we're already synced!")
|
||||
}
|
||||
}, 200L).also { twig("ZZZ startMaybe???") }
|
||||
}, 200L)
|
||||
}
|
||||
// set(value) {
|
||||
// field = value
|
||||
// if (value in 1..99 && isStopped) {
|
||||
// lottie.playAnimation()
|
||||
// isStopped = false
|
||||
// } else if (value >= 100) {
|
||||
// isStopped = true
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
private val isDownloading get() = downloadProgress in 1..99
|
||||
private val isScanning get() = scanProgress in 1..99
|
||||
|
@ -83,25 +64,11 @@ class MagicSnakeLoader(
|
|||
lottie.addAnimatorUpdateListener(this)
|
||||
}
|
||||
|
||||
// downloading = true
|
||||
// lottieAnimationView.playAnimation()
|
||||
// lottieAnimationView.addAnimatorUpdateListener { valueAnimator ->
|
||||
// // Set animation progress
|
||||
// val progress = (valueAnimator.animatedValue as Float * 100).toInt()
|
||||
// progressTv.text = "Progress: $progress%"
|
||||
//
|
||||
// if (downloading && progress >= 40) {
|
||||
// lottieAnimationView.progress = 0f
|
||||
// }
|
||||
// }
|
||||
|
||||
override fun onAnimationUpdate(animation: ValueAnimator) {
|
||||
if (isSynced || isPaused) {
|
||||
// playToCompletion()
|
||||
return
|
||||
}
|
||||
twig("ZZZ")
|
||||
twig("ZZZ\t\tonAnimationUpdate(${animation.animatedValue})")
|
||||
|
||||
// if we are scanning, then set the animation progress, based on the scan progress
|
||||
// if we're not scanning, then we're looping
|
||||
|
@ -112,7 +79,6 @@ class MagicSnakeLoader(
|
|||
|
||||
private val acceptablePauseFrames = arrayOf(33,34,67,68,99)
|
||||
private fun applyScanProgress(frame: Int) {
|
||||
twig("ZZZ applyScanProgress($frame) : isPaused=$isPaused isStarted=$isStarted min=${lottie.minFrame} max=${lottie.maxFrame}")
|
||||
// don't hardcode the progress until the loop animation has completed, cleanly
|
||||
if (isPaused) {
|
||||
onScanUpdated()
|
||||
|
@ -126,7 +92,6 @@ class MagicSnakeLoader(
|
|||
}
|
||||
|
||||
private fun onScanUpdated() {
|
||||
twig("ZZZ onScanUpdated : isPaused=$isPaused")
|
||||
if (isSynced) {
|
||||
// playToCompletion()
|
||||
return
|
||||
|
@ -137,7 +102,6 @@ class MagicSnakeLoader(
|
|||
val scanRange = scanningEndFrame - scanningStartFrame
|
||||
val scanRangeProgress = scanProgress.toFloat() / 100.0f * scanRange.toFloat()
|
||||
lottie.progress = (scanningStartFrame.toFloat() + scanRangeProgress) / totalFrames
|
||||
twig("ZZZ onScanUpdated : scanRange=$scanRange scanRangeProgress=$scanRangeProgress lottie.progress=${(scanningStartFrame.toFloat() + scanRangeProgress)}/$totalFrames=${lottie.progress}")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -160,17 +124,14 @@ class MagicSnakeLoader(
|
|||
}
|
||||
|
||||
private fun allowLoop(frame: Int) {
|
||||
twig("ZZZ allowLoop($frame) : isPaused=$isPaused")
|
||||
unpause()
|
||||
if (frame >= scanningStartFrame) {
|
||||
twig("ZZZ resetting to 0f (LOOPING)")
|
||||
lottie.progress = 0f
|
||||
}
|
||||
}
|
||||
|
||||
fun unpause() {
|
||||
if (isPaused) {
|
||||
twig("ZZZ unpausing")
|
||||
lottie.resumeAnimation()
|
||||
isPaused = false
|
||||
}
|
||||
|
@ -178,7 +139,6 @@ class MagicSnakeLoader(
|
|||
|
||||
fun pause() {
|
||||
if (!isPaused) {
|
||||
twig("ZZZ pausing")
|
||||
lottie.pauseAnimation()
|
||||
isPaused = true
|
||||
}
|
||||
|
|
|
@ -81,8 +81,8 @@ class SendViewModel @Inject constructor() : ViewModel() {
|
|||
synchronizer.validateAddress(toAddress).isNotValid -> {
|
||||
emit("Please enter a valid address")
|
||||
}
|
||||
zatoshiAmount < ZcashSdk.MINERS_FEE_ZATOSHI -> {
|
||||
emit("Too little! Please enter at least 0.0001")
|
||||
zatoshiAmount <= 1 -> {
|
||||
emit("Too little! Please enter at least 1 Zatoshi.")
|
||||
}
|
||||
maxZatoshi != null && zatoshiAmount > maxZatoshi -> {
|
||||
emit( "Too much! Please enter no more than ${maxZatoshi.convertZatoshiToZecString(8)}")
|
||||
|
|
|
@ -62,7 +62,7 @@ class WalletSetupViewModel @Inject constructor() : ViewModel() {
|
|||
}
|
||||
|
||||
suspend fun importWallet(seedPhrase: String, birthdayHeight: Int): Initializer {
|
||||
twig("Importing wallet")
|
||||
twig("Importing wallet. Requested birthday: $birthdayHeight")
|
||||
return ZcashWalletApp.component.initializerSubcomponent().create(Initializer.DefaultBirthdayStore(ZcashWalletApp.instance, birthdayHeight)).run {
|
||||
initializer().apply {
|
||||
import(importWallet(seedPhrase.toCharArray()), birthdayStore().getBirthday())
|
||||
|
|
|
@ -2,6 +2,6 @@
|
|||
<shape
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<corners android:radius="10dp" />
|
||||
<stroke android:width="1dp" android:color="#282828"/>
|
||||
<stroke android:width="1dp" android:color="@color/background_banner_stroke"/>
|
||||
<solid android:color="@color/background_banner"/>
|
||||
</shape>
|
|
@ -58,6 +58,7 @@
|
|||
but have a more useful name for use in code -->
|
||||
|
||||
<color name="background_banner">@color/zcashBlack_dark</color>
|
||||
<color name="background_banner_stroke">#282828</color>
|
||||
<color name="scan_overlay_background">@color/zcashBlack_87</color>
|
||||
<color name="spacer">#1FBB666A</color>
|
||||
<color name="text_send_amount_disabled">@color/text_light</color>
|
||||
|
|
|
@ -1,23 +1,18 @@
|
|||
# Project-wide Gradle settings.
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
# For more details on how to configure your build environment visit
|
||||
## For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
#
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs=-Xmx1536m
|
||||
# Default value: -Xmx1024m -XX:MaxPermSize=256m
|
||||
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||
#
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||
# Android operating system, and which are packaged with your app's APK
|
||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||
android.useAndroidX=true
|
||||
# Automatically convert third-party libraries to use AndroidX
|
||||
android.enableJetifier=true
|
||||
# Kotlin code style for this project: "official" or "obsolete":
|
||||
#Wed Jan 29 09:45:08 EST 2020
|
||||
kotlin.code.style=official
|
||||
|
||||
dagger.fastInit=enabled
|
||||
android.enableJetifier=true
|
||||
org.gradle.jvmargs=-Xmx2048M -Dkotlin.daemon.jvm.options\="-Xmx2048M"
|
||||
android.useAndroidX=true
|
||||
dagger.fastInit=enabled
|
||||
|
|
Loading…
Reference in New Issue