diff --git a/app/src/main/java/one/mixin/android/ui/landing/LandingActivity.kt b/app/src/main/java/one/mixin/android/ui/landing/LandingActivity.kt index 10f097187e..81b0739168 100644 --- a/app/src/main/java/one/mixin/android/ui/landing/LandingActivity.kt +++ b/app/src/main/java/one/mixin/android/ui/landing/LandingActivity.kt @@ -63,23 +63,25 @@ class LandingActivity : BaseActivity() { SystemUIManager.setSafePadding(window, color = colorFromAttribute(R.attr.bg_white), imePadding = true) checkVersion() setContentView(binding.root) - val pin = intent.getStringExtra(ARGS_PIN) - val from = intent.getIntExtra(ARGS_FROM, -1) - val phoneNumber = intent.getStringExtra(ARGS_PHONE_NUM) - val fragment = - if (pin != null) { - MobileFragment.newInstance(pin, FROM_CHANGE_PHONE_ACCOUNT) - } else if (from == FROM_CHANGE_PHONE_ACCOUNT) { - MobileFragment.newInstance(from = FROM_CHANGE_PHONE_ACCOUNT) - } else if (from == FROM_VERIFY_MOBILE_REMINDER) { - MobileFragment.newInstance(from = FROM_VERIFY_MOBILE_REMINDER, phoneNumber = phoneNumber) - } else { - lifecycleScope.launch(Dispatchers.IO) { - jobManager.clear() + if (shouldInitializeLanding(savedInstanceState != null)) { + val pin = intent.getStringExtra(ARGS_PIN) + val from = intent.getIntExtra(ARGS_FROM, -1) + val phoneNumber = intent.getStringExtra(ARGS_PHONE_NUM) + val fragment = + if (pin != null) { + MobileFragment.newInstance(pin, FROM_CHANGE_PHONE_ACCOUNT) + } else if (from == FROM_CHANGE_PHONE_ACCOUNT) { + MobileFragment.newInstance(from = FROM_CHANGE_PHONE_ACCOUNT) + } else if (from == FROM_VERIFY_MOBILE_REMINDER) { + MobileFragment.newInstance(from = FROM_VERIFY_MOBILE_REMINDER, phoneNumber = phoneNumber) + } else { + lifecycleScope.launch(Dispatchers.IO) { + jobManager.clear() + } + LandingFragment.newInstance() } - LandingFragment.newInstance() - } - replaceFragment(fragment, R.id.container) + replaceFragment(fragment, R.id.container) + } } private fun checkVersion(){ @@ -93,3 +95,5 @@ class LandingActivity : BaseActivity() { } } } + +internal fun shouldInitializeLanding(hasSavedInstanceState: Boolean) = !hasSavedInstanceState diff --git a/app/src/main/java/one/mixin/android/ui/landing/MnemonicPhraseFragment.kt b/app/src/main/java/one/mixin/android/ui/landing/MnemonicPhraseFragment.kt index c17dff20f6..b896437bd5 100644 --- a/app/src/main/java/one/mixin/android/ui/landing/MnemonicPhraseFragment.kt +++ b/app/src/main/java/one/mixin/android/ui/landing/MnemonicPhraseFragment.kt @@ -78,6 +78,8 @@ class MnemonicPhraseFragment : BaseFragment(R.layout.fragment_compose) { const val ARGS_MNEMONIC_PHRASE = "mnemonic_phrase" private const val ARGS_PENDING_IMPORT_MNEMONIC = "pending_import_mnemonic" private const val ARGS_PASTED_MNEMONIC = "pasted_mnemonic" + private const val STATE_ERROR_INFO = "error_info" + private const val STATE_REQUEST_FAILED = "request_failed" fun newInstance( words: ArrayList? = null, @@ -129,12 +131,27 @@ class MnemonicPhraseFragment : BaseFragment(R.layout.fragment_compose) { LogViewerBottomSheet.newInstance().showNow(parentFragmentManager, LogViewerBottomSheet.TAG) true } + errorInfo = savedInstanceState?.getString(STATE_ERROR_INFO) + val restoredRequestFailed = savedInstanceState?.getBoolean(STATE_REQUEST_FAILED) == true binding.compose.setContent { MnemonicPhrasePage(!words.isNullOrEmpty(), errorInfo) { anonymousRequest(words) } } - anonymousRequest(words) + if (shouldRequestAnonymousLogin(restoredRequestFailed)) { + anonymousRequest(words) + } else { + landingViewModel.updateMnemonicPhraseState(MnemonicPhraseState.Failure) + } + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + errorInfo?.let { outState.putString(STATE_ERROR_INFO, it) } + outState.putBoolean( + STATE_REQUEST_FAILED, + landingViewModel.mnemonicPhraseState.value == MnemonicPhraseState.Failure, + ) } private fun applySafeTopPadding(rootView: View) { @@ -476,3 +493,5 @@ class MnemonicPhraseFragment : BaseFragment(R.layout.fragment_compose) { } } } + +internal fun shouldRequestAnonymousLogin(restoredRequestFailed: Boolean) = !restoredRequestFailed diff --git a/app/src/test/java/one/mixin/android/ui/landing/LandingActivityStateRestorationTest.kt b/app/src/test/java/one/mixin/android/ui/landing/LandingActivityStateRestorationTest.kt new file mode 100644 index 0000000000..a666eed980 --- /dev/null +++ b/app/src/test/java/one/mixin/android/ui/landing/LandingActivityStateRestorationTest.kt @@ -0,0 +1,27 @@ +package one.mixin.android.ui.landing + +import kotlin.test.Test +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +class LandingActivityStateRestorationTest { + @Test + fun `initializes landing content on first creation`() { + assertTrue(shouldInitializeLanding(hasSavedInstanceState = false)) + } + + @Test + fun `keeps restored fragment stack after recreation`() { + assertFalse(shouldInitializeLanding(hasSavedInstanceState = true)) + } + + @Test + fun `preserves captcha failure without an error message`() { + assertFalse(shouldRequestAnonymousLogin(restoredRequestFailed = true)) + } + + @Test + fun `starts anonymous login when no failure was restored`() { + assertTrue(shouldRequestAnonymousLogin(restoredRequestFailed = false)) + } +}