* Add ktlint Upgrade Gradle Fix formatting for ktlint * Upgrade to Gradle 7.0 Bump up NDK, targetSDKVersion & kotlinVersion Fix expected type issue in ScanFragment getPackageInfo() * Revert SDK version. Delete unused file SendAddressFragment.kt Fix comment formatting. Co-authored-by: Kevin Gorham <kevin.gorham@electriccoin.co>
This commit is contained in:
parent
3a45ac5db2
commit
695bffab9a
|
@ -0,0 +1,14 @@
|
|||
# Comma-separated list of rules to disable (Since 0.34.0)
|
||||
# Note that rules in any ruleset other than the standard ruleset will need to be prefixed
|
||||
# by the ruleset identifier.
|
||||
disabled_rules=import-ordering,no-wildcard-imports
|
||||
|
||||
# Defines the imports layout. The layout can be composed by the following symbols:
|
||||
# "*" - wildcard. There must be at least one entry of a single wildcard to match all other imports. Matches anything after a specified symbol/import as well.
|
||||
# "|" - blank line. Supports only single blank lines between imports. No blank line is allowed in the beginning or end of the layout.
|
||||
# "^" - alias import, e.g. "^android.*" will match all android alias imports, "^" will match all other alias imports.
|
||||
# import paths - these can be full paths, e.g. "java.util.List.*" as well as wildcard paths, e.g. "kotlin.**"
|
||||
# Examples (we use ij_kotlin_imports_layout to set an imports layout for both ktlint and IDEA via a single property):
|
||||
ij_kotlin_imports_layout=* # alphabetical with capital letters before lower case letters (e.g. Z before a), no blank lines
|
||||
ij_kotlin_imports_layout=*,java.**,javax.**,kotlin.**,^ # default IntelliJ IDEA style, same as alphabetical, but with "java", "javax", "kotlin" and alias imports in the end of the imports list
|
||||
ij_kotlin_imports_layout=android.**,|,^org.junit.**,kotlin.io.Closeable.*,|,*,^ # custom imports layout
|
|
@ -185,3 +185,7 @@ dependencies {
|
|||
}
|
||||
|
||||
defaultTasks 'clean', 'assembleZcashmainnetRelease'
|
||||
|
||||
apply from: "$rootDir/ktlint.gradle"
|
||||
preBuild.dependsOn('ktlintFormat')
|
||||
preBuild.dependsOn('ktlint')
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
package cash.z.ecc.android
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.Parameterized
|
||||
import cash.z.ecc.android.ui.util.MemoUtil
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.Parameterized
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
// @RunWith(Parameterized::class)
|
||||
|
|
|
@ -14,7 +14,6 @@ class ConversionsTest {
|
|||
|
||||
@Before
|
||||
fun setUp() {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -73,7 +72,6 @@ class ConversionsTest {
|
|||
Assert.assertEquals(1000, result.longValueExact())
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testToBigDecimal_thousandCommaWithDecimal() {
|
||||
val input = "1,000.00"
|
||||
|
@ -109,12 +107,6 @@ class ConversionsTest {
|
|||
Assert.assertEquals(1, result.longValueExact())
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
fun testToZecString_full() {
|
||||
val input = 112_341_123L
|
||||
|
|
|
@ -4,7 +4,6 @@ import android.content.Context
|
|||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import cash.z.ecc.android.lockbox.LockBox
|
||||
import cash.z.ecc.kotlin.mnemonic.Mnemonics
|
||||
import cash.z.ecc.android.sdk.Initializer
|
||||
import cash.z.ecc.android.sdk.type.ZcashNetwork
|
||||
import okio.Buffer
|
||||
|
@ -94,7 +93,6 @@ class IntegrationTest {
|
|||
initializer.erase()
|
||||
}
|
||||
|
||||
|
||||
private fun ByteArray.toHex(): String {
|
||||
val sb = StringBuilder(size * 2)
|
||||
for (b in this)
|
||||
|
|
|
@ -11,10 +11,13 @@ import cash.z.ecc.android.ext.tryWithWarning
|
|||
import cash.z.ecc.android.feedback.FeedbackCoordinator
|
||||
import cash.z.ecc.android.sdk.ext.twig
|
||||
import cash.z.ecc.android.sdk.type.ZcashNetwork
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
class ZcashWalletApp : Application(), CameraXConfig.Provider {
|
||||
|
||||
@Inject
|
||||
|
@ -102,10 +105,9 @@ class ZcashWalletApp : Application(), CameraXConfig.Provider {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
fun ZcashWalletApp.isEmulator(): Boolean {
|
||||
val goldfish = Build.HARDWARE.contains("goldfish");
|
||||
val emu = (System.getProperty("ro.kernel.qemu", "")?.length ?: 0) > 0;
|
||||
val goldfish = Build.HARDWARE.contains("goldfish")
|
||||
val emu = (System.getProperty("ro.kernel.qemu", "")?.length ?: 0) > 0
|
||||
val sdk = Build.MODEL.toLowerCase().contains("sdk")
|
||||
return goldfish || emu || sdk;
|
||||
return goldfish || emu || sdk
|
||||
}
|
||||
|
|
|
@ -5,10 +5,14 @@ import android.content.Context
|
|||
import cash.z.ecc.android.ZcashWalletApp
|
||||
import cash.z.ecc.android.di.component.MainActivitySubcomponent
|
||||
import cash.z.ecc.android.ext.Const
|
||||
import cash.z.ecc.android.feedback.*
|
||||
import cash.z.ecc.android.feedback.Feedback
|
||||
import cash.z.ecc.android.feedback.FeedbackBugsnag
|
||||
import cash.z.ecc.android.feedback.FeedbackConsole
|
||||
import cash.z.ecc.android.feedback.FeedbackCoordinator
|
||||
import cash.z.ecc.android.feedback.FeedbackFile
|
||||
import cash.z.ecc.android.feedback.FeedbackMixpanel
|
||||
import cash.z.ecc.android.lockbox.LockBox
|
||||
import cash.z.ecc.android.sdk.ext.SilentTwig
|
||||
import cash.z.ecc.android.sdk.ext.TroubleshootingTwig
|
||||
import cash.z.ecc.android.sdk.ext.Twig
|
||||
import cash.z.ecc.android.ui.util.DebugFileTwig
|
||||
import dagger.Module
|
||||
|
@ -35,12 +39,10 @@ class AppModule {
|
|||
return LockBox(appContext)
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Feedback
|
||||
//
|
||||
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideFeedback(): Feedback = Feedback()
|
||||
|
@ -59,7 +61,6 @@ class AppModule {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Default Feedback Observer Set
|
||||
//
|
||||
|
|
|
@ -8,6 +8,4 @@ import dagger.Module
|
|||
includes = [ViewModelsActivityModule::class],
|
||||
subcomponents = [SynchronizerSubcomponent::class, InitializerSubcomponent::class]
|
||||
)
|
||||
class MainActivityModule {
|
||||
|
||||
}
|
||||
class MainActivityModule
|
||||
|
|
|
@ -19,5 +19,4 @@ class SynchronizerModule {
|
|||
fun provideSynchronizer(appContext: Context, initializer: Initializer): Synchronizer {
|
||||
return Synchronizer(initializer)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package cash.z.ecc.android.di.module
|
||||
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import cash.z.ecc.android.di.annotation.ActivityScope
|
||||
|
@ -26,7 +25,6 @@ abstract class ViewModelsActivityModule {
|
|||
@ViewModelKey(WalletSetupViewModel::class)
|
||||
abstract fun bindWalletSetupViewModel(implementation: WalletSetupViewModel): ViewModel
|
||||
|
||||
|
||||
/**
|
||||
* Factory for view models that are created until before the Synchronizer exists. This is a
|
||||
* little tricky because we cannot make them all in one place or else they won't be available
|
||||
|
@ -38,5 +36,4 @@ abstract class ViewModelsActivityModule {
|
|||
@Named(Const.Name.BEFORE_SYNCHRONIZER)
|
||||
@Binds
|
||||
abstract fun bindViewModelFactory(viewModelFactory: ViewModelFactory): ViewModelProvider.Factory
|
||||
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
package cash.z.ecc.android.di.module
|
||||
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import cash.z.ecc.android.di.annotation.SynchronizerScope
|
||||
|
|
|
@ -5,7 +5,6 @@ import androidx.lifecycle.ViewModelProvider
|
|||
import cash.z.ecc.android.ui.MainActivity
|
||||
import cash.z.ecc.android.ui.base.BaseFragment
|
||||
|
||||
|
||||
inline fun <reified VM : ViewModel> BaseFragment<*>.viewModel() = object : Lazy<VM> {
|
||||
val cached: VM? = null
|
||||
override fun isInitialized(): Boolean = cached != null
|
||||
|
|
|
@ -5,15 +5,12 @@ import cash.z.ecc.android.ext.ConversionsUniform.LONG_SCALE
|
|||
import cash.z.ecc.android.ext.ConversionsUniform.SHORT_FORMATTER
|
||||
import cash.z.ecc.android.sdk.ext.Conversions
|
||||
import cash.z.ecc.android.sdk.ext.ZcashSdk
|
||||
import cash.z.ecc.android.sdk.ext.convertZatoshiToZec
|
||||
import cash.z.ecc.android.sdk.ext.toZec
|
||||
import java.math.BigDecimal
|
||||
import java.math.MathContext
|
||||
import java.math.RoundingMode
|
||||
import java.text.DecimalFormat
|
||||
import java.text.NumberFormat
|
||||
import java.util.*
|
||||
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* Do the necessary conversions in one place
|
||||
|
@ -75,5 +72,4 @@ object WalletZecFormmatter {
|
|||
BigDecimal(this ?: 0L, MathContext.DECIMAL128)
|
||||
.divide(ConversionsUniform.ONE_ZEC_IN_ZATOSHI)
|
||||
.setScale(LONG_SCALE, ConversionsUniform.roundingMode)
|
||||
|
||||
}
|
|
@ -9,7 +9,6 @@ import androidx.core.content.getSystemService
|
|||
import cash.z.ecc.android.R
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
|
||||
|
||||
fun Context.showClearDataConfirmation(onDismiss: () -> Unit = {}, onCancel: () -> Unit = {}): Dialog {
|
||||
return MaterialAlertDialogBuilder(this)
|
||||
.setTitle(R.string.dialog_nuke_wallet_title)
|
||||
|
@ -39,10 +38,13 @@ fun Context.showUninitializedError(error: Throwable? = null, onDismiss: () -> Un
|
|||
if (error != null) throw error
|
||||
}
|
||||
.setNegativeButton(getString(R.string.dialog_error_uninitialized_button_negative)) { dialog, _ ->
|
||||
showClearDataConfirmation(onDismiss, onCancel = {
|
||||
showClearDataConfirmation(
|
||||
onDismiss,
|
||||
onCancel = {
|
||||
// do not let the user back into the app because we cannot recover from this case
|
||||
showUninitializedError(error, onDismiss)
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ inline fun EditText.limitDecimalPlaces(max: Int) {
|
|||
|
||||
// Restore the cursor position
|
||||
if (oldText != textStr) {
|
||||
val cursorPosition = editText.selectionEnd;
|
||||
val cursorPosition = editText.selectionEnd
|
||||
editText.setText(textStr)
|
||||
editText.setSelection(
|
||||
(cursorPosition - (oldText.length - textStr.length)).coerceIn(
|
||||
|
@ -68,12 +68,10 @@ inline fun EditText.limitDecimalPlaces(max: Int) {
|
|||
}
|
||||
|
||||
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
fun TextView.convertZecToZatoshi(): Long? {
|
||||
return try {
|
||||
text.toString().safelyConvertToBigDecimal()?.convertZecToZatoshi() ?: null
|
||||
|
|
|
@ -7,7 +7,7 @@ import cash.z.ecc.android.sdk.ext.Bush
|
|||
import cash.z.ecc.android.sdk.ext.Twig
|
||||
import cash.z.ecc.android.sdk.ext.twig
|
||||
import cash.z.ecc.android.sdk.type.WalletBalance
|
||||
import java.util.*
|
||||
import java.util.Locale
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
/**
|
||||
|
|
|
@ -32,13 +32,12 @@ internal inline fun @receiver:StringRes Int.toAppStringFormatted(vararg formatAr
|
|||
return ZcashWalletApp.instance.getString(this, *formatArgs)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Grab an integer from the application resources
|
||||
*/
|
||||
internal inline fun @receiver:IntegerRes Int.toAppInt(): Int {
|
||||
return ZcashWalletApp.instance.resources.getInteger(this)}
|
||||
|
||||
return ZcashWalletApp.instance.resources.getInteger(this)
|
||||
}
|
||||
|
||||
fun Float.toPx() = this * Resources.getSystem().displayMetrics.density
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package cash.z.ecc.android.ext
|
||||
|
||||
import android.view.View
|
||||
import android.view.View.*
|
||||
import android.view.View.GONE
|
||||
import android.view.View.INVISIBLE
|
||||
import android.view.View.VISIBLE
|
||||
import cash.z.ecc.android.ui.MainActivity
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.channelFlow
|
||||
|
@ -40,8 +42,10 @@ fun View.onClickNavTo(navResId: Int, block: (() -> Any) = {}) {
|
|||
setOnClickListener {
|
||||
block()
|
||||
(context as? MainActivity)?.safeNavigate(navResId)
|
||||
?: throw IllegalStateException("Cannot navigate from this activity. " +
|
||||
"Expected MainActivity but found ${context.javaClass.simpleName}")
|
||||
?: throw IllegalStateException(
|
||||
"Cannot navigate from this activity. " +
|
||||
"Expected MainActivity but found ${context.javaClass.simpleName}"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,5 +29,4 @@ class FeedbackMixpanel : FeedbackCoordinator.FeedbackObserver {
|
|||
private fun track(eventName: String, properties: Map<String, Any>) {
|
||||
mixpanel.trackMap(eventName, properties)
|
||||
}
|
||||
|
||||
}
|
|
@ -22,11 +22,13 @@ object Report {
|
|||
// Errors
|
||||
abstract class Error(stepName: String, step: Int, val errorCode: Int?, val errorMessage: String?, vararg properties: Pair<String, Any>) : Send("error.$stepName", step, "isError" to true, *properties)
|
||||
object ErrorNotFound : Error("notfound", 51, null, "Key not found")
|
||||
class ErrorEncoding(errorCode: Int? = null, errorMessage: String? = null) : Error("encode", 71, errorCode, errorMessage,
|
||||
class ErrorEncoding(errorCode: Int? = null, errorMessage: String? = null) : Error(
|
||||
"encode", 71, errorCode, errorMessage,
|
||||
"errorCode" to (errorCode ?: -1),
|
||||
"errorMessage" to (errorMessage ?: "None")
|
||||
)
|
||||
class ErrorSubmitting(errorCode: Int? = null, errorMessage: String? = null) : Error("submit", 81, errorCode, errorMessage,
|
||||
class ErrorSubmitting(errorCode: Int? = null, errorMessage: String? = null) : Error(
|
||||
"submit", 81, errorCode, errorMessage,
|
||||
"errorCode" to (errorCode ?: -1),
|
||||
"errorMessage" to (errorMessage ?: "None")
|
||||
)
|
||||
|
@ -241,6 +243,5 @@ class LaunchMetric private constructor(private val metric: Feedback.TimeMetric)
|
|||
override fun toString(): String = metric.toString()
|
||||
}
|
||||
|
||||
|
||||
inline fun <T> Feedback.measure(type: Report.MetricType, block: () -> T): T =
|
||||
this.measure(type.key, type.description, block)
|
|
@ -85,7 +85,6 @@ import kotlinx.coroutines.flow.collect
|
|||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
@Inject
|
||||
|
@ -421,11 +420,14 @@ class MainActivity : AppCompatActivity() {
|
|||
}
|
||||
|
||||
fun onFragmentBackPressed(fragment: Fragment, block: () -> Unit) {
|
||||
onBackPressedDispatcher.addCallback(fragment, object : OnBackPressedCallback(true) {
|
||||
onBackPressedDispatcher.addCallback(
|
||||
fragment,
|
||||
object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
block()
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun showMessage(message: String, linger: Boolean = false) {
|
||||
|
@ -534,7 +536,8 @@ class MainActivity : AppCompatActivity() {
|
|||
if (dialog == null && !ignoreScanFailure) throttle("scanFailure", 20_000L) {
|
||||
notified = true
|
||||
runOnUiThread {
|
||||
dialog = showScanFailure(error,
|
||||
dialog = showScanFailure(
|
||||
error,
|
||||
onCancel = { dialog = null },
|
||||
onDismiss = { dialog = null }
|
||||
)
|
||||
|
@ -564,7 +567,6 @@ class MainActivity : AppCompatActivity() {
|
|||
feedback.report(Reorg(errorHeight, rewindHeight))
|
||||
}
|
||||
|
||||
|
||||
// TODO: maybe move this quick helper code somewhere general or throttle the dialogs differently (like with a flow and stream operators, instead)
|
||||
|
||||
private val throttles = mutableMapOf<String, () -> Any>()
|
||||
|
@ -579,21 +581,19 @@ class MainActivity : AppCompatActivity() {
|
|||
|
||||
// after doing the work, check back in later and if another request came in, throttle it, otherwise exit
|
||||
throttles[key] = noWork
|
||||
findViewById<View>(android.R.id.content).postDelayed({
|
||||
findViewById<View>(android.R.id.content).postDelayed(
|
||||
{
|
||||
throttles[key]?.let { pendingWork ->
|
||||
throttles.remove(key)
|
||||
if (pendingWork !== noWork) throttle(key, delay, pendingWork)
|
||||
}
|
||||
}, delay)
|
||||
},
|
||||
delay
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* Memo functions that might possibly get moved to MemoUtils */
|
||||
|
||||
// private val addressRegex = """zs\d\w{65,}""".toRegex()
|
||||
|
||||
suspend fun getSender(transaction: ConfirmedTransaction?): String {
|
||||
if (transaction == null) return getString(R.string.unknown)
|
||||
return MemoUtil.findAddressInMemo(transaction, ::isValidAddress)?.toAbbreviatedAddress() ?: getString(R.string.unknown)
|
||||
|
@ -655,5 +655,4 @@ class MainActivity : AppCompatActivity() {
|
|||
twig("Warning: failed to open browser due to $t")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -10,10 +10,15 @@ import androidx.lifecycle.lifecycleScope
|
|||
import androidx.viewbinding.ViewBinding
|
||||
import cash.z.ecc.android.feedback.Report
|
||||
import cash.z.ecc.android.ui.MainActivity
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
abstract class BaseFragment<T : ViewBinding> : Fragment() {
|
||||
val mainActivity: MainActivity? get() = activity as MainActivity?
|
||||
|
|
|
@ -10,7 +10,12 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import cash.z.ecc.android.R
|
||||
import cash.z.ecc.android.databinding.FragmentHistoryBinding
|
||||
import cash.z.ecc.android.di.viewmodel.activityViewModel
|
||||
import cash.z.ecc.android.ext.*
|
||||
import cash.z.ecc.android.ext.WalletZecFormmatter
|
||||
import cash.z.ecc.android.ext.goneIf
|
||||
import cash.z.ecc.android.ext.onClickNavUp
|
||||
import cash.z.ecc.android.ext.pending
|
||||
import cash.z.ecc.android.ext.toAppString
|
||||
import cash.z.ecc.android.ext.toColoredSpan
|
||||
import cash.z.ecc.android.feedback.Report
|
||||
import cash.z.ecc.android.feedback.Report.Tap.HISTORY_BACK
|
||||
import cash.z.ecc.android.sdk.db.entity.ConfirmedTransaction
|
||||
|
@ -21,7 +26,6 @@ import cash.z.ecc.android.sdk.type.WalletBalance
|
|||
import cash.z.ecc.android.ui.base.BaseFragment
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
||||
class HistoryFragment : BaseFragment<FragmentHistoryBinding>() {
|
||||
override val screen = Report.Screen.HISTORY
|
||||
|
||||
|
@ -34,7 +38,6 @@ class HistoryFragment : BaseFragment<FragmentHistoryBinding>() {
|
|||
override fun inflate(inflater: LayoutInflater): FragmentHistoryBinding =
|
||||
FragmentHistoryBinding.inflate(inflater)
|
||||
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
twig("HistoryFragment.onViewCreated")
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
@ -61,7 +64,7 @@ class HistoryFragment : BaseFragment<FragmentHistoryBinding>() {
|
|||
goneIf(change <= 0L)
|
||||
val changeString = WalletZecFormmatter.toZecStringFull(change)
|
||||
val expecting = R.string.home_banner_expecting.toAppString(true)
|
||||
text = "($expecting +$changeString ZEC)".toColoredSpan(R.color.text_light, "+${changeString}")
|
||||
text = "($expecting +$changeString ZEC)".toColoredSpan(R.color.text_light, "+$changeString")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,9 +93,12 @@ class HistoryFragment : BaseFragment<FragmentHistoryBinding>() {
|
|||
private fun scrollToTop() {
|
||||
twig("scrolling to the top")
|
||||
binding.recyclerTransactions.apply {
|
||||
postDelayed({
|
||||
postDelayed(
|
||||
{
|
||||
smoothScrollToPosition(0)
|
||||
}, 5L)
|
||||
},
|
||||
5L
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -68,7 +68,6 @@ class HistoryViewModel @Inject constructor() : ViewModel() {
|
|||
var txId: String? = null
|
||||
)
|
||||
|
||||
|
||||
private suspend fun ConfirmedTransaction?.toUiModel(latestHeight: Int? = null): UiModel = UiModel().apply {
|
||||
this@toUiModel.let { tx ->
|
||||
txId = toTxId(tx?.rawTransactionId)
|
||||
|
@ -105,7 +104,8 @@ class HistoryViewModel @Inject constructor() : ViewModel() {
|
|||
if (it.minedHeight > 0 && hasLatestHeight) {
|
||||
val confirmations = latestHeight!! - it.minedHeight + 1
|
||||
confirmation = if (confirmations >= 10) getString(R.string.transaction_status_confirmed) else "$confirmations ${getString(
|
||||
R.string.transaction_status_confirming)}"
|
||||
R.string.transaction_status_confirming
|
||||
)}"
|
||||
} else {
|
||||
if (!hasLatestHeight && isSufficientlyOld(tx)) {
|
||||
twig("Warning: could not load latestheight from server to determine confirmations but this transaction is mined and old enough to be considered confirmed")
|
||||
|
@ -118,11 +118,8 @@ class HistoryViewModel @Inject constructor() : ViewModel() {
|
|||
} else {
|
||||
confirmation = getString(R.string.transaction_status_pending)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// val mainActivity = (context as MainActivity)
|
||||
// inbound v. outbound values
|
||||
when (isInbound) {
|
||||
true -> {
|
||||
topLabel = getString(R.string.transaction_story_inbound)
|
||||
|
@ -166,8 +163,7 @@ class HistoryViewModel @Inject constructor() : ViewModel() {
|
|||
private fun isSufficientlyOld(tx: ConfirmedTransaction): Boolean {
|
||||
val threshold = 75 * 1000 * 25 // approx 25 blocks
|
||||
val delta = System.currentTimeMillis() / 1000L - tx.blockTimeInSeconds
|
||||
return tx.minedHeight > synchronizer.network.saplingActivationHeight
|
||||
&& delta < threshold
|
||||
return tx.minedHeight > synchronizer.network.saplingActivationHeight &&
|
||||
delta < threshold
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -13,9 +13,9 @@ class TransactionAdapter<T : ConfirmedTransaction> :
|
|||
override fun areItemsTheSame(
|
||||
oldItem: T,
|
||||
newItem: T
|
||||
) = oldItem.minedHeight == newItem.minedHeight && oldItem.noteId == newItem.noteId
|
||||
) = 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!!)))
|
||||
((oldItem.raw == null && newItem.raw == null) || (oldItem.raw != null && newItem.raw != null && oldItem.raw!!.contentEquals(newItem.raw!!)))
|
||||
|
||||
override fun areContentsTheSame(
|
||||
oldItem: T,
|
||||
|
|
|
@ -4,32 +4,34 @@ import android.content.res.ColorStateList
|
|||
import android.graphics.ColorMatrix
|
||||
import android.graphics.ColorMatrixColorFilter
|
||||
import android.os.Bundle
|
||||
import android.text.format.DateUtils
|
||||
import android.text.method.ScrollingMovementMethod
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.transition.*
|
||||
import androidx.transition.ChangeBounds
|
||||
import androidx.transition.ChangeClipBounds
|
||||
import androidx.transition.ChangeTransform
|
||||
import androidx.transition.Transition
|
||||
import androidx.transition.TransitionSet
|
||||
import cash.z.ecc.android.R
|
||||
import cash.z.ecc.android.ZcashWalletApp
|
||||
import cash.z.ecc.android.databinding.FragmentTransactionBinding
|
||||
import cash.z.ecc.android.di.viewmodel.activityViewModel
|
||||
import cash.z.ecc.android.ext.*
|
||||
import cash.z.ecc.android.ext.Const
|
||||
import cash.z.ecc.android.ext.gone
|
||||
import cash.z.ecc.android.ext.invisible
|
||||
import cash.z.ecc.android.ext.onClickNavBack
|
||||
import cash.z.ecc.android.ext.toAppColor
|
||||
import cash.z.ecc.android.ext.toColoredSpan
|
||||
import cash.z.ecc.android.ext.visible
|
||||
import cash.z.ecc.android.feedback.Report
|
||||
import cash.z.ecc.android.sdk.db.entity.ConfirmedTransaction
|
||||
import cash.z.ecc.android.sdk.ext.ZcashSdk
|
||||
import cash.z.ecc.android.sdk.ext.toAbbreviatedAddress
|
||||
import cash.z.ecc.android.sdk.ext.twig
|
||||
import cash.z.ecc.android.ui.MainActivity
|
||||
import cash.z.ecc.android.ui.base.BaseFragment
|
||||
import cash.z.ecc.android.ui.history.HistoryViewModel.UiModel
|
||||
import cash.z.ecc.android.ui.util.toUtf8Memo
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.*
|
||||
|
||||
|
||||
class TransactionFragment : BaseFragment<FragmentTransactionBinding>() {
|
||||
override val screen = Report.Screen.TRANSACTION
|
||||
|
@ -117,7 +119,6 @@ class TransactionFragment : BaseFragment<FragmentTransactionBinding>() {
|
|||
uiModel.toAddressLabel()?.let { subwaySpotAddress.visible(); subwayLabelAddress.visible(); subwayLabelAddress.text = it }
|
||||
uiModel.toAddressClickListener()?.let { subwayLabelAddress.setOnClickListener(it) }
|
||||
|
||||
|
||||
// TODO: remove logic from sections below and add more fields or extension functions to UiModel
|
||||
uiModel.confirmation?.let {
|
||||
subwaySpotConfirmations.visible(); subwayLabelConfirmations.visible()
|
||||
|
@ -196,12 +197,4 @@ class TransactionFragment : BaseFragment<FragmentTransactionBinding>() {
|
|||
it.toColoredSpan(R.color.tx_text_light_dimmed, if (address == null) it else prefix)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -4,10 +4,7 @@ import android.graphics.drawable.Drawable
|
|||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.ColorRes
|
||||
import androidx.annotation.IntegerRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import cash.z.ecc.android.R
|
||||
|
@ -22,12 +19,12 @@ import cash.z.ecc.android.sdk.ext.ZcashSdk
|
|||
import cash.z.ecc.android.sdk.ext.isShielded
|
||||
import cash.z.ecc.android.sdk.ext.toAbbreviatedAddress
|
||||
import cash.z.ecc.android.ui.MainActivity
|
||||
import cash.z.ecc.android.ui.util.MemoUtil
|
||||
import cash.z.ecc.android.ui.util.toUtf8Memo
|
||||
import kotlinx.coroutines.launch
|
||||
import java.text.SimpleDateFormat
|
||||
|
||||
class TransactionViewHolder<T : ConfirmedTransaction>(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
|
||||
private val indicator = itemView.findViewById<View>(R.id.indicator)
|
||||
private val amountText = itemView.findViewById<TextView>(R.id.text_transaction_amount)
|
||||
private val topText = itemView.findViewById<TextView>(R.id.text_transaction_top)
|
||||
|
@ -132,6 +129,7 @@ class TransactionViewHolder<T : ConfirmedTransaction>(itemView: View) : Recycler
|
|||
indicator.setBackgroundColor(indicatorBackground.toAppColor())
|
||||
transactionArrow.setColorFilter(arrowBackgroundTint.toAppColor())
|
||||
transactionArrow.rotation = arrowRotation.toAppInt().toFloat()
|
||||
|
||||
var bottomTextRightDrawable: Drawable? = null
|
||||
iconMemo.goneIf(!transaction?.memo.toUtf8Memo().isNotEmpty())
|
||||
bottomText.setCompoundDrawablesWithIntrinsicBounds(null, null, bottomTextRightDrawable, null)
|
||||
|
@ -153,7 +151,4 @@ class TransactionViewHolder<T : ConfirmedTransaction>(itemView: View) : Recycler
|
|||
}
|
||||
|
||||
private inline fun str(@StringRes resourceId: Int) = itemView.context.getString(resourceId)
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@ import android.view.View
|
|||
import androidx.recyclerview.widget.RecyclerView
|
||||
import cash.z.ecc.android.R
|
||||
|
||||
|
||||
class TransactionsFooter(context: Context) : RecyclerView.ItemDecoration() {
|
||||
|
||||
private var footer: Drawable = context.resources.getDrawable(R.drawable.background_footer)
|
||||
|
|
|
@ -68,7 +68,6 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
|
|||
override fun inflate(inflater: LayoutInflater): FragmentHomeBinding =
|
||||
FragmentHomeBinding.inflate(inflater)
|
||||
|
||||
|
||||
//
|
||||
// LifeCycle
|
||||
//
|
||||
|
@ -144,7 +143,6 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
|
|||
// if the model already existed, cool but let the sendViewModel be the source of truth for the amount
|
||||
onModelUpdated(null, uiModel.copy(pendingSend = WalletZecFormmatter.toZecStringFull(sendViewModel.zatoshiAmount.coerceAtLeast(0))))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun onClearAmount() {
|
||||
|
@ -190,7 +188,6 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Public UI API
|
||||
//
|
||||
|
@ -294,7 +291,6 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Private UI Events
|
||||
//
|
||||
|
@ -413,7 +409,6 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
|
|||
}.launchIn(resumedScope)
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Inner classes and extensions
|
||||
//
|
||||
|
@ -444,7 +439,6 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
|
|||
return this
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// User Interruptions
|
||||
//
|
||||
|
|
|
@ -5,14 +5,23 @@ import cash.z.ecc.android.R
|
|||
import cash.z.ecc.android.ext.toAppString
|
||||
import cash.z.ecc.android.sdk.SdkSynchronizer
|
||||
import cash.z.ecc.android.sdk.Synchronizer
|
||||
import cash.z.ecc.android.sdk.Synchronizer.Status.*
|
||||
import cash.z.ecc.android.sdk.Synchronizer.Status.DISCONNECTED
|
||||
import cash.z.ecc.android.sdk.Synchronizer.Status.DOWNLOADING
|
||||
import cash.z.ecc.android.sdk.Synchronizer.Status.SCANNING
|
||||
import cash.z.ecc.android.sdk.Synchronizer.Status.SYNCED
|
||||
import cash.z.ecc.android.sdk.Synchronizer.Status.VALIDATING
|
||||
import cash.z.ecc.android.sdk.block.CompactBlockProcessor
|
||||
import cash.z.ecc.android.sdk.exception.RustLayerException
|
||||
import cash.z.ecc.android.sdk.ext.ZcashSdk.MINERS_FEE_ZATOSHI
|
||||
import cash.z.ecc.android.sdk.ext.ZcashSdk.ZATOSHI_PER_ZEC
|
||||
import cash.z.ecc.android.sdk.ext.twig
|
||||
import kotlinx.coroutines.channels.ConflatedBroadcastChannel
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.asFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.conflate
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.flow.scan
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
|
@ -44,8 +53,8 @@ class HomeViewModel @Inject constructor() : ViewModel() {
|
|||
val zec = typedChars.scan(preTypedChars) { acc, c ->
|
||||
when {
|
||||
// no-op cases
|
||||
acc == "0" && c == '0'
|
||||
|| (c == backspace && acc == "0")
|
||||
acc == "0" && c == '0' ||
|
||||
(c == backspace && acc == "0")
|
||||
|| (c == decimal && acc.contains(decimal)) -> {
|
||||
acc
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package cash.z.ecc.android.ui.home
|
||||
|
||||
import android.animation.ValueAnimator
|
||||
import cash.z.ecc.android.sdk.ext.twig
|
||||
import com.airbnb.lottie.LottieAnimationView
|
||||
|
||||
class MagicSnakeLoader(
|
||||
|
@ -54,14 +53,17 @@ class MagicSnakeLoader(
|
|||
|
||||
private fun startMaybe() {
|
||||
|
||||
if (!isSynced && !isStarted) lottie.postDelayed({
|
||||
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) {
|
||||
lottie.resumeAnimation()
|
||||
isPaused = false
|
||||
isStarted = true
|
||||
}
|
||||
}, 200L)
|
||||
},
|
||||
200L
|
||||
)
|
||||
}
|
||||
|
||||
private val isDownloading get() = downloadProgress in 1..99
|
||||
|
@ -151,4 +153,3 @@ class MagicSnakeLoader(
|
|||
return ((animatedValue as Float) * totalFrames).toInt()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@ import cash.z.ecc.android.feedback.Report
|
|||
import cash.z.ecc.android.feedback.Report.Tap.AWESOME_CLOSE
|
||||
import cash.z.ecc.android.feedback.Report.Tap.AWESOME_SHIELD
|
||||
import cash.z.ecc.android.feedback.Report.Tap.COPY_TRANSPARENT_ADDRESS
|
||||
import cash.z.ecc.android.sdk.SdkSynchronizer
|
||||
import cash.z.ecc.android.sdk.db.entity.PendingTransaction
|
||||
import cash.z.ecc.android.sdk.db.entity.isCancelled
|
||||
import cash.z.ecc.android.sdk.db.entity.isCreated
|
||||
|
@ -37,7 +36,6 @@ import kotlinx.coroutines.flow.launchIn
|
|||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
||||
class AwesomeFragment : BaseFragment<FragmentAwesomeBinding>() {
|
||||
override val screen = Report.Screen.AWESOME
|
||||
|
||||
|
@ -96,7 +94,6 @@ class AwesomeFragment : BaseFragment<FragmentAwesomeBinding>() {
|
|||
viewModel.getTransparentBalance().let { balance ->
|
||||
onBalanceUpdated(balance, utxoCount)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun onAddressLoaded(address: String) {
|
||||
|
@ -200,7 +197,6 @@ class AwesomeFragment : BaseFragment<FragmentAwesomeBinding>() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private fun onShieldComplete(isSuccess: Boolean) {
|
||||
binding.lottieShielding.visibility = View.GONE
|
||||
|
||||
|
@ -248,9 +244,6 @@ class AwesomeFragment : BaseFragment<FragmentAwesomeBinding>() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private fun PendingTransaction.toUiModel() = UiModel().also { model ->
|
||||
when {
|
||||
isCancelled() -> {
|
||||
|
|
|
@ -3,21 +3,16 @@ package cash.z.ecc.android.ui.profile
|
|||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewTreeObserver
|
||||
import android.view.WindowManager
|
||||
import android.widget.Toast
|
||||
import androidx.core.view.doOnLayout
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import cash.z.ecc.android.R
|
||||
import cash.z.ecc.android.databinding.FragmentFeedbackBinding
|
||||
import cash.z.ecc.android.di.viewmodel.viewModel
|
||||
import cash.z.ecc.android.feedback.Report
|
||||
import cash.z.ecc.android.feedback.Report.Funnel.UserFeedback
|
||||
import cash.z.ecc.android.feedback.Report.Tap.FEEDBACK_CANCEL
|
||||
import cash.z.ecc.android.feedback.Report.Tap.FEEDBACK_SUBMIT
|
||||
import cash.z.ecc.android.ui.base.BaseFragment
|
||||
|
||||
|
||||
/**
|
||||
* Fragment representing the home screen of the app. This is the screen most often seen by the user when launching the
|
||||
* application.
|
||||
|
@ -68,7 +63,6 @@ class FeedbackFragment : BaseFragment<FragmentFeedbackBinding>() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Private API
|
||||
//
|
||||
|
|
|
@ -40,7 +40,6 @@ import cash.z.ecc.android.ui.util.DebugFileTwig
|
|||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
|
||||
|
||||
class ProfileFragment : BaseFragment<FragmentProfileBinding>() {
|
||||
override val screen = Report.Screen.PROFILE
|
||||
|
||||
|
@ -93,13 +92,14 @@ class ProfileFragment : BaseFragment<FragmentProfileBinding>() {
|
|||
if (viewModel.isEasterEggTriggered()) {
|
||||
binding.iconProfile.setImageResource(R.drawable.ic_profile_zebra_02)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun onEnterAwesomeMode() {
|
||||
(context as? MainActivity)?.safeNavigate(R.id.action_nav_profile_to_nav_awesome)
|
||||
?: throw IllegalStateException("Cannot navigate from this activity. " +
|
||||
"Expected MainActivity but found ${context?.javaClass?.simpleName}")
|
||||
?: throw IllegalStateException(
|
||||
"Cannot navigate from this activity. " +
|
||||
"Expected MainActivity but found ${context?.javaClass?.simpleName}"
|
||||
)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
|
|
|
@ -6,7 +6,6 @@ import cash.z.ecc.android.ZcashWalletApp
|
|||
import cash.z.ecc.android.ext.Const
|
||||
import cash.z.ecc.android.lockbox.LockBox
|
||||
import cash.z.ecc.android.sdk.Initializer
|
||||
import cash.z.ecc.android.sdk.SdkSynchronizer
|
||||
import cash.z.ecc.android.sdk.Synchronizer
|
||||
import cash.z.ecc.android.sdk.db.entity.PendingTransaction
|
||||
import cash.z.ecc.android.sdk.ext.ZcashSdk
|
||||
|
@ -15,7 +14,6 @@ import cash.z.ecc.android.sdk.tool.DerivationTool
|
|||
import cash.z.ecc.android.sdk.type.WalletBalance
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
|
|
|
@ -8,20 +8,16 @@ import android.view.LayoutInflater
|
|||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import cash.z.android.qrecycler.QRecycler
|
||||
import cash.z.ecc.android.R
|
||||
import cash.z.ecc.android.databinding.FragmentReceiveNewBinding
|
||||
import cash.z.ecc.android.di.viewmodel.viewModel
|
||||
import cash.z.ecc.android.ext.distribute
|
||||
import cash.z.ecc.android.ext.onClickNavBack
|
||||
import cash.z.ecc.android.ext.onClickNavTo
|
||||
import cash.z.ecc.android.feedback.Report
|
||||
import cash.z.ecc.android.feedback.Report.Tap.*
|
||||
import cash.z.ecc.android.ui.base.BaseFragment
|
||||
import cash.z.ecc.android.sdk.ext.toAbbreviatedAddress
|
||||
import cash.z.ecc.android.feedback.Report.Tap.RECEIVE_BACK
|
||||
import cash.z.ecc.android.sdk.ext.twig
|
||||
import cash.z.ecc.android.ui.base.BaseFragment
|
||||
import cash.z.ecc.android.ui.util.AddressPartNumberSpan
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class ReceiveFragment : BaseFragment<FragmentReceiveNewBinding>() {
|
||||
override val screen = Report.Screen.RECEIVE
|
||||
|
|
|
@ -10,7 +10,6 @@ import com.google.zxing.Reader
|
|||
import com.google.zxing.common.HybridBinarizer
|
||||
import com.google.zxing.qrcode.QRCodeReader
|
||||
|
||||
|
||||
class QrAnalyzer(val scanCallback: (qrContent: String, image: ImageProxy) -> Unit) :
|
||||
ImageAnalysis.Analyzer {
|
||||
|
||||
|
@ -63,5 +62,4 @@ class QrAnalyzer(val scanCallback: (qrContent: String, image: ImageProxy) -> Uni
|
|||
private fun onImageScan(result: String, image: ImageProxy) {
|
||||
scanCallback(result, image)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,7 +6,11 @@ import android.os.Bundle
|
|||
import android.util.DisplayMetrics
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.camera.core.*
|
||||
import androidx.camera.core.AspectRatio
|
||||
import androidx.camera.core.CameraSelector
|
||||
import androidx.camera.core.ImageAnalysis
|
||||
import androidx.camera.core.ImageProxy
|
||||
import androidx.camera.core.Preview
|
||||
import androidx.camera.lifecycle.ProcessCameraProvider
|
||||
import androidx.core.content.ContextCompat
|
||||
import cash.z.ecc.android.R
|
||||
|
@ -16,7 +20,6 @@ import cash.z.ecc.android.di.viewmodel.viewModel
|
|||
import cash.z.ecc.android.ext.onClickNavBack
|
||||
import cash.z.ecc.android.feedback.Report
|
||||
import cash.z.ecc.android.feedback.Report.Tap.SCAN_BACK
|
||||
import cash.z.ecc.android.sdk.ext.ZcashSdk
|
||||
import cash.z.ecc.android.sdk.ext.twig
|
||||
import cash.z.ecc.android.ui.base.BaseFragment
|
||||
import cash.z.ecc.android.ui.send.SendViewModel
|
||||
|
@ -26,7 +29,9 @@ import java.util.concurrent.ExecutorService
|
|||
import java.util.concurrent.Executors
|
||||
|
||||
class ScanFragment : BaseFragment<FragmentScanBinding>() {
|
||||
|
||||
override val screen = Report.Screen.SCAN
|
||||
|
||||
private val viewModel: ScanViewModel by viewModel()
|
||||
|
||||
private val sendViewModel: SendViewModel by activityViewModel()
|
||||
|
@ -54,9 +59,12 @@ class ScanFragment : BaseFragment<FragmentScanBinding>() {
|
|||
override fun onAttach(context: Context) {
|
||||
super.onAttach(context)
|
||||
cameraProviderFuture = ProcessCameraProvider.getInstance(context)
|
||||
cameraProviderFuture.addListener(Runnable {
|
||||
cameraProviderFuture.addListener(
|
||||
Runnable {
|
||||
bindPreview(cameraProviderFuture.get())
|
||||
}, ContextCompat.getMainExecutor(context))
|
||||
},
|
||||
ContextCompat.getMainExecutor(context)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
|
@ -87,9 +95,12 @@ class ScanFragment : BaseFragment<FragmentScanBinding>() {
|
|||
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
|
||||
.build()
|
||||
|
||||
imageAnalysis.setAnalyzer(cameraExecutor!!, QrAnalyzer { q, i ->
|
||||
imageAnalysis.setAnalyzer(
|
||||
cameraExecutor!!,
|
||||
QrAnalyzer { q, i ->
|
||||
onQrScanned(q, i)
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
// Must unbind the use-cases before rebinding them
|
||||
cameraProvider.unbindAll()
|
||||
|
@ -102,7 +113,6 @@ class ScanFragment : BaseFragment<FragmentScanBinding>() {
|
|||
mainActivity?.feedback?.report(t)
|
||||
twig("Error while opening the camera: $t")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -114,7 +124,8 @@ class ScanFragment : BaseFragment<FragmentScanBinding>() {
|
|||
height
|
||||
)
|
||||
if (kotlin.math.abs(previewRatio - (4.0 / 3.0))
|
||||
<= kotlin.math.abs(previewRatio - (16.0 / 9.0))) {
|
||||
<= kotlin.math.abs(previewRatio - (16.0 / 9.0))
|
||||
) {
|
||||
return AspectRatio.RATIO_4_3
|
||||
}
|
||||
return AspectRatio.RATIO_16_9
|
||||
|
@ -157,12 +168,6 @@ class ScanFragment : BaseFragment<FragmentScanBinding>() {
|
|||
// overlay.set(list)
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Permissions
|
||||
//
|
||||
|
@ -171,7 +176,7 @@ class ScanFragment : BaseFragment<FragmentScanBinding>() {
|
|||
get() {
|
||||
return try {
|
||||
val info = mainActivity?.packageManager
|
||||
?.getPackageInfo(mainActivity?.packageName, PackageManager.GET_PERMISSIONS)
|
||||
?.getPackageInfo(mainActivity?.packageName ?: "", PackageManager.GET_PERMISSIONS)
|
||||
val ps = info?.requestedPermissions
|
||||
if (ps != null && ps.isNotEmpty()) {
|
||||
ps
|
||||
|
|
|
@ -27,5 +27,4 @@ class ScanViewModel @Inject constructor() : ViewModel() {
|
|||
super.onCleared()
|
||||
twig("${javaClass.simpleName} cleared!")
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,18 +4,15 @@ import android.os.Bundle
|
|||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import cash.z.ecc.android.R
|
||||
import cash.z.ecc.android.databinding.FragmentSendConfirmBinding
|
||||
import cash.z.ecc.android.di.viewmodel.activityViewModel
|
||||
import cash.z.ecc.android.ext.WalletZecFormmatter
|
||||
import cash.z.ecc.android.ext.goneIf
|
||||
import cash.z.ecc.android.ext.onClickNavTo
|
||||
import cash.z.ecc.android.feedback.Report
|
||||
import cash.z.ecc.android.feedback.Report.Funnel.Send
|
||||
import cash.z.ecc.android.feedback.Report.Tap.*
|
||||
import cash.z.ecc.android.ui.base.BaseFragment
|
||||
import cash.z.ecc.android.feedback.Report.Tap.SEND_CONFIRM_NEXT
|
||||
import cash.z.ecc.android.sdk.ext.toAbbreviatedAddress
|
||||
import cash.z.ecc.android.sdk.ext.convertZatoshiToZecString
|
||||
import cash.z.ecc.android.ui.base.BaseFragment
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class SendConfirmFragment : BaseFragment<FragmentSendConfirmBinding>() {
|
||||
|
|
|
@ -13,7 +13,12 @@ import cash.z.ecc.android.ext.WalletZecFormmatter
|
|||
import cash.z.ecc.android.ext.goneIf
|
||||
import cash.z.ecc.android.feedback.Report
|
||||
import cash.z.ecc.android.feedback.Report.Tap.SEND_FINAL_CLOSE
|
||||
import cash.z.ecc.android.sdk.db.entity.*
|
||||
import cash.z.ecc.android.sdk.db.entity.PendingTransaction
|
||||
import cash.z.ecc.android.sdk.db.entity.isCancelled
|
||||
import cash.z.ecc.android.sdk.db.entity.isCreating
|
||||
import cash.z.ecc.android.sdk.db.entity.isFailedEncoding
|
||||
import cash.z.ecc.android.sdk.db.entity.isFailure
|
||||
import cash.z.ecc.android.sdk.db.entity.isSubmitSuccess
|
||||
import cash.z.ecc.android.sdk.ext.toAbbreviatedAddress
|
||||
import cash.z.ecc.android.sdk.ext.twig
|
||||
import cash.z.ecc.android.ui.base.BaseFragment
|
||||
|
@ -128,7 +133,8 @@ class SendFinalFragment : BaseFragment<FragmentSendFinalBinding>() {
|
|||
isFailure() -> {
|
||||
model.title = getString(R.string.send_final_button_primary_failed)
|
||||
model.errorMessage = if (isFailedEncoding()) getString(R.string.send_final_error_encoding) else getString(
|
||||
R.string.send_final_error_submitting)
|
||||
R.string.send_final_error_submitting
|
||||
)
|
||||
model.errorDescription = errorMessage.toString()
|
||||
model.primaryButtonText = getString(R.string.send_final_button_primary_retry)
|
||||
model.primaryAction = { onReturnToSend() }
|
||||
|
@ -158,5 +164,4 @@ class SendFinalFragment : BaseFragment<FragmentSendFinalBinding>() {
|
|||
var primaryButtonText: String = "See Details",
|
||||
var primaryAction: () -> Unit = {}
|
||||
)
|
||||
|
||||
}
|
||||
|
|
|
@ -19,10 +19,24 @@ import androidx.lifecycle.viewModelScope
|
|||
import cash.z.ecc.android.R
|
||||
import cash.z.ecc.android.databinding.FragmentSendBinding
|
||||
import cash.z.ecc.android.di.viewmodel.activityViewModel
|
||||
import cash.z.ecc.android.ext.*
|
||||
import cash.z.ecc.android.ext.WalletZecFormmatter
|
||||
import cash.z.ecc.android.ext.gone
|
||||
import cash.z.ecc.android.ext.goneIf
|
||||
import cash.z.ecc.android.ext.onClickNavUp
|
||||
import cash.z.ecc.android.ext.toAppColor
|
||||
import cash.z.ecc.android.ext.visible
|
||||
import cash.z.ecc.android.feedback.Report
|
||||
import cash.z.ecc.android.feedback.Report.Tap.*
|
||||
import cash.z.ecc.android.sdk.ext.*
|
||||
import cash.z.ecc.android.feedback.Report.Tap.SEND_ADDRESS_BACK
|
||||
import cash.z.ecc.android.feedback.Report.Tap.SEND_ADDRESS_PASTE
|
||||
import cash.z.ecc.android.feedback.Report.Tap.SEND_ADDRESS_REUSE
|
||||
import cash.z.ecc.android.feedback.Report.Tap.SEND_ADDRESS_SCAN
|
||||
import cash.z.ecc.android.feedback.Report.Tap.SEND_MEMO_EXCLUDE
|
||||
import cash.z.ecc.android.feedback.Report.Tap.SEND_MEMO_INCLUDE
|
||||
import cash.z.ecc.android.feedback.Report.Tap.SEND_SUBMIT
|
||||
import cash.z.ecc.android.sdk.ext.ZcashSdk
|
||||
import cash.z.ecc.android.sdk.ext.collectWith
|
||||
import cash.z.ecc.android.sdk.ext.onFirstWith
|
||||
import cash.z.ecc.android.sdk.ext.toAbbreviatedAddress
|
||||
import cash.z.ecc.android.sdk.type.AddressType
|
||||
import cash.z.ecc.android.sdk.type.WalletBalance
|
||||
import cash.z.ecc.android.ui.base.BaseFragment
|
||||
|
@ -30,7 +44,8 @@ import kotlinx.coroutines.delay
|
|||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class SendFragment : BaseFragment<FragmentSendBinding>(),
|
||||
class SendFragment :
|
||||
BaseFragment<FragmentSendBinding>(),
|
||||
ClipboardManager.OnPrimaryClipChangedListener {
|
||||
override val screen = Report.Screen.SEND_ADDRESS
|
||||
|
||||
|
@ -49,7 +64,6 @@ class SendFragment : BaseFragment<FragmentSendBinding>(),
|
|||
applyViewModel(sendViewModel)
|
||||
updateAddressUi(false)
|
||||
|
||||
|
||||
// Apply behaviors
|
||||
|
||||
binding.buttonSend.setOnClickListener {
|
||||
|
@ -172,8 +186,7 @@ class SendFragment : BaseFragment<FragmentSendBinding>(),
|
|||
}
|
||||
|
||||
/**
|
||||
* To hide input Memo and reply-to option for T type address and show a info message about memo option availability
|
||||
* */
|
||||
* To hide input Memo and reply-to option for T type address and show a info message about memo option availability */
|
||||
private fun updateAddressUi(isMemoHidden: Boolean) {
|
||||
if (isMemoHidden) {
|
||||
binding.textLayoutMemo.gone()
|
||||
|
@ -186,7 +199,6 @@ class SendFragment : BaseFragment<FragmentSendBinding>(),
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private fun onSubmit(unused: EditText? = null) {
|
||||
sendViewModel.toAddress = binding.inputZcashAddress.text.toString()
|
||||
sendViewModel.validate(requireContext(), availableZatoshi, maxZatoshi).onFirstWith(resumedScope) { errorMessage ->
|
||||
|
@ -217,7 +229,6 @@ class SendFragment : BaseFragment<FragmentSendBinding>(),
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
override fun onAttach(context: Context) {
|
||||
super.onAttach(context)
|
||||
mainActivity?.clipboard?.addPrimaryClipChangedListener(this)
|
||||
|
@ -280,7 +291,8 @@ class SendFragment : BaseFragment<FragmentSendBinding>(),
|
|||
imageLastUsedShield,
|
||||
lastUsedAddressLabel,
|
||||
selected,
|
||||
address.takeUnless { isBoth })
|
||||
address.takeUnless { isBoth }
|
||||
)
|
||||
}
|
||||
binding.dividerClipboard.setText(if (isBoth) R.string.send_history_last_and_clipboard else R.string.send_history_clipboard)
|
||||
}
|
||||
|
@ -352,7 +364,6 @@ class SendFragment : BaseFragment<FragmentSendBinding>(),
|
|||
return lastUsedAddress
|
||||
}
|
||||
|
||||
|
||||
private fun ClipboardManager.text(): CharSequence =
|
||||
primaryClip!!.getItemAt(0).coerceToText(mainActivity)
|
||||
}
|
|
@ -12,7 +12,11 @@ import cash.z.ecc.android.ext.goneIf
|
|||
import cash.z.ecc.android.ext.onEditorActionDone
|
||||
import cash.z.ecc.android.feedback.Report
|
||||
import cash.z.ecc.android.feedback.Report.Funnel.Send
|
||||
import cash.z.ecc.android.feedback.Report.Tap.*
|
||||
import cash.z.ecc.android.feedback.Report.Tap.SEND_MEMO_CLEAR
|
||||
import cash.z.ecc.android.feedback.Report.Tap.SEND_MEMO_EXCLUDE
|
||||
import cash.z.ecc.android.feedback.Report.Tap.SEND_MEMO_INCLUDE
|
||||
import cash.z.ecc.android.feedback.Report.Tap.SEND_MEMO_NEXT
|
||||
import cash.z.ecc.android.feedback.Report.Tap.SEND_MEMO_SKIP
|
||||
import cash.z.ecc.android.ui.base.BaseFragment
|
||||
import cash.z.ecc.android.ui.util.INCLUDE_MEMO_PREFIX_STANDARD
|
||||
|
||||
|
|
|
@ -15,10 +15,20 @@ import cash.z.ecc.android.feedback.Report.Funnel.Send.SendSelected
|
|||
import cash.z.ecc.android.feedback.Report.Funnel.Send.SpendingKeyFound
|
||||
import cash.z.ecc.android.feedback.Report.Issue
|
||||
import cash.z.ecc.android.feedback.Report.MetricType
|
||||
import cash.z.ecc.android.feedback.Report.MetricType.*
|
||||
import cash.z.ecc.android.feedback.Report.MetricType.TRANSACTION_CREATED
|
||||
import cash.z.ecc.android.feedback.Report.MetricType.TRANSACTION_INITIALIZED
|
||||
import cash.z.ecc.android.feedback.Report.MetricType.TRANSACTION_MINED
|
||||
import cash.z.ecc.android.feedback.Report.MetricType.TRANSACTION_SUBMITTED
|
||||
import cash.z.ecc.android.lockbox.LockBox
|
||||
import cash.z.ecc.android.sdk.Synchronizer
|
||||
import cash.z.ecc.android.sdk.db.entity.*
|
||||
import cash.z.ecc.android.sdk.db.entity.PendingTransaction
|
||||
import cash.z.ecc.android.sdk.db.entity.isCancelled
|
||||
import cash.z.ecc.android.sdk.db.entity.isCreated
|
||||
import cash.z.ecc.android.sdk.db.entity.isCreating
|
||||
import cash.z.ecc.android.sdk.db.entity.isFailedEncoding
|
||||
import cash.z.ecc.android.sdk.db.entity.isFailedSubmit
|
||||
import cash.z.ecc.android.sdk.db.entity.isMined
|
||||
import cash.z.ecc.android.sdk.db.entity.isSubmitSuccess
|
||||
import cash.z.ecc.android.sdk.ext.ZcashSdk
|
||||
import cash.z.ecc.android.sdk.ext.twig
|
||||
import cash.z.ecc.android.sdk.tool.DerivationTool
|
||||
|
@ -140,7 +150,6 @@ class SendViewModel @Inject constructor() : ViewModel() {
|
|||
includeFromAddress = false
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Analytics
|
||||
//
|
||||
|
@ -176,7 +185,7 @@ class SendViewModel @Inject constructor() : ViewModel() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun updateMetrics(tx: PendingTransaction) {
|
||||
fun updateMetrics(tx: PendingTransaction) {
|
||||
try {
|
||||
when {
|
||||
tx.isMined() -> TRANSACTION_SUBMITTED to TRANSACTION_MINED by tx.id
|
||||
|
@ -192,7 +201,7 @@ class SendViewModel @Inject constructor() : ViewModel() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun report(metricId: String?) {
|
||||
fun report(metricId: String?) {
|
||||
metrics[metricId]?.let { metric ->
|
||||
metric.takeUnless { (it.elapsedTime ?: 0) <= 0L }?.let {
|
||||
viewModelScope.launch {
|
||||
|
@ -236,18 +245,9 @@ class SendViewModel @Inject constructor() : ViewModel() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun Keyed<String>.toMetricIdFor(id: Long): String = "$id.$key"
|
||||
private fun String.toRelatedMetricId(): String = "$this.related"
|
||||
private fun String.toTxId(): Long = split('.').first().toLong()
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -10,7 +10,13 @@ import cash.z.ecc.android.R
|
|||
import cash.z.ecc.android.ZcashWalletApp
|
||||
import cash.z.ecc.android.databinding.FragmentSettingsBinding
|
||||
import cash.z.ecc.android.di.viewmodel.viewModel
|
||||
import cash.z.ecc.android.ext.*
|
||||
import cash.z.ecc.android.ext.gone
|
||||
import cash.z.ecc.android.ext.onClickNavBack
|
||||
import cash.z.ecc.android.ext.showUpdateServerCriticalError
|
||||
import cash.z.ecc.android.ext.showUpdateServerDialog
|
||||
import cash.z.ecc.android.ext.toAppColor
|
||||
import cash.z.ecc.android.ext.toAppString
|
||||
import cash.z.ecc.android.ext.visible
|
||||
import cash.z.ecc.android.sdk.exception.LightWalletException
|
||||
import cash.z.ecc.android.sdk.ext.collectWith
|
||||
import cash.z.ecc.android.sdk.ext.twig
|
||||
|
@ -53,7 +59,6 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding>() {
|
|||
viewModel.uiModels.collectWith(resumedScope, ::onUiModelUpdated)
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Event handlers
|
||||
//
|
||||
|
@ -137,7 +142,6 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding>() {
|
|||
context?.showUpdateServerCriticalError(message)
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Utilities
|
||||
//
|
||||
|
@ -151,4 +155,3 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding>() {
|
|||
return ColorStateList.valueOf(color.toAppColor())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ import cash.z.ecc.android.lockbox.LockBox
|
|||
import cash.z.ecc.android.sdk.Synchronizer
|
||||
import cash.z.ecc.android.sdk.ext.twig
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.cancellable
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
import kotlin.properties.Delegates.observable
|
||||
|
@ -28,7 +27,6 @@ class SettingsViewModel @Inject constructor() : ViewModel() {
|
|||
var pendingHost by observable("", ::onUpdateModel)
|
||||
var pendingPortText by observable("", ::onUpdateModel)
|
||||
|
||||
|
||||
private fun getHost(): String {
|
||||
return prefs[Const.Pref.SERVER_HOST] ?: Const.Default.Server.HOST
|
||||
}
|
||||
|
|
|
@ -100,9 +100,12 @@ class LandingFragment : BaseFragment<FragmentLandingBinding>() {
|
|||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
view?.postDelayed({
|
||||
view?.postDelayed(
|
||||
{
|
||||
mainActivity?.hideKeyboard()
|
||||
}, 25L)
|
||||
},
|
||||
25L
|
||||
)
|
||||
}
|
||||
|
||||
private fun onSkip(count: Int) {
|
||||
|
|
|
@ -10,7 +10,6 @@ import android.view.MotionEvent
|
|||
import android.view.MotionEvent.ACTION_DOWN
|
||||
import android.view.MotionEvent.ACTION_UP
|
||||
import android.view.View
|
||||
import android.widget.RelativeLayout
|
||||
import android.widget.TextView
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
@ -28,7 +27,6 @@ import cash.z.ecc.android.feedback.Report.Tap.RESTORE_BACK
|
|||
import cash.z.ecc.android.feedback.Report.Tap.RESTORE_CLEAR
|
||||
import cash.z.ecc.android.feedback.Report.Tap.RESTORE_DONE
|
||||
import cash.z.ecc.android.feedback.Report.Tap.RESTORE_SUCCESS
|
||||
import cash.z.ecc.android.sdk.ext.twig
|
||||
import cash.z.ecc.android.ui.base.BaseFragment
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.tylersuehr.chips.Chip
|
||||
|
@ -36,7 +34,6 @@ import com.tylersuehr.chips.ChipsAdapter
|
|||
import com.tylersuehr.chips.SeedWordAdapter
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
||||
class RestoreFragment : BaseFragment<FragmentRestoreBinding>(), View.OnKeyListener {
|
||||
override val screen = Report.Screen.RESTORE
|
||||
|
||||
|
@ -57,7 +54,6 @@ class RestoreFragment : BaseFragment<FragmentRestoreBinding>(), View.OnKeyListen
|
|||
}.also { onChipsModified() }
|
||||
seedWordRecycler.adapter = seedWordAdapter
|
||||
|
||||
|
||||
binding.chipsInput.apply {
|
||||
setFilterableChipList(getChips())
|
||||
setDelimiter("[ ;,]", true)
|
||||
|
@ -116,7 +112,6 @@ class RestoreFragment : BaseFragment<FragmentRestoreBinding>(), View.OnKeyListen
|
|||
touchScreenForUser()
|
||||
}
|
||||
|
||||
|
||||
private fun onExit() {
|
||||
mainActivity?.reportFunnel(Restore.Exit)
|
||||
hideAutoCompleteWords()
|
||||
|
@ -189,12 +184,15 @@ class RestoreFragment : BaseFragment<FragmentRestoreBinding>(), View.OnKeyListen
|
|||
// forcefully show the keyboard as a hack to fix odd behavior where the keyboard
|
||||
// sometimes closes randomly and inexplicably in between seed word entries
|
||||
private fun forceShowKeyboard() {
|
||||
requireView().postDelayed({
|
||||
requireView().postDelayed(
|
||||
{
|
||||
val isDone = (seedWordAdapter?.itemCount ?: 0) > 24
|
||||
val focusedView = if (isDone) binding.inputBirthdate else seedWordAdapter!!.editText
|
||||
mainActivity!!.showKeyboard(focusedView)
|
||||
focusedView.requestFocus()
|
||||
}, 500L)
|
||||
},
|
||||
500L
|
||||
)
|
||||
}
|
||||
|
||||
private fun reportWords(count: Int) {
|
||||
|
@ -220,11 +218,14 @@ class RestoreFragment : BaseFragment<FragmentRestoreBinding>(), View.OnKeyListen
|
|||
|
||||
private fun touchScreenForUser() {
|
||||
seedWordAdapter?.editText?.apply {
|
||||
postDelayed({
|
||||
postDelayed(
|
||||
{
|
||||
seedWordAdapter?.editText?.inputType = InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS or InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
|
||||
dispatchTouchEvent(motionEvent(ACTION_DOWN))
|
||||
dispatchTouchEvent(motionEvent(ACTION_UP))
|
||||
}, 100L)
|
||||
},
|
||||
100L
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -235,7 +236,6 @@ class RestoreFragment : BaseFragment<FragmentRestoreBinding>(), View.OnKeyListen
|
|||
override fun onKey(v: View?, keyCode: Int, event: KeyEvent?): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SeedWordChip(val word: String, var index: Int = -1) : Chip() {
|
||||
|
|
|
@ -7,7 +7,6 @@ import android.widget.TextView
|
|||
import androidx.recyclerview.widget.RecyclerView
|
||||
import cash.z.ecc.android.R
|
||||
import cash.z.ecc.android.ext.toAppColor
|
||||
import cash.z.ecc.android.sdk.ext.twig
|
||||
import cash.z.ecc.android.ui.setup.SeedWordChip
|
||||
|
||||
class SeedWordAdapter : ChipsAdapter {
|
||||
|
@ -24,7 +23,7 @@ class SeedWordAdapter : ChipsAdapter {
|
|||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
if (getItemViewType(position) == CHIP) { // Chips
|
||||
// Display the chip information on the chip view
|
||||
(holder as SeedWordHolder).seedChipView.bind(mDataSource.getSelectedChip(position), position);
|
||||
(holder as SeedWordHolder).seedChipView.bind(mDataSource.getSelectedChip(position), position)
|
||||
} else {
|
||||
val size = mDataSource.selectedChips.size
|
||||
|
||||
|
@ -66,10 +65,13 @@ class SeedWordAdapter : ChipsAdapter {
|
|||
if (mDataSource.originalChips.firstOrNull { it.title == text } != null) {
|
||||
mDataSource.addSelectedChip(DefaultCustomChip(text))
|
||||
mEditText.apply {
|
||||
postDelayed({
|
||||
postDelayed(
|
||||
{
|
||||
setText("")
|
||||
requestFocus()
|
||||
}, 50L)
|
||||
},
|
||||
50L
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -99,4 +101,3 @@ class SeedWordAdapter : ChipsAdapter {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -175,7 +175,6 @@ class WalletSetupViewModel @Inject constructor() : ViewModel() {
|
|||
private fun loadNearestBirthday(network: ZcashNetwork, birthdayHeight: Int? = null) =
|
||||
WalletBirthdayTool.loadNearest(ZcashWalletApp.instance, network, birthdayHeight)
|
||||
|
||||
|
||||
//
|
||||
// Storage Helpers
|
||||
//
|
||||
|
@ -229,5 +228,4 @@ class WalletSetupViewModel @Inject constructor() : ViewModel() {
|
|||
lockBox[Const.Backup.VIEWING_KEY] = vk.extfvk
|
||||
lockBox[Const.Backup.PUBLIC_KEY] = vk.extpub
|
||||
}
|
||||
|
||||
}
|
|
@ -1,12 +1,13 @@
|
|||
package cash.z.ecc.android
|
||||
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.flow.scanReduce
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import kotlin.math.round
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class ScratchPad {
|
||||
|
||||
|
@ -23,7 +24,7 @@ class ScratchPad {
|
|||
t0 = t
|
||||
started = true
|
||||
}
|
||||
println("$Δt\temitting $it");
|
||||
println("$Δt\temitting $it")
|
||||
}
|
||||
val flow2 = flowOf("a", "b", "c", "d", "e", "f").onEach { delay(150); println("$Δt\temitting $it") }
|
||||
val flow3 = flowOf("A", "B").onEach { delay(450); println("$Δt\temitting $it") }
|
||||
|
@ -50,5 +51,4 @@ class ScratchPad {
|
|||
println("got $it")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -2,9 +2,13 @@ package cash.z.ecc.android
|
|||
|
||||
import cash.z.ecc.android.feedback.Feedback
|
||||
import cash.z.ecc.android.sdk.db.entity.PendingTransaction
|
||||
import cash.z.ecc.android.sdk.db.entity.*
|
||||
import cash.z.ecc.android.sdk.db.entity.isCreated
|
||||
import cash.z.ecc.android.sdk.db.entity.isCreating
|
||||
import cash.z.ecc.android.sdk.db.entity.isMined
|
||||
import cash.z.ecc.android.sdk.db.entity.isSubmitSuccess
|
||||
import cash.z.ecc.android.ui.send.SendViewModel
|
||||
import com.nhaarman.mockitokotlin2.*
|
||||
import com.nhaarman.mockitokotlin2.verify
|
||||
import com.nhaarman.mockitokotlin2.whenever
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.newSingleThreadContext
|
||||
import kotlinx.coroutines.test.setMain
|
||||
|
@ -92,7 +96,6 @@ class SendViewModelTest {
|
|||
verify(feedback).report(sendViewModel.metrics.values.first())
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testUpdateMetrics_mined() {
|
||||
assertEquals(true, minedTx.isMined())
|
||||
|
@ -106,5 +109,4 @@ class SendViewModelTest {
|
|||
// Thread.sleep(100)
|
||||
// assertEquals(0, sendViewModel.metrics.size)
|
||||
}
|
||||
|
||||
}
|
|
@ -4,22 +4,19 @@ buildscript {
|
|||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
maven {
|
||||
url 'https://maven.fabric.io/public'
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.1.3'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${Deps.kotlinVersion}"
|
||||
classpath 'io.fabric.tools:gradle:1.31.2'
|
||||
classpath 'com.bugsnag:bugsnag-android-gradle-plugin:4.7.5'
|
||||
classpath 'com.google.gms:google-services:4.3.5'
|
||||
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.5.2'
|
||||
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:${Deps.navigationVersion}"
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
// mavenLocal()
|
||||
google()
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
|
@ -32,4 +29,3 @@ task clean(type: Delete) {
|
|||
}
|
||||
|
||||
defaultTasks 'clean', 'installZcashmainnetRelease'
|
||||
|
||||
|
|
|
@ -6,12 +6,10 @@ object Deps {
|
|||
const val kotlinVersion = "1.4.32"
|
||||
const val navigationVersion = "2.3.0"
|
||||
|
||||
const val compileSdkVersion = 29
|
||||
const val buildToolsVersion = "29.0.2"
|
||||
const val compileSdkVersion = 30
|
||||
const val buildToolsVersion = "30.0.3"
|
||||
const val minSdkVersion = 21
|
||||
const val targetSdkVersion = 29
|
||||
const val versionName = "1.0.0-alpha69"
|
||||
const val versionCode = 1_00_00_169 // 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.
|
||||
const val targetSdkVersion = 30
|
||||
const val packageName = "cash.z.ecc.android"
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#Fri Apr 02 00:54:33 EDT 2021
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.2-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-all.zip
|
||||
distributionPath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
configurations {
|
||||
ktlint
|
||||
}
|
||||
|
||||
dependencies {
|
||||
ktlint "com.pinterest:ktlint:0.41.0"
|
||||
}
|
||||
|
||||
task ktlint(type: org.gradle.api.tasks.JavaExec, group: "verification") {
|
||||
description = "Verifying Kotlin code style.."
|
||||
classpath = configurations.ktlint
|
||||
main = "com.pinterest.ktlint.Main"
|
||||
args "-F", "src/**/*.kt", "--editorconfig=${rootProject.file(".editorconfig")}"
|
||||
}
|
||||
|
||||
task ktlintFormat(type: org.gradle.api.tasks.JavaExec, group: "formatting") {
|
||||
description = "Format Kotlin code style deviations."
|
||||
classpath = configurations.ktlint
|
||||
main = "com.pinterest.ktlint.Main"
|
||||
args "-F", "src/**/*.kt", "--editorconfig=${rootProject.file(".editorconfig")}"
|
||||
}
|
Loading…
Reference in New Issue