Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
edfc20a
feat: add verify-mobile reminder and gate wallet buy flow
SeniorZhai Jan 19, 2026
57f457e
Code clean
SeniorZhai Jan 19, 2026
153569d
Landing: refine feature carousel layout and rename resources
SeniorZhai Jan 27, 2026
b05c657
Merge remote-tracking branch 'origin/master' into opt/landing_carousel
SeniorZhai Jan 29, 2026
be8f47e
Merge remote-tracking branch 'origin/master' into feature/verify-mobi…
SeniorZhai Jan 29, 2026
adc68fb
Replace icon
SeniorZhai Jan 29, 2026
62661d3
Merge branch 'feature/verify-mobile-reminder' into opt/landing_carousel
SeniorZhai Jan 29, 2026
9c41848
Update safe area
SeniorZhai Jan 29, 2026
724b97a
Update setup pin
SeniorZhai Jan 29, 2026
21800bc
Merge remote-tracking branch 'origin/master' into opt/landing_carousel
SeniorZhai Feb 2, 2026
0c6a0f2
Update create flow
SeniorZhai Feb 2, 2026
01a9bcb
Merge remote-tracking branch 'origin/master' into opt/landing_carousel
SeniorZhai Feb 3, 2026
5091252
Update account confirm
SeniorZhai Feb 3, 2026
9ceeec5
Update setup name
SeniorZhai Feb 3, 2026
e594926
Update UI
SeniorZhai Feb 4, 2026
77862b0
Update quiz page
SeniorZhai Feb 4, 2026
5af5cc3
Update set pin flow
SeniorZhai Feb 4, 2026
2048c72
feat: Add 12/24 words option to mnemonic phrase input
SeniorZhai Feb 5, 2026
be2eaa1
refactor: Restructure landing feature layou
SeniorZhai Feb 5, 2026
383d980
Merge remote-tracking branch 'origin/master' into opt/landing_carousel
SeniorZhai Feb 5, 2026
300339b
Merge remote-tracking branch 'origin/master' into opt/landing_carousel
SeniorZhai Feb 6, 2026
5f415f9
Open wallet on first entry
SeniorZhai Feb 6, 2026
01c53e4
Update design
SeniorZhai Feb 6, 2026
d19ede6
Update strings, design
SeniorZhai Feb 10, 2026
212f8b4
Update analytics
SeniorZhai Feb 10, 2026
5b3b91f
Merge remote-tracking branch 'origin/master' into opt/landing_carousel
SeniorZhai Feb 10, 2026
21c64a8
Update
SeniorZhai Feb 11, 2026
0f0ea14
Update strings
SeniorZhai Feb 11, 2026
c59b992
Update design
SeniorZhai Feb 11, 2026
0374d99
Merge commit 'f6decfead910fb17dd4e708beb5eb0077098e4cd' into opt/land…
SeniorZhai Feb 11, 2026
3ec769d
Update
SeniorZhai Feb 11, 2026
d7d715b
Pop back stack
SeniorZhai Feb 12, 2026
564c136
Merge remote-tracking branch 'origin/master' into opt/landing_carousel
SeniorZhai Feb 12, 2026
2248a9c
Revert
SeniorZhai Feb 12, 2026
4a69194
Update
SeniorZhai Feb 12, 2026
43afe66
Update
SeniorZhai Feb 12, 2026
69b9ae7
Merge branch 'master' into opt/landing_carousel
SeniorZhai Feb 13, 2026
da2f33a
Merge commit '6fb9edf11408c7f19f3f6772883ab2365caf3616' into opt/land…
SeniorZhai Feb 22, 2026
a769584
Update strings
SeniorZhai Feb 22, 2026
3928661
Merge commit '5ed4a698156f0037b981ce67f17402804369ba35' into opt/land…
SeniorZhai Feb 24, 2026
ad5fa85
keep some tip comment
Tougee Feb 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -762,13 +762,10 @@ class ConversationListFragment : LinkFragment() {
}.not()) {
if (parentFragmentManager.findFragmentByTag(VerifyMobileReminderBottomSheetDialogFragment.TAG) != null) return@launch
if (VerifyMobileReminderBottomSheetDialogFragment.shouldShow(requireContext())) {
try {
VerifyMobileReminderBottomSheetDialogFragment.showSafely(
parentFragmentManager
)
} catch (e: IllegalStateException) {
// Fragment state already saved, skip showing dialog
}
VerifyMobileReminderBottomSheetDialogFragment.showSafely(
parentFragmentManager,
R.string.Verify_Mobile_Number_Desc
)
return@launch
}
ReminderBottomSheetDialogFragment.getType(requireContext(), totalUsd)
Expand Down
49 changes: 31 additions & 18 deletions app/src/main/java/one/mixin/android/ui/home/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,12 @@ class MainActivity : BlazeBaseActivity(), WalletMissingBtcAddressFragment.Callba
return
}

if (Session.getAccount()?.hasPin == false) {
InitializeActivity.showSetupPin(this)
finish()
return
}

if (defaultSharedPreferences.getBoolean(Account.PREF_RESTORE, false)) {
RestoreActivity.show(this)
finish()
Expand Down Expand Up @@ -388,26 +394,33 @@ class MainActivity : BlazeBaseActivity(), WalletMissingBtcAddressFragment.Callba
} else if (Session.getTipPub().isNullOrBlank()) {
TipActivity.show(this, TipType.Upgrade, shouldWatch = true)
} else {
if (Session.hasSafe()) {
jobManager.addJobInBackground(RefreshAccountJob(checkTip = true))
val isLoginVerified: Boolean = defaultSharedPreferences.getBoolean(PREF_LOGIN_VERIFY, false)
val shouldGoWallet: Boolean = defaultSharedPreferences.getBoolean(PREF_LOGIN_OR_SIGN_UP, false)
if (shouldGoWallet) {
defaultSharedPreferences.putBoolean(PREF_LOGIN_OR_SIGN_UP, false)
}
if (isLoginVerified) {
AnalyticsTracker.trackLoginPinVerify("pin_verify")
LoginVerifyBottomSheetDialogFragment.newInstance().apply {
onDismissCallback = { success ->
if (success) {
defaultSharedPreferences.putBoolean(PREF_LOGIN_VERIFY, false)
lifecycleScope.launch {
if (Session.hasSafe()) {
jobManager.addJobInBackground(RefreshAccountJob(checkTip = true))
val isLoginVerified: Boolean = defaultSharedPreferences.getBoolean(PREF_LOGIN_VERIFY, false)
val shouldGoWallet: Boolean = defaultSharedPreferences.getBoolean(PREF_LOGIN_OR_SIGN_UP, false)
val shouldBlockNavigation: Boolean = shouldShowWalletMissingBtcAddress()
Timber.e("isLoginVerified: $isLoginVerified, shouldGoWallet: $shouldGoWallet, shouldBlockNavigation: $shouldBlockNavigation")
if (shouldGoWallet && !shouldBlockNavigation) {
defaultSharedPreferences.putBoolean(PREF_LOGIN_OR_SIGN_UP, false)
binding.bottomNav.selectedItemId = R.id.nav_wallet
switchToDestination(NavigationController.Wallet)
lastBottomNavItemId = R.id.nav_wallet
}
if (isLoginVerified) {
AnalyticsTracker.trackLoginPinVerify("pin_verify")
LoginVerifyBottomSheetDialogFragment.newInstance().apply {
onDismissCallback = { success ->
if (success) {
defaultSharedPreferences.putBoolean(PREF_LOGIN_VERIFY, false)
}
}
}
}.showNow(supportFragmentManager, LoginVerifyBottomSheetDialogFragment.TAG)
}.showNow(supportFragmentManager, LoginVerifyBottomSheetDialogFragment.TAG)
}
} else {
CheckRegisterBottomSheetDialogFragment.newInstance()
.showNow(supportFragmentManager, CheckRegisterBottomSheetDialogFragment.TAG)
}
} else {
CheckRegisterBottomSheetDialogFragment.newInstance()
.showNow(supportFragmentManager, CheckRegisterBottomSheetDialogFragment.TAG)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
package one.mixin.android.ui.landing

import android.annotation.SuppressLint
import android.app.Dialog
import android.content.ClipData
import android.view.Gravity
import android.view.View
import android.view.ViewGroup
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Icon
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import dagger.hilt.android.AndroidEntryPoint
import one.mixin.android.R
import one.mixin.android.compose.theme.MixinAppTheme
import one.mixin.android.extension.booleanFromAttribute
import one.mixin.android.extension.getClipboardManager
import one.mixin.android.extension.getSafeAreaInsetsTop
import one.mixin.android.extension.heavyClickVibrate
import one.mixin.android.extension.isNightMode
import one.mixin.android.extension.screenHeight
import one.mixin.android.extension.toast
import one.mixin.android.ui.common.MixinComposeBottomSheetDialogFragment
import one.mixin.android.ui.landing.components.HighlightedTextWithClick
import one.mixin.android.util.SystemUIManager

@AndroidEntryPoint
class CreateAccountConfirmBottomSheetDialogFragment : MixinComposeBottomSheetDialogFragment() {
companion object {
const val TAG: String = "CreateAccountConfirmBottomSheetDialogFragment"

fun newInstance(): CreateAccountConfirmBottomSheetDialogFragment = CreateAccountConfirmBottomSheetDialogFragment()
}

private var onCreateAccount: (() -> Unit)? = null
private var onPrivacyPolicy: (() -> Unit)? = null
private var onTermsOfService: (() -> Unit)? = null

fun setOnCreateAccount(callback: () -> Unit): CreateAccountConfirmBottomSheetDialogFragment {
onCreateAccount = callback
return this
}

fun setOnPrivacyPolicy(callback: () -> Unit): CreateAccountConfirmBottomSheetDialogFragment {
onPrivacyPolicy = callback
return this
}

fun setOnTermsOfService(callback: () -> Unit): CreateAccountConfirmBottomSheetDialogFragment {
onTermsOfService = callback
return this
}

override fun getTheme() = R.style.AppTheme_Dialog

@Composable
override fun ComposeContent() {
MixinAppTheme {
Column(
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(topEnd = 8.dp, topStart = 8.dp))
.background(MixinAppTheme.colors.background)
.padding(horizontal = 20.dp)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth().padding(vertical = 32.dp)) {
Spacer(modifier = Modifier.weight(1f))
Icon(
modifier = Modifier.clickable {
dismiss()
},
painter = painterResource(id = R.drawable.ic_circle_close),
tint = Color.Unspecified,
contentDescription = stringResource(id = R.string.close)
)
}
Spacer(modifier = Modifier.height(22.dp))
Icon(
modifier = Modifier
.size(64.dp)
.align(Alignment.CenterHorizontally),
painter = painterResource(R.drawable.ic_mnemonic_phrase_creaeting),
contentDescription = null,
tint = Color.Unspecified
)
Spacer(modifier = Modifier.height(24.dp))
Text(
text = stringResource(R.string.create_account_confirm_title),
modifier = Modifier.align(Alignment.CenterHorizontally),
fontSize = 20.sp,
fontWeight = FontWeight.W600,
color = MixinAppTheme.colors.textPrimary
)
Spacer(modifier = Modifier.height(48.dp))
FeatureRow(
iconResId = R.drawable.ic_account_truly,
titleResId = R.string.feature_truly_decentralized,
descriptionResId = R.string.feature_truly_decentralized_description,
)
Spacer(modifier = Modifier.height(14.dp))
FeatureRow(
iconResId = R.drawable.ic_account_privacy,
titleResId = R.string.feature_privacy_by_default,
descriptionResId = R.string.feature_privacy_by_default_description,
)
Spacer(modifier = Modifier.height(14.dp))
FeatureRow(
iconResId = R.drawable.ic_account_all_in_one,
titleResId = R.string.feature_all_in_one,
descriptionResId = R.string.feature_all_in_one_description,
)
Spacer(modifier = Modifier.weight(1f))
Button(
modifier = Modifier
.fillMaxWidth(),
onClick = {
onCreateAccount?.invoke()
dismiss()
},
colors = ButtonDefaults.outlinedButtonColors(backgroundColor = MixinAppTheme.colors.accent),
shape = androidx.compose.foundation.shape.RoundedCornerShape(20.dp),
contentPadding = androidx.compose.foundation.layout.PaddingValues(horizontal = 36.dp, vertical = 11.dp),
) {
Text(text = stringResource(R.string.create_account_confirm_action_create), color = Color.White)
}
Spacer(modifier = Modifier.height(10.dp))
val privacyPolicyText = stringResource(R.string.Privacy_Policy)
val termsOfServiceText = stringResource(R.string.Terms_of_Service)
val landingIntroduction = stringResource(R.string.landing_introduction, privacyPolicyText, termsOfServiceText)
HighlightedTextWithClick(
fullText = landingIntroduction,
modifier = Modifier.align(Alignment.CenterHorizontally),
privacyPolicyText,
termsOfServiceText,
fontSize = 12.sp,
lineHeight = 16.8.sp,
) { clickedText ->
when (clickedText) {
privacyPolicyText -> onPrivacyPolicy?.invoke()
termsOfServiceText -> onTermsOfService?.invoke()
}
}
Spacer(modifier = Modifier.height(30.dp))
}
}
}

@Composable
private fun FeatureRow(
iconResId: Int,
titleResId: Int,
descriptionResId: Int,
) {
Row(modifier = Modifier.fillMaxWidth()) {
Icon(
modifier = Modifier.size(48.dp),
painter = painterResource(iconResId),
contentDescription = null,
tint = Color.Unspecified
)
Spacer(modifier = Modifier.width(12.dp))
Column(modifier = Modifier.weight(1f)) {
Text(
text = stringResource(titleResId),
fontSize = 16.sp,
fontWeight = FontWeight.W600,
color = MixinAppTheme.colors.textPrimary
)
Spacer(modifier = Modifier.height(6.dp))
Text(
text = stringResource(descriptionResId),
fontSize = 14.sp,
color = MixinAppTheme.colors.textAssist
)
}
}
}


override fun getBottomSheetHeight(view: View): Int {
return requireContext().screenHeight() - view.getSafeAreaInsetsTop()
}

override fun showError(error: String) {
}

@SuppressLint("RestrictedApi")
override fun setupDialog(
dialog: Dialog,
style: Int,
) {
super.setupDialog(dialog, R.style.MixinBottomSheet)
dialog.window?.let { window ->
SystemUIManager.lightUI(window, requireContext().isNightMode())
}
dialog.window?.setGravity(Gravity.BOTTOM)
dialog.window?.setLayout(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT,
)
}

override fun onStart() {
super.onStart()
dialog?.window?.let { window ->
SystemUIManager.lightUI(
window,
!requireContext().booleanFromAttribute(R.attr.flag_night),
)
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package one.mixin.android.ui.landing

import android.os.Bundle
import android.view.View
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.fragment.app.Fragment
import one.mixin.android.Constants
import one.mixin.android.R
Expand Down Expand Up @@ -30,6 +32,9 @@ class CreateAccountFragment : Fragment(R.layout.fragment_compose) {
savedInstanceState: Bundle?,
) {
super.onViewCreated(view, savedInstanceState)
if (activity is LandingActivity) {
applySafeTopPadding(view)
}
Timber.e("CreateAccountFragment onViewCreated")
binding.titleView.setSubTitle(requireContext().getString(R.string.Create_Account), "")
binding.titleView.leftIb.setOnClickListener {
Expand Down Expand Up @@ -73,4 +78,14 @@ class CreateAccountFragment : Fragment(R.layout.fragment_compose) {
})
}
}

private fun applySafeTopPadding(rootView: View) {
val originalPaddingTop: Int = rootView.paddingTop
ViewCompat.setOnApplyWindowInsetsListener(rootView) { v: View, insets: WindowInsetsCompat ->
val topInset: Int = insets.getInsets(WindowInsetsCompat.Type.displayCutout()).top
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

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

In the window insets handling, the wrong inset type is being used. The code uses WindowInsetsCompat.Type.displayCutout() instead of WindowInsetsCompat.Type.statusBars(). This is inconsistent with other fragments like VerificationFragment.kt and WebFragment.kt which correctly use statusBars for top padding.

Suggested change
val topInset: Int = insets.getInsets(WindowInsetsCompat.Type.displayCutout()).top
val topInset: Int = insets.getInsets(WindowInsetsCompat.Type.statusBars()).top

Copilot uses AI. Check for mistakes.
v.setPadding(v.paddingLeft, originalPaddingTop + topInset, v.paddingRight, v.paddingBottom)
insets
}
ViewCompat.requestApplyInsets(rootView)
}
}
Loading
Loading