Skip to content

Feature/unify recovery reminder#6268

Merged
crossle merged 13 commits intomasterfrom
feature/unify-recovery-reminder
Apr 6, 2026
Merged

Feature/unify recovery reminder#6268
crossle merged 13 commits intomasterfrom
feature/unify-recovery-reminder

Conversation

@SeniorZhai
Copy link
Copy Markdown
Member

No description provided.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR unifies account-recovery related reminders (mnemonic backup / add phone / recovery contact) into a single “Recovery Kit” reminder flow, and wires it into multiple risk entry points (buy/swap/send/receive/transfer) across main wallet and Web3.

Changes:

  • Introduce a new RecoveryReminderBottomSheetDialogFragment and update reminder UI components to support richer content (checklist + link).
  • Replace several legacy reminders (verify mobile strings/assets, backup mnemonic reminder, restore-contact reminder) with the unified recovery reminder gating on “risk actions”.
  • Add navigation support to open the Recovery Kit settings page directly from the reminder.

Reviewed changes

Copilot reviewed 23 out of 25 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
app/src/main/res/values/strings.xml Removes legacy verify-mobile strings; adds unified recovery reminder copy.
app/src/main/res/values-zh-rCN/strings.xml Chinese localization update for the unified recovery reminder copy.
app/src/main/res/layout/fragment_pin_setting.xml Swaps the header illustration to the new recovery kit graphic.
app/src/main/res/drawable/ic_check_circle_filled.xml Adds “checked” icon for recovery checklist UI.
app/src/main/res/drawable/ic_check_circle_empty.xml Adds “unchecked” icon for recovery checklist UI.
app/src/main/res/drawable/bg_reminder_verify_mobile.xml Removes old verify-mobile reminder background vector.
app/src/main/res/drawable/bg_recovery_kit.xml Adds the new recovery kit illustration vector.
app/src/main/java/one/mixin/android/web3/details/Web3TransactionsFragment.kt Adds unified recovery reminder gating before Web3 send/receive/swap and refactors import-key gating.
app/src/main/java/one/mixin/android/ui/wallet/TransactionsFragment.kt Gates send/receive/swap with unified recovery reminder for risk actions.
app/src/main/java/one/mixin/android/ui/wallet/PrivacyWalletFragment.kt Gates buy/send/receive/swap with unified recovery reminder for risk actions.
app/src/main/java/one/mixin/android/ui/wallet/fiatmoney/CalculateFragment.kt Replaces verify-mobile reminder-on-buy with unified recovery reminder risk gating.
app/src/main/java/one/mixin/android/ui/wallet/ClassicWalletFragment.kt Gates buy/send/receive/swap with unified recovery reminder and refactors import-key gating.
app/src/main/java/one/mixin/android/ui/setting/ui/page/RecoveryKitPage.kt Simplifies recovery kit items list and allows access to all items (with gating handled in fragment).
app/src/main/java/one/mixin/android/ui/setting/SettingActivity.kt Adds intent extra + entry point to open the Recovery Kit page directly.
app/src/main/java/one/mixin/android/ui/setting/RecoveryFragment.kt Fixes TAG and adds gating to require phone before navigating to recovery contact.
app/src/main/java/one/mixin/android/ui/home/reminder/VerifyMobileReminderBottomSheetDialogFragment.kt Updates UI to new ReminderPage API and adds “More information” link handling for add-phone copy.
app/src/main/java/one/mixin/android/ui/home/reminder/ReminderPage.kt Refactors ReminderPage to accept composable content slots and adds reusable checklist row component.
app/src/main/java/one/mixin/android/ui/home/reminder/ReminderBottomSheetDialogFragment.kt Removes legacy backup-mnemonic / restore-contact reminder types and updates to new ReminderPage API.
app/src/main/java/one/mixin/android/ui/home/reminder/RecoveryReminderBottomSheetDialogFragment.kt New unified recovery reminder bottom sheet with snooze logic + checklist + deep link to Recovery Kit settings.
app/src/main/java/one/mixin/android/ui/home/MainActivity.kt Blocks BUY/TRANSFER intent flows behind unified recovery reminder when required.
app/src/main/java/one/mixin/android/ui/home/ExploreFragment.kt Blocks explore buy/swap shortcuts behind unified recovery reminder when required.
app/src/main/java/one/mixin/android/ui/home/ConversationListFragment.kt Replaces verify-mobile reminder-on-home with unified recovery reminder-on-home; updates reminder type lookup signature.
app/src/main/java/one/mixin/android/ui/conversation/link/parser/NewSchemeParser.kt Adds extensive Timber logging for pay-link parsing and execution flow.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

modifier = Modifier
.clip(RoundedCornerShape(bottomStart = 8.dp, bottomEnd = 8.dp))
.clickable { recoveryClick.invoke() },
stringResource(R.string.Recovery_Contact), if (Session.hasEmergencyContact()) stringResource(R.string.Added) else stringResource(R.string.Add)
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as the reminder bottom sheet: the Recovery_Contact item uses Session.hasEmergencyContact() directly for its status text. If emergency contact requires a verified/added phone, this can display “Added” even when the user can’t proceed (since RecoveryFragment blocks navigation when !Session.hasPhone()). Consider deriving the “Added” state from Session.hasPhone() && Session.hasEmergencyContact() or disabling the row until a phone is present.

Suggested change
stringResource(R.string.Recovery_Contact), if (Session.hasEmergencyContact()) stringResource(R.string.Added) else stringResource(R.string.Add)
stringResource(R.string.Recovery_Contact), if (Session.hasPhone() && Session.hasEmergencyContact()) stringResource(R.string.Added) else stringResource(R.string.Add)

Copilot uses AI. Check for mistakes.
<string name="mnemonic_phrase_take_long">This shouldn\'t take long.</string>
<string name="Backup_Now">Start Backup</string>
<string name="Backup_Mnemonic_Phrase_desc">Protect your assets by backing up your mnemonic phrase now.</string>
<string name="recovery_reminder_desc">For your account security, please back up your mnemonic phrase, add your mobile number, and set a recovery contact as soon as possible so you can recover your account. Read the documentation for "%1$s".</string>
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new recovery_reminder_desc reads a bit off in English: Read the documentation for "%1$s". Since %1$s is passed More_Information, this becomes “Read the documentation for "More Information"”, which is awkward. Consider rephrasing to match existing copy patterns (e.g., “Read the documentation to learn more about %1$s.”) and drop the extra quotes unless they’re required.

Suggested change
<string name="recovery_reminder_desc">For your account security, please back up your mnemonic phrase, add your mobile number, and set a recovery contact as soon as possible so you can recover your account. Read the documentation for "%1$s".</string>
<string name="recovery_reminder_desc">For your account security, please back up your mnemonic phrase, add your mobile number, and set a recovery contact as soon as possible so you can recover your account. Read the documentation to learn more about %1$s.</string>

Copilot uses AI. Check for mistakes.
import one.mixin.android.ui.landing.components.HighlightedTextWithClick
import one.mixin.android.ui.setting.SettingActivity
import one.mixin.android.util.SystemUIManager
import timber.log.Timber
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

timber.log.Timber is imported but never used in this file, which will fail builds if unused-import checks are enforced (and adds noise otherwise). Please remove the unused import.

Suggested change
import timber.log.Timber

Copilot uses AI. Check for mistakes.
@SeniorZhai SeniorZhai force-pushed the feature/unify-recovery-reminder branch from b95cbdc to dadd173 Compare April 2, 2026 03:30
@SeniorZhai SeniorZhai force-pushed the feature/unify-recovery-reminder branch from dadd173 to 44a5cb7 Compare April 2, 2026 03:52
@SeniorZhai SeniorZhai force-pushed the feature/unify-recovery-reminder branch from ba6fa07 to ee8fc19 Compare April 2, 2026 05:44
@SeniorZhai SeniorZhai force-pushed the feature/unify-recovery-reminder branch from ceb87b1 to 33aa6e5 Compare April 2, 2026 15:49
@SeniorZhai SeniorZhai marked this pull request as ready for review April 3, 2026 03:37
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 36 out of 38 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

return showSafely(
fragmentManager,
enableSnooze = false,
continueOnDismiss = false,
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

showForRiskAction accepts an onContinue callback and many call sites now pass lambdas expecting the risky action to continue after the sheet interaction, but continueOnDismiss is hardcoded to false so onContinue is never invoked. This currently blocks flows like deep-link transfers in MainActivity (the intent is consumed and the action never runs). Either wire onContinue to a user action (e.g., dismiss/secondary button) by enabling continueOnDismiss, or remove the callback + update callers to not rely on it.

Suggested change
continueOnDismiss = false,
continueOnDismiss = true,

Copilot uses AI. Check for mistakes.
Comment on lines +90 to +106
if (isShowing) return false
pendingOnDismissContinueAction = if (continueOnDismiss) onContinue else null
val fragment = RecoveryReminderBottomSheetDialogFragment().apply {
arguments = android.os.Bundle().apply {
putBoolean(ARGS_ENABLE_SNOOZE, enableSnooze)
putBoolean(ARGS_CONTINUE_ON_DISMISS, continueOnDismiss)
putInt(ARGS_DISMISS_TEXT, dismissTextRes)
}
}
return try {
if (fragmentManager.isStateSaved) {
false
} else {
isShowing = true
fragment.show(fragmentManager, TAG)
true
}
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pendingOnDismissContinueAction is assigned before the fragmentManager.isStateSaved guard. If isStateSaved is true, this method returns false but leaves pendingOnDismissContinueAction set, potentially retaining references captured by the lambda (leak) and causing incorrect behavior on the next show attempt. Only set pendingOnDismissContinueAction when actually showing the fragment, or clear it when returning early.

Copilot uses AI. Check for mistakes.
Comment on lines 529 to 532
var amount = t.amount
if (amount.isBlank()) {
Timber.d("$TAG checkUtxo skipped because amount blank ${describeBiometricItem(t)}")
callback.invoke()
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In checkUtxo, when amount is blank you invoke callback, but the function then continues and may (a) call checkUtxoSufficiency with a blank amount and (b) invoke callback a second time in the else branch. This can lead to double navigation / double bottom-sheet presentation. Add an early return after the blank-amount callback (or restructure the logic) to ensure the callback is invoked at most once and blank amounts are not passed into the sufficiency check.

Suggested change
var amount = t.amount
if (amount.isBlank()) {
Timber.d("$TAG checkUtxo skipped because amount blank ${describeBiometricItem(t)}")
callback.invoke()
val amount = t.amount
if (amount.isBlank()) {
Timber.d("$TAG checkUtxo skipped because amount blank ${describeBiometricItem(t)}")
callback.invoke()
return

Copilot uses AI. Check for mistakes.
Comment on lines 64 to 66
try {
Timber.d("$TAG parse start text=$text from=$from")
val urlQueryParser = UrlQueryParser(text.toUri(), from)
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

parse() logs the full input text (likely the entire payment URL) in both debug and error paths. These URLs can include addresses, user IDs, amounts, memo/reference parameters, etc., and should not be written to logs. Consider logging only a redacted form (e.g., scheme + host + payType + traceId) or gating the full text behind a strictly local debug flag.

Copilot uses AI. Check for mistakes.
import one.mixin.android.session.Session
import one.mixin.android.ui.common.BaseFragment
import one.mixin.android.ui.home.web3.Web3ViewModel
import one.mixin.android.ui.home.reminder.RecoveryReminderBottomSheetDialogFragment
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file now has an unused import of RecoveryReminderBottomSheetDialogFragment. If the project enforces no-unused-imports via ktlint/detekt or treats warnings as errors in CI, this will fail the build. Please remove the import or use it.

Suggested change
import one.mixin.android.ui.home.reminder.RecoveryReminderBottomSheetDialogFragment

Copilot uses AI. Check for mistakes.
@SeniorZhai SeniorZhai force-pushed the feature/unify-recovery-reminder branch 2 times, most recently from 9203469 to 32e4223 Compare April 3, 2026 09:03
@SeniorZhai SeniorZhai force-pushed the feature/unify-recovery-reminder branch from 32e4223 to 0d31527 Compare April 3, 2026 09:05
@crossle crossle merged commit c6efc04 into master Apr 6, 2026
3 checks passed
@crossle crossle deleted the feature/unify-recovery-reminder branch April 6, 2026 11:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants