
clairehurst pushed to branch tor-browser-140.3.0esr-15.0-1 at The Tor Project / Applications / Tor Browser Commits: 9538f500 by clairehurst at 2025-09-24T19:03:40-06:00 TB 44027 [android]: Update PBM lockscreen tor-browser#44027 Update PBM lockscreen - - - - - 14 changed files: - + mobile/android/fenix/app/src/beta/res/drawable/tor_browser_app_icon.png - mobile/android/fenix/app/src/main/java/org/mozilla/fenix/biometricauthentication/UnlockPrivateTabsFragment.kt - mobile/android/fenix/app/src/main/java/org/mozilla/fenix/biometricauthentication/UnlockPrivateTabsScreen.kt - mobile/android/fenix/app/src/main/java/org/mozilla/fenix/lifecycle/PrivateBrowsingLockFeature.kt - mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/TabsSettingsFragment.kt - mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/biometric/BiometricUtils.kt - mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayBanner.kt - mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayFragment.kt - mobile/android/fenix/app/src/main/java/org/mozilla/fenix/utils/Settings.kt - + mobile/android/fenix/app/src/main/res/drawable/tor_browser_app_icon.png - mobile/android/fenix/app/src/main/res/values/torbrowser_strings.xml - mobile/android/fenix/app/src/main/res/xml/tabs_preferences.xml - + mobile/android/fenix/app/src/nightly/res/drawable/tor_browser_app_icon.png - + mobile/android/fenix/app/src/release/res/drawable/tor_browser_app_icon.png Changes: ===================================== mobile/android/fenix/app/src/beta/res/drawable/tor_browser_app_icon.png ===================================== Binary files /dev/null and b/mobile/android/fenix/app/src/beta/res/drawable/tor_browser_app_icon.png differ ===================================== mobile/android/fenix/app/src/main/java/org/mozilla/fenix/biometricauthentication/UnlockPrivateTabsFragment.kt ===================================== @@ -56,8 +56,7 @@ class UnlockPrivateTabsFragment : Fragment(), UserInteractionHandler { UnlockPrivateTabsScreen( onUnlockClicked = { requestPrompt() }, onLeaveClicked = { - PrivateBrowsingLocked.seeOtherTabsClicked.record() - closeFragment() + requireActivity().moveTaskToBack(true) }, ) @@ -67,13 +66,14 @@ class UnlockPrivateTabsFragment : Fragment(), UserInteractionHandler { } override fun onBackPressed(): Boolean { - closeFragment() + requireActivity().moveTaskToBack(true) return true } private fun requestPrompt() { DefaultBiometricUtils.bindBiometricsCredentialsPromptOrShowWarning( - titleRes = R.string.pbm_authentication_unlock_private_tabs, + titleRes = R.string.tor_authentication_unlock_private_tabs, + titleRes2 = R.string.app_name, view = requireView(), onShowPinVerification = { intent -> startForResult.launch(intent) }, onAuthSuccess = ::onAuthSuccess, ===================================== mobile/android/fenix/app/src/main/java/org/mozilla/fenix/biometricauthentication/UnlockPrivateTabsScreen.kt ===================================== @@ -31,6 +31,14 @@ import org.mozilla.fenix.ext.isLargeWindow import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.size +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp +import org.mozilla.fenix.compose.parseHtml + private const val FILL_WIDTH_LARGE_WINDOW = 0.5f private const val FILL_WIDTH_DEFAULT = 1.0f @@ -72,16 +80,30 @@ private fun Header() { modifier = Modifier.padding(horizontal = 16.dp), horizontalAlignment = Alignment.CenterHorizontally, ) { - Image( - painter = painterResource(id = R.drawable.ic_pbm_firefox_logo), - contentDescription = null, // decorative only. - modifier = Modifier.padding(32.dp), - ) + Row ( + verticalAlignment = Alignment.CenterVertically + ) { + Image( + painter = painterResource(id = R.drawable.tor_browser_app_icon), + contentDescription = null, // decorative only. + Modifier + .size(62.dp) + .padding(end = 10.dp), + ) + Text( + text = stringResource(R.string.app_name), + color = FirefoxTheme.colors.textPrimary, + fontWeight = FontWeight.Bold, + fontSize = 29.sp, + letterSpacing = 0.18.sp, + lineHeight = 52.sp, + ) + } Spacer(modifier = Modifier.height(24.dp)) Text( - text = stringResource(id = R.string.pbm_authentication_unlock_private_tabs), + text = stringResource(id = R.string.tor_authentication_unlock_private_tabs, stringResource(R.string.app_name)), color = FirefoxTheme.colors.textPrimary, textAlign = TextAlign.Center, style = FirefoxTheme.typography.headline6, @@ -109,15 +131,6 @@ private fun Footer(onUnlockClicked: () -> Unit, onLeaveClicked: () -> Unit) { modifier = Modifier.fillMaxWidth(), onClick = onUnlockClicked, ) - - Spacer(modifier = Modifier.height(8.dp)) - - TextButton( - text = stringResource(R.string.pbm_authentication_leave_private_tabs), - onClick = onLeaveClicked, - textColor = FirefoxTheme.colors.textActionPrimary, - upperCaseText = false, - ) } } ===================================== mobile/android/fenix/app/src/main/java/org/mozilla/fenix/lifecycle/PrivateBrowsingLockFeature.kt ===================================== @@ -312,7 +312,8 @@ fun Fragment.verifyUser( onVerified: (() -> Unit)? = null, ) { biometricUtils.bindBiometricsCredentialsPromptOrShowWarning( - titleRes = R.string.pbm_authentication_unlock_private_tabs, + titleRes = R.string.tor_authentication_unlock_private_tabs, + titleRes2 = R.string.app_name, view = requireView(), onShowPinVerification = { intent -> fallbackVerification.launch(intent) }, onAuthSuccess = { handleVerificationSuccess(requireContext(), onVerified) }, ===================================== mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/TabsSettingsFragment.kt ===================================== @@ -17,6 +17,16 @@ import org.mozilla.fenix.ext.settings import org.mozilla.fenix.ext.showToolbar import org.mozilla.fenix.utils.view.addToRadioGroup +import android.content.Intent +import android.provider.Settings +import androidx.activity.result.ActivityResultLauncher +import androidx.biometric.BiometricManager +import androidx.preference.Preference +import org.mozilla.fenix.ext.registerForActivityResult +import org.mozilla.fenix.settings.biometric.DefaultBiometricUtils +import org.mozilla.fenix.settings.biometric.ext.isAuthenticatorAvailable +import org.mozilla.fenix.settings.biometric.ext.isHardwareAvailable + /** * Lets the user customize auto closing tabs. */ @@ -40,6 +50,31 @@ class TabsSettingsFragment : PreferenceFragmentCompat() { findPreference<PreferenceCategory>(getString(R.string.pref_key_inactive_tabs_category))?.apply { isVisible = !context.settings().shouldDisableNormalMode } + + startForResult = registerForActivityResult( + onFailure = { }, + onSuccess = { onSuccessfulAuthenticationUsingFallbackPrompt() }, + ) + } + + private lateinit var startForResult: ActivityResultLauncher<Intent> + + private fun onSuccessfulAuthenticationUsingFallbackPrompt() { + val newValue = !requireContext().settings().privateBrowsingLockedEnabled + requireContext().settings().privateBrowsingLockedEnabled = newValue + // Update switch state manually + requirePreference<SwitchPreference>(R.string.pref_key_private_browsing_locked_enabled).apply { + isChecked = !isChecked + } + } + + private fun onSuccessfulAuthenticationUsingPrimaryPrompt( + pbmLockEnabled: Boolean, + preference: Preference, + ) { + requireContext().settings().privateBrowsingLockedEnabled = pbmLockEnabled + // Update switch state manually + (preference as? SwitchPreference)?.isChecked = pbmLockEnabled } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -86,6 +121,67 @@ class TabsSettingsFragment : PreferenceFragmentCompat() { radioOneMonth.onClickListener(::enableInactiveTabsSetting) setupRadioGroups() + /** + * Changes in this file for "tor-browser#44027 Update PBM lockscreen" were copied from + * [PrivateBrowsingFragment] and changed to make sense and work for TBA such as removing + * any use of nimbus/glean that was being used for business logic which was making the + * release build variant not work. We should check [PrivateBrowsingFragment] for updates + * when we rebase + * */ + setUpHideBrowsingSessionPreference() + } + + private fun setUpHideBrowsingSessionPreference() { + val biometricManager = BiometricManager.from(requireContext()) + val deviceCapable = biometricManager.isHardwareAvailable() + val userHasEnabledCapability = biometricManager.isAuthenticatorAvailable() + + requirePreference<SwitchPreference>(R.string.pref_key_private_browsing_locked_enabled).apply { + title = getString(R.string.preferences_tor_lock_screen_title, getString(R.string.app_name)) + summary = getString(R.string.preferences_tor_lock_screen_summary, getString(R.string.app_name)) + isChecked = context.settings().privateBrowsingLockedEnabled && + biometricManager.isAuthenticatorAvailable() + isVisible = deviceCapable + isEnabled = userHasEnabledCapability + + setOnPreferenceChangeListener { preference, newValue -> + val pbmLockEnabled = newValue as? Boolean + ?: return@setOnPreferenceChangeListener false + + val titleRes = if (pbmLockEnabled) { + R.string.tor_authentication_enable_lock + } else { + R.string.tor_authentication_disable_lock + } + + DefaultBiometricUtils.bindBiometricsCredentialsPromptOrShowWarning( + titleRes = titleRes, + titleRes2 = R.string.app_name, + view = requireView(), + onShowPinVerification = { intent -> startForResult.launch(intent) }, + onAuthSuccess = { + onSuccessfulAuthenticationUsingPrimaryPrompt( + pbmLockEnabled = pbmLockEnabled, + preference = preference, + ) + }, + onAuthFailure = { }, + ) + + // Cancel toggle change until biometric is successful + false + } + } + + requirePreference<Preference>(R.string.pref_key_private_browsing_lock_device_feature_enabled).apply { + title = getString(R.string.tor_authentication_lock_device_feature_disabled, getString(R.string.app_name)) + isVisible = deviceCapable && !userHasEnabledCapability + + setOnPreferenceClickListener { + context.startActivity(Intent(Settings.ACTION_SECURITY_SETTINGS)) + true + } + } } private fun setupRadioGroups() { ===================================== mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/biometric/BiometricUtils.kt ===================================== @@ -42,6 +42,7 @@ interface BiometricUtils { */ fun bindBiometricsCredentialsPromptOrShowWarning( @StringRes titleRes: Int = R.string.logins_biometric_prompt_message_2, + @StringRes titleRes2: Int = R.string.empty_string, view: View, onShowPinVerification: (Intent) -> Unit, onAuthSuccess: () -> Unit, @@ -56,6 +57,7 @@ object DefaultBiometricUtils : BiometricUtils { @Suppress("Deprecation") override fun bindBiometricsCredentialsPromptOrShowWarning( @StringRes titleRes: Int, + @StringRes titleRes2: Int, view: View, onShowPinVerification: (Intent) -> Unit, onAuthSuccess: () -> Unit, @@ -90,7 +92,7 @@ object DefaultBiometricUtils : BiometricUtils { // Use the BiometricPrompt first if (BiometricPromptFeature.canUseFeature(BiometricManager.from(context))) { biometricPromptFeature.get() - ?.requestAuthentication(context.resources.getString(titleRes)) + ?.requestAuthentication(context.resources.getString(titleRes, context.resources.getString(titleRes2))) return } @@ -99,7 +101,7 @@ object DefaultBiometricUtils : BiometricUtils { if (manager?.isKeyguardSecure == true) { val confirmDeviceCredentialIntent = manager.createConfirmDeviceCredentialIntent( context.resources.getString(R.string.logins_biometric_prompt_message_pin), - context.resources.getString(titleRes), + context.resources.getString(titleRes, context.resources.getString(titleRes2)), ) onShowPinVerification(confirmDeviceCredentialIntent) } else { ===================================== mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayBanner.kt ===================================== @@ -205,7 +205,7 @@ fun TabsTrayBanner( // After this bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1965545 // is resolved, we should swap the button 1 and button 2 click actions. Banner( - message = stringResource(id = R.string.private_tab_cfr_title), + message = stringResource(id = R.string.tor_tab_cfr_title, stringResource(R.string.app_name)), button1Text = stringResource(id = R.string.private_tab_cfr_negative), button2Text = stringResource(id = R.string.private_tab_cfr_positive), onButton1Click = { ===================================== mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayFragment.kt ===================================== @@ -265,7 +265,7 @@ class TabsTrayFragment : AppCompatDialogFragment() { shouldShowTabAutoCloseBanner = requireContext().settings().shouldShowAutoCloseTabsBanner && requireContext().settings().canShowCfr, shouldShowLockPbmBanner = - if (FxNimbus.features.privateBrowsingLock.value().enabled) { + if (true) { shouldShowLockPbmBanner( isPrivateMode = (activity as HomeActivity).browsingModeManager.mode.isPrivate, hasPrivateTabs = requireComponents.core.store.state.privateTabs.isNotEmpty(), @@ -775,10 +775,11 @@ class TabsTrayFragment : AppCompatDialogFragment() { val isAuthenticatorAvailable = BiometricManager.from(requireContext()).isAuthenticatorAvailable() if (!isAuthenticatorAvailable) { - findNavController().navigate(TabsTrayFragmentDirections.actionGlobalPrivateBrowsingFragment()) + findNavController().navigate(TabsTrayFragmentDirections.actionGlobalTabSettingsFragment()) } else { DefaultBiometricUtils.bindBiometricsCredentialsPromptOrShowWarning( - titleRes = R.string.pbm_authentication_enable_lock, + titleRes = R.string.tor_authentication_enable_lock, + titleRes2 = R.string.app_name, view = requireView(), onShowPinVerification = { intent -> enablePbmPinLauncher.launch(intent) }, onAuthSuccess = { ===================================== mobile/android/fenix/app/src/main/java/org/mozilla/fenix/utils/Settings.kt ===================================== @@ -334,10 +334,9 @@ class Settings(private val appContext: Context) : PreferencesHolder { default = false, ) - var privateBrowsingLockedEnabled by lazyFeatureFlagPreference( + var privateBrowsingLockedEnabled by booleanPreference( appContext.getPreferenceKey(R.string.pref_key_private_browsing_locked_enabled), - featureFlag = FxNimbus.features.privateBrowsingLock.value().enabled, - default = { false }, + default = false, ) var shouldReturnToBrowser by booleanPreference( ===================================== mobile/android/fenix/app/src/main/res/drawable/tor_browser_app_icon.png ===================================== Binary files /dev/null and b/mobile/android/fenix/app/src/main/res/drawable/tor_browser_app_icon.png differ ===================================== mobile/android/fenix/app/src/main/res/values/torbrowser_strings.xml ===================================== @@ -142,4 +142,21 @@ <!-- Notification body for closing browser tabs. "%s" will be replaced with the localised application name, such as "Tor Browser". --> <string name="notification_close_tor_browser_tabs_long">Tap or swipe this notification to close %s’s tabs</string> + <!-- Hide Tor Browser tabs authentication screen --> + <!-- Label for the primary action button to unlock private tabs. "%s" will be replaced with the localised application name, such as "Tor Browser" --> + <string name="tor_authentication_unlock_private_tabs">Unlock %s tabs</string> + <!-- Authentication prompt title to enable the private tabs mode. "%s" will be replaced with the localised application name, such as "Tor Browser" --> + <string name="tor_authentication_enable_lock">Enable %s tabs screen lock</string> + <!-- Authentication prompt title to disable the private tabs mode. "%s" will be replaced with the localised application name, such as "Tor Browser" --> + <string name="tor_authentication_disable_lock">Disable %s tabs screen lock</string> + <!-- Label for a preference shown when the device supports screen lock (e.g., PIN, pattern, or password) but the user has not set one up. "%s" will be replaced with the localised application name, such as "Tor Browser" --> + <string name="tor_authentication_lock_device_feature_disabled">Set up screen lock to hide %s tabs</string> + <!-- Title text for the contextual feature recommendation (CFR) suggesting the user enable a screen lock to protect Tor Browser tabs. "%s" will be replaced with the localised application name, such as "Tor Browser" --> + <string name="tor_tab_cfr_title">Use screen lock to hide %s tabs?</string> + + <!-- Preference title for using the screen lock to hide Tor Browser tabs. "%s" will be replaced with the localised application name, such as "Tor Browser" --> + <string name="preferences_tor_lock_screen_title">Use screen lock to hide %s tabs</string> + <!-- Informs the user how to access the tabs when "Use screen lock to hide Tor Browser tabs" is enabled. "%s" will be replaced with the localised application name, such as "Tor Browser" --> + <string name="preferences_tor_lock_screen_summary">View %s tabs with your fingerprint, PIN, or face unlock.</string> + </resources> ===================================== mobile/android/fenix/app/src/main/res/xml/tabs_preferences.xml ===================================== @@ -57,4 +57,14 @@ android:key="@string/pref_key_inactive_tabs" android:title="@string/preferences_inactive_tabs_title"/> </androidx.preference.PreferenceCategory> + + <PreferenceCategory + android:key="@string/pref_key_pbm_lock_category_divider" + android:layout="@xml/preference_category_divider" + android:selectable="false" /> + <SwitchPreference + android:defaultValue="false" + android:key="pref_key_private_browsing_locked_enabled" /> + <Preference + android:key="@string/pref_key_private_browsing_lock_device_feature_enabled" /> </androidx.preference.PreferenceScreen> ===================================== mobile/android/fenix/app/src/nightly/res/drawable/tor_browser_app_icon.png ===================================== Binary files /dev/null and b/mobile/android/fenix/app/src/nightly/res/drawable/tor_browser_app_icon.png differ ===================================== mobile/android/fenix/app/src/release/res/drawable/tor_browser_app_icon.png ===================================== Binary files /dev/null and b/mobile/android/fenix/app/src/release/res/drawable/tor_browser_app_icon.png differ View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/9538f500... -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/9538f500... You're receiving this email because of your account on gitlab.torproject.org.
participants (1)
-
clairehurst (@clairehurst)