diff --git a/app/src/main/java/org/mozilla/fenix/components/FxaServer.kt b/app/src/main/java/org/mozilla/fenix/components/FxaServer.kt index 78d76342b..5967bac44 100644 --- a/app/src/main/java/org/mozilla/fenix/components/FxaServer.kt +++ b/app/src/main/java/org/mozilla/fenix/components/FxaServer.kt @@ -7,6 +7,7 @@ import android.content.Context import mozilla.components.service.fxa.ServerConfig import mozilla.components.service.fxa.ServerConfig.Server import org.mozilla.fenix.FeatureFlags +import org.mozilla.fenix.ext.settings /** * Utility to configure Firefox Account servers. @@ -24,6 +25,11 @@ object FxaServer { } fun config(context: Context): ServerConfig { - return ServerConfig(Server.RELEASE, CLIENT_ID, redirectUrl(context)) + val serverOverride = context.settings().overrideFxAServer + val tokenServerOverride = context.settings().overrideSyncTokenServer.ifEmpty { null } + if (serverOverride.isEmpty()) { + return ServerConfig(Server.RELEASE, CLIENT_ID, redirectUrl(context), tokenServerOverride) + } + return ServerConfig(serverOverride, CLIENT_ID, redirectUrl(context), tokenServerOverride) } } diff --git a/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt index f7e529aa1..8aae91345 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt @@ -10,6 +10,8 @@ import android.content.Intent import android.net.Uri import android.os.Bundle import androidx.appcompat.content.res.AppCompatResources +import android.os.Handler +import android.widget.Toast import androidx.lifecycle.lifecycleScope import androidx.navigation.NavDirections import androidx.navigation.findNavController @@ -45,6 +47,7 @@ import org.mozilla.fenix.ext.settings import org.mozilla.fenix.ext.showToolbar import org.mozilla.fenix.settings.account.AccountAuthErrorPreference import org.mozilla.fenix.settings.account.AccountPreference +import kotlin.system.exitProcess @Suppress("LargeClass", "TooManyFunctions") class SettingsFragment : PreferenceFragmentCompat() { @@ -300,6 +303,29 @@ class SettingsFragment : PreferenceFragmentCompat() { requireComponents.core.engine.settings.remoteDebuggingEnabled = newValue true } + + val preferenceFxAOverride = + findPreference(getPreferenceKey(R.string.pref_key_override_fxa_server)) + val preferenceSyncOverride = + findPreference(getPreferenceKey(R.string.pref_key_override_sync_tokenserver)) + + val syncFxAOverrideUpdater = object : StringSharedPreferenceUpdater() { + override fun onPreferenceChange(preference: Preference, newValue: Any?): Boolean { + return super.onPreferenceChange(preference, newValue).also { + updateFxASyncOverrideMenu() + Toast.makeText( + context, + getString(R.string.toast_override_fxa_sync_server_done), + Toast.LENGTH_LONG + ).show() + Handler().postDelayed({ + exitProcess(0) + }, FXA_SYNC_OVERRIDE_EXIT_DELAY) + } + } + } + preferenceFxAOverride?.onPreferenceChangeListener = syncFxAOverrideUpdater + preferenceSyncOverride?.onPreferenceChangeListener = syncFxAOverrideUpdater } private fun navigateFromSettings(directions: NavDirections) { @@ -343,6 +369,8 @@ class SettingsFragment : PreferenceFragmentCompat() { val accountManager = requireComponents.backgroundServices.accountManager val account = accountManager.authenticatedAccount() + updateFxASyncOverrideMenu() + // Signed-in, no problems. if (account != null && !accountManager.accountNeedsReauth()) { preferenceSignIn?.isVisible = false @@ -388,7 +416,31 @@ class SettingsFragment : PreferenceFragmentCompat() { } } + private fun updateFxASyncOverrideMenu() { + val preferenceFxAOverride = + findPreference(getPreferenceKey(R.string.pref_key_override_fxa_server)) + val preferenceSyncOverride = + findPreference(getPreferenceKey(R.string.pref_key_override_sync_tokenserver)) + val settings = requireContext().settings() + val show = settings.overrideFxAServer.isNotEmpty() || + settings.overrideSyncTokenServer.isNotEmpty() || + settings.showSecretDebugMenuThisSession + // Only enable changes to these prefs when the user isn't connected to an account. + val enabled = requireComponents.backgroundServices.accountManager.authenticatedAccount() == null + preferenceFxAOverride?.apply { + isVisible = show + isEnabled = enabled + summary = settings.overrideFxAServer.ifEmpty { null } + } + preferenceSyncOverride?.apply { + isVisible = show + isEnabled = enabled + summary = settings.overrideSyncTokenServer.ifEmpty { null } + } + } + companion object { private const val SCROLL_INDICATOR_DELAY = 10L + private const val FXA_SYNC_OVERRIDE_EXIT_DELAY = 2000L } } diff --git a/app/src/main/java/org/mozilla/fenix/settings/about/AboutFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/about/AboutFragment.kt index 66cda3490..e96d6be35 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/about/AboutFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/about/AboutFragment.kt @@ -10,6 +10,7 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.Toast import androidx.core.content.pm.PackageInfoCompat import androidx.fragment.app.Fragment import androidx.recyclerview.widget.DividerItemDecoration @@ -20,6 +21,7 @@ import org.mozilla.fenix.BuildConfig import org.mozilla.fenix.R import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.ext.requireComponents +import org.mozilla.fenix.ext.settings import org.mozilla.fenix.lib.Do import org.mozilla.fenix.settings.SupportUtils import org.mozilla.fenix.settings.about.AboutItemType.LICENSING_INFO @@ -36,6 +38,8 @@ import org.mozilla.geckoview.BuildConfig as GeckoViewBuildConfig class AboutFragment : Fragment(), AboutPageListener { private lateinit var appName: String private val aboutPageAdapter: AboutPageAdapter = AboutPageAdapter(this) + private var secretDebugMenuClicks = 0 + private var lastDebugMenuToast: Toast? = null override fun onCreateView( inflater: LayoutInflater, @@ -49,6 +53,11 @@ class AboutFragment : Fragment(), AboutPageListener { return rootView } + override fun onResume() { + super.onResume() + secretDebugMenuClicks = 0 + } + @ExperimentalCoroutinesApi override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -63,6 +72,34 @@ class AboutFragment : Fragment(), AboutPageListener { ) } + // 5 taps on the logo activate the "secret" debug menu. + wordmark.setOnClickListener { + // Because the user will mostly likely tap the logo in rapid succession, + // we ensure only 1 toast is shown at any given time. + lastDebugMenuToast?.let { toast -> toast.cancel() } + secretDebugMenuClicks += 1 + when (secretDebugMenuClicks) { + in 2 until SECRET_DEBUG_MENU_CLICKS -> { + val clicksLeft = SECRET_DEBUG_MENU_CLICKS - secretDebugMenuClicks + val toast = Toast.makeText( + context, + getString(R.string.about_debug_menu_toast_progress, clicksLeft), + Toast.LENGTH_SHORT + ) + toast.show() + lastDebugMenuToast = toast + } + SECRET_DEBUG_MENU_CLICKS -> { + Toast.makeText( + context, + getString(R.string.about_debug_menu_toast_done), + Toast.LENGTH_LONG + ).show() + requireContext().settings().showSecretDebugMenuThisSession = true + } + } + } + populateAboutHeader() aboutPageAdapter.submitList(populateAboutList()) } @@ -183,5 +220,7 @@ class AboutFragment : Fragment(), AboutPageListener { companion object { private const val ABOUT_LICENSE_URL = "about:license" + // Number of clicks on the app logo to enable the "secret" debug menu. + private const val SECRET_DEBUG_MENU_CLICKS = 5 } } diff --git a/app/src/main/java/org/mozilla/fenix/utils/Settings.kt b/app/src/main/java/org/mozilla/fenix/utils/Settings.kt index 0510f67d3..5b4c5f4cf 100644 --- a/app/src/main/java/org/mozilla/fenix/utils/Settings.kt +++ b/app/src/main/java/org/mozilla/fenix/utils/Settings.kt @@ -187,6 +187,8 @@ class Settings private constructor( (trackingProtectionOnboardingCount < trackingProtectionOnboardingMaximumCount && !trackingProtectionOnboardingShownThisSession) + var showSecretDebugMenuThisSession = false + val shouldShowSecurityPinWarningSync: Boolean get() = loginsSecureWarningSyncCount < showLoginsSecureWarningSyncMaxCount @@ -597,4 +599,14 @@ class Settings private constructor( appContext.getPreferenceKey(R.string.pref_key_open_links_in_external_app), default = false ) + + var overrideFxAServer by stringPreference( + appContext.getPreferenceKey(R.string.pref_key_override_fxa_server), + default = "" + ) + + var overrideSyncTokenServer by stringPreference( + appContext.getPreferenceKey(R.string.pref_key_override_sync_tokenserver), + default = "" + ) } diff --git a/app/src/main/res/values/preference_keys.xml b/app/src/main/res/values/preference_keys.xml index 1b622472f..fd44fb5bf 100644 --- a/app/src/main/res/values/preference_keys.xml +++ b/app/src/main/res/values/preference_keys.xml @@ -38,6 +38,8 @@ pref_key_account pref_key_sign_in pref_key_account_auth_error + pref_key_override_fxa_server + pref_key_override_sync_tokenserver pref_key_private_mode pref_key_customize pref_key_toolbar diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7b1111b18..9cffd5513 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -202,6 +202,12 @@ Add private browsing shortcut Accessibility + + Custom Firefox Account server + + Custom Sync server + + Firefox Account/Sync server modified. Quitting the application to apply changes… Account @@ -1062,6 +1068,10 @@ Licensing information Libraries that we use + + Debug menu: %1$d click(s) left to enable + Debug menu enabled 1 tab diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 283349827..2f727bef3 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -29,6 +29,20 @@ android:key="@string/pref_key_account_auth_error"/> + + + +