Bug and crash fixes.
This commit is contained in:
parent
6da700d683
commit
b630b9fa78
|
@ -24,7 +24,7 @@ fun View.disabledIf(isDisabled: Boolean) {
|
|||
|
||||
fun View.onClickNavTo(navResId: Int) {
|
||||
setOnClickListener {
|
||||
(context as? MainActivity)?.navController?.navigate(navResId)
|
||||
(context as? MainActivity)?.safeNavigate(navResId)
|
||||
?: throw IllegalStateException("Cannot navigate from this activity. " +
|
||||
"Expected MainActivity but found ${context.javaClass.simpleName}")
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package cash.z.ecc.android.ui
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Dialog
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
|
@ -17,6 +18,7 @@ import android.view.WindowManager
|
|||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.Toast
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.annotation.IdRes
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.getSystemService
|
||||
|
@ -34,6 +36,9 @@ import cash.z.ecc.android.feedback.LaunchMetric
|
|||
import cash.z.ecc.android.feedback.Report.NonUserAction.FEEDBACK_STOPPED
|
||||
import cash.z.ecc.android.feedback.Report.NonUserAction.SYNC_START
|
||||
import cash.z.wallet.sdk.Initializer
|
||||
import cash.z.wallet.sdk.exception.CompactBlockProcessorException
|
||||
import cash.z.wallet.sdk.ext.twig
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
@ -41,6 +46,7 @@ import javax.inject.Inject
|
|||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
|
||||
@Inject
|
||||
lateinit var feedback: Feedback
|
||||
|
||||
|
@ -52,11 +58,14 @@ class MainActivity : AppCompatActivity() {
|
|||
|
||||
private val mediaPlayer: MediaPlayer = MediaPlayer()
|
||||
private var snackbar: Snackbar? = null
|
||||
private var dialog: Dialog? = null
|
||||
|
||||
lateinit var navController: NavController
|
||||
lateinit var component: MainActivitySubcomponent
|
||||
lateinit var synchronizerComponent: SynchronizerSubcomponent
|
||||
|
||||
var navController: NavController? = null
|
||||
private val navInitListeners: MutableList<() -> Unit> = mutableListOf()
|
||||
|
||||
private val hasCameraPermission
|
||||
get() = ContextCompat.checkSelfPermission(
|
||||
this,
|
||||
|
@ -118,19 +127,49 @@ class MainActivity : AppCompatActivity() {
|
|||
|
||||
private fun initNavigation() {
|
||||
navController = findNavController(R.id.nav_host_fragment)
|
||||
navController.addOnDestinationChangedListener { _, _, _ ->
|
||||
navController!!.addOnDestinationChangedListener { _, _, _ ->
|
||||
// hide the keyboard anytime we change destinations
|
||||
getSystemService<InputMethodManager>()?.hideSoftInputFromWindow(
|
||||
this@MainActivity.window.decorView.rootView.windowToken,
|
||||
InputMethodManager.HIDE_NOT_ALWAYS
|
||||
)
|
||||
}
|
||||
|
||||
for (listener in navInitListeners) {
|
||||
listener()
|
||||
}
|
||||
navInitListeners.clear()
|
||||
}
|
||||
|
||||
fun safeNavigate(@IdRes destination: Int) {
|
||||
if (navController == null) {
|
||||
navInitListeners.add {
|
||||
try {
|
||||
navController?.navigate(destination)
|
||||
} catch (t: Throwable) {
|
||||
twig("WARNING: during callback, did not navigate to destination: R.id.${resources.getResourceEntryName(destination)} due to: $t")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
navController?.navigate(destination)
|
||||
} catch (t: Throwable) {
|
||||
twig("WARNING: did not immediately navigate to destination: R.id.${resources.getResourceEntryName(destination)} due to: $t")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun startSync(initializer: Initializer) {
|
||||
synchronizerComponent = ZcashWalletApp.component.synchronizerSubcomponent().create(initializer)
|
||||
feedback.report(SYNC_START)
|
||||
synchronizerComponent.synchronizer().start(lifecycleScope)
|
||||
if (!::synchronizerComponent.isInitialized) {
|
||||
synchronizerComponent = ZcashWalletApp.component.synchronizerSubcomponent().create(initializer)
|
||||
feedback.report(SYNC_START)
|
||||
synchronizerComponent.synchronizer().let { synchronizer ->
|
||||
synchronizer.onProcessorErrorHandler = ::onProcessorError
|
||||
synchronizer.start(lifecycleScope)
|
||||
}
|
||||
} else {
|
||||
twig("Ignoring request to start sync because sync has already been started!")
|
||||
}
|
||||
}
|
||||
|
||||
fun playSound(fileName: String) {
|
||||
|
@ -218,6 +257,19 @@ class MainActivity : AppCompatActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
fun showKeyboard(focusedView: View) {
|
||||
twig("SHOWING KEYBOARD")
|
||||
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
|
||||
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
imm.showSoftInput(focusedView, InputMethodManager.SHOW_FORCED)
|
||||
}
|
||||
|
||||
fun hideKeyboard() {
|
||||
twig("HIDING KEYBOARD")
|
||||
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
imm.hideSoftInputFromWindow(findViewById<View>(android.R.id.content).windowToken, 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param popUpToInclusive the destination to remove from the stack before opening the camera.
|
||||
* This only takes effect in the common case where the permission is granted.
|
||||
|
@ -250,10 +302,31 @@ class MainActivity : AppCompatActivity() {
|
|||
}
|
||||
|
||||
private fun openCamera(popUpToInclusive: Int? = null) {
|
||||
navController.navigate(popUpToInclusive ?: R.id.action_global_nav_scan)
|
||||
navController?.navigate(popUpToInclusive ?: R.id.action_global_nav_scan)
|
||||
}
|
||||
|
||||
private fun onNoCamera() {
|
||||
showSnackbar("Well, this is awkward. You denied permission for the camera.")
|
||||
}
|
||||
|
||||
private fun onProcessorError(error: Throwable?): Boolean {
|
||||
when (error) {
|
||||
is CompactBlockProcessorException.Uninitialized -> {
|
||||
if (dialog == null)
|
||||
runOnUiThread {
|
||||
dialog = MaterialAlertDialogBuilder(this)
|
||||
.setTitle("Wallet Improperly Initialized")
|
||||
.setMessage("This wallet has not been initialized correctly! Perhaps an error occurred during install.\n\nThis can be fixed with a reset. Please reimport using your backup seed phrase.")
|
||||
.setCancelable(false)
|
||||
.setPositiveButton("Exit") { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
throw error
|
||||
}
|
||||
.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
feedback.report(error)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ abstract class BaseFragment<T : ViewBinding> : Fragment() {
|
|||
|
||||
fun onBackPressNavTo(navResId: Int) {
|
||||
mainActivity?.onFragmentBackPressed(this) {
|
||||
mainActivity?.navController?.navigate(navResId)
|
||||
mainActivity?.safeNavigate(navResId)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@ import cash.z.wallet.sdk.ext.convertZatoshiToZecString
|
|||
import cash.z.wallet.sdk.ext.isShielded
|
||||
import cash.z.wallet.sdk.ext.toAbbreviatedAddress
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import java.nio.charset.Charset
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
|
@ -81,8 +82,8 @@ class TransactionViewHolder<T : ConfirmedTransaction>(itemView: View) : Recycler
|
|||
val txId = transaction.rawTransactionId.toTxId()
|
||||
val detailsMessage: String = "Zatoshi amount: ${transaction.value}\n\n" +
|
||||
"Transaction: $txId" +
|
||||
"${if (transaction.toAddress != null) "\nto: ${transaction.toAddress}" else ""}" +
|
||||
"${if (transaction.memo != null) "\nmemo: ${transaction.toAddress}" else ""}"
|
||||
"${if (transaction.toAddress != null) "\n\nto: ${transaction.toAddress}" else ""}" +
|
||||
"${if (transaction.memo != null) "\n\nmemo: \n${String(transaction.memo!!, Charset.forName("UTF-8"))}" else ""}"
|
||||
|
||||
MaterialAlertDialogBuilder(itemView.context)
|
||||
.setMessage(detailsMessage)
|
||||
|
|
|
@ -21,10 +21,7 @@ import cash.z.ecc.android.ui.setup.WalletSetupViewModel
|
|||
import cash.z.ecc.android.ui.setup.WalletSetupViewModel.WalletSetupState.NO_SEED
|
||||
import cash.z.wallet.sdk.Synchronizer
|
||||
import cash.z.wallet.sdk.Synchronizer.Status.SYNCED
|
||||
import cash.z.wallet.sdk.ext.convertZatoshiToZecString
|
||||
import cash.z.wallet.sdk.ext.convertZecToZatoshi
|
||||
import cash.z.wallet.sdk.ext.safelyConvertToBigDecimal
|
||||
import cash.z.wallet.sdk.ext.twig
|
||||
import cash.z.wallet.sdk.ext.*
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
@ -65,7 +62,7 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
|
|||
// interact with user to create, backup and verify seed
|
||||
// leads to a call to startSync(), later (after accounts are created from seed)
|
||||
twig("Seed not found, therefore, launching seed creation flow")
|
||||
mainActivity?.navController?.navigate(R.id.action_nav_home_to_create_wallet)
|
||||
mainActivity?.safeNavigate(R.id.action_nav_home_to_create_wallet)
|
||||
} else {
|
||||
twig("Found seed. Re-opening existing wallet")
|
||||
mainActivity?.startSync(walletSetup.openWallet())
|
||||
|
@ -299,7 +296,7 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
|
|||
}
|
||||
|
||||
private fun onSend() {
|
||||
mainActivity?.navController?.navigate(R.id.action_nav_home_to_send)
|
||||
mainActivity?.safeNavigate(R.id.action_nav_home_to_send)
|
||||
}
|
||||
|
||||
private fun onBannerAction(action: BannerAction) {
|
||||
|
@ -311,7 +308,7 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
|
|||
.setCancelable(true)
|
||||
.setPositiveButton("View Address") { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
mainActivity?.navController?.navigate(R.id.action_nav_home_to_nav_receive)
|
||||
mainActivity?.safeNavigate(R.id.action_nav_home_to_nav_receive)
|
||||
}
|
||||
.show()
|
||||
// MaterialAlertDialogBuilder(activity)
|
||||
|
@ -324,7 +321,7 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
|
|||
// }
|
||||
// .setNegativeButton("View Address") { dialog, _ ->
|
||||
// dialog.dismiss()
|
||||
// mainActivity?.navController?.navigate(R.id.action_nav_home_to_nav_receive)
|
||||
// mainActivity?.safeNavigate(R.id.action_nav_home_to_nav_receive)
|
||||
// }
|
||||
// .show()
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import cash.z.wallet.sdk.SdkSynchronizer
|
|||
import cash.z.wallet.sdk.Synchronizer
|
||||
import cash.z.wallet.sdk.Synchronizer.Status.*
|
||||
import cash.z.wallet.sdk.block.CompactBlockProcessor
|
||||
import cash.z.wallet.sdk.exception.RustLayerException
|
||||
import cash.z.wallet.sdk.ext.ZcashSdk.MINERS_FEE_ZATOSHI
|
||||
import cash.z.wallet.sdk.ext.ZcashSdk.ZATOSHI_PER_ZEC
|
||||
import cash.z.wallet.sdk.ext.twig
|
||||
|
@ -71,7 +72,11 @@ class HomeViewModel @Inject constructor() : ViewModel() {
|
|||
}
|
||||
|
||||
suspend fun refreshBalance() {
|
||||
(synchronizer as SdkSynchronizer).refreshBalance()
|
||||
try {
|
||||
(synchronizer as SdkSynchronizer).refreshBalance()
|
||||
} catch (e: RustLayerException.BalanceException) {
|
||||
twig("Balance refresh failed. This is probably caused by a critical error but we'll give the app a chance to try to recover.")
|
||||
}
|
||||
}
|
||||
|
||||
data class UiModel( // <- THIS ERROR IS AN IDE BUG WITH PARCELIZE
|
||||
|
|
|
@ -79,7 +79,7 @@ class ScanFragment : BaseFragment<FragmentScanBinding>() {
|
|||
if (viewModel.isNotValid(qrContent)) image.close() // continue scanning
|
||||
else {
|
||||
sendViewModel.toAddress = qrContent
|
||||
mainActivity?.navController?.navigate(R.id.action_nav_scan_to_nav_send_address)
|
||||
mainActivity?.safeNavigate(R.id.action_nav_scan_to_nav_send_address)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -99,7 +99,7 @@ class SendAddressFragment : BaseFragment<FragmentSendAddressBinding>(),
|
|||
binding.inputZcashAmount.convertZecToZatoshi()?.let { sendViewModel.zatoshiAmount = it }
|
||||
sendViewModel.validate(maxZatoshi).onFirstWith(resumedScope) {
|
||||
if (it == null) {
|
||||
mainActivity?.navController?.navigate(R.id.action_nav_send_address_to_send_memo)
|
||||
mainActivity?.safeNavigate(R.id.action_nav_send_address_to_send_memo)
|
||||
} else {
|
||||
resumedScope.launch {
|
||||
binding.textAddressError.text = it
|
||||
|
|
|
@ -42,6 +42,6 @@ class SendConfirmFragment : BaseFragment<FragmentSendConfirmBinding>() {
|
|||
}
|
||||
|
||||
private fun onSend() {
|
||||
mainActivity?.navController?.navigate(R.id.action_nav_send_confirm_to_send_final)
|
||||
mainActivity?.safeNavigate(R.id.action_nav_send_confirm_to_send_final)
|
||||
}
|
||||
}
|
|
@ -72,9 +72,8 @@ class SendMemoFragment : BaseFragment<FragmentSendMemoBinding>() {
|
|||
binding.buttonNext.text = "ADD MEMO"
|
||||
binding.buttonSkip.text = "OMIT MEMO"
|
||||
} else {
|
||||
binding.buttonNext.text = "WAIT, GO BACK"
|
||||
binding.buttonNext.text = "GO BACK"
|
||||
binding.buttonSkip.text = "PROCEED"
|
||||
binding.sadTitle.text = binding.sadTitle.text.toString().toColoredSpan(R.color.colorPrimary, "sad")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -94,7 +93,7 @@ class SendMemoFragment : BaseFragment<FragmentSendMemoBinding>() {
|
|||
sendViewModel.memo = binding.inputMemo.text.toString()
|
||||
onNext()
|
||||
} else {
|
||||
mainActivity?.navController?.navigate(R.id.action_nav_send_memo_to_nav_send_address)
|
||||
mainActivity?.safeNavigate(R.id.action_nav_send_memo_to_nav_send_address)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -106,6 +105,6 @@ class SendMemoFragment : BaseFragment<FragmentSendMemoBinding>() {
|
|||
}
|
||||
|
||||
private fun onNext() {
|
||||
mainActivity?.navController?.navigate(R.id.action_nav_send_memo_to_send_confirm)
|
||||
mainActivity?.safeNavigate(R.id.action_nav_send_memo_to_send_confirm)
|
||||
}
|
||||
}
|
|
@ -69,12 +69,17 @@ class LandingFragment : BaseFragment<FragmentLandingBinding>() {
|
|||
walletSetup.checkSeed().onEach {
|
||||
when(it) {
|
||||
SEED_WITHOUT_BACKUP, SEED_WITH_BACKUP -> {
|
||||
mainActivity?.navController?.navigate(R.id.nav_backup)
|
||||
mainActivity?.safeNavigate(R.id.nav_backup)
|
||||
}
|
||||
}
|
||||
}.launchIn(lifecycleScope)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
mainActivity?.hideKeyboard()
|
||||
}
|
||||
|
||||
private fun onSkip(count: Int) {
|
||||
when (count) {
|
||||
1 -> {
|
||||
|
@ -94,7 +99,7 @@ class LandingFragment : BaseFragment<FragmentLandingBinding>() {
|
|||
}
|
||||
|
||||
private fun onRestoreWallet() {
|
||||
mainActivity?.navController?.navigate(R.id.action_nav_landing_to_nav_restore)
|
||||
mainActivity?.safeNavigate(R.id.action_nav_landing_to_nav_restore)
|
||||
}
|
||||
|
||||
// AKA import wallet
|
||||
|
@ -133,7 +138,7 @@ class LandingFragment : BaseFragment<FragmentLandingBinding>() {
|
|||
|
||||
private fun onBackupWallet() {
|
||||
skipCount = 0
|
||||
mainActivity?.navController?.navigate(R.id.action_nav_landing_to_nav_backup)
|
||||
mainActivity?.safeNavigate(R.id.action_nav_landing_to_nav_backup)
|
||||
}
|
||||
|
||||
private fun onEnterWallet() {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<resources>
|
||||
<string name="app_name">Zcash Wallet</string>
|
||||
<string name="app_name">zECC Wallet</string>
|
||||
<string name="receive_address_title">Your Shielded Address</string>
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue