diff --git a/app/src/main/java/org/mozilla/fenix/components/Components.kt b/app/src/main/java/org/mozilla/fenix/components/Components.kt index f6e94701b..24846cc4d 100644 --- a/app/src/main/java/org/mozilla/fenix/components/Components.kt +++ b/app/src/main/java/org/mozilla/fenix/components/Components.kt @@ -17,8 +17,10 @@ import mozilla.components.feature.addons.update.DefaultAddonUpdater import mozilla.components.lib.publicsuffixlist.PublicSuffixList import mozilla.components.support.migration.state.MigrationStore import org.mozilla.fenix.BuildConfig +import org.mozilla.fenix.Config import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.components.metrics.AppStartupTelemetry +import org.mozilla.fenix.ext.settings import org.mozilla.fenix.utils.ClipboardHandler import org.mozilla.fenix.utils.Mockable import org.mozilla.fenix.utils.Settings @@ -71,14 +73,26 @@ class Components(private val context: Context) { } val addonCollectionProvider by lazy { - if (!BuildConfig.AMO_COLLECTION.isNullOrEmpty()) { + // Check if we have a customized (overridden) AMO collection (only supported in Nightly) + if (Config.channel.isNightlyOrDebug && context.settings().amoCollectionOverrideConfigured()) { + AddonCollectionProvider( + context, + core.client, + collectionUser = context.settings().overrideAmoUser, + collectionName = context.settings().overrideAmoCollection + ) + } + // Use build config otherwise + else if (!BuildConfig.AMO_COLLECTION.isNullOrEmpty()) { AddonCollectionProvider( context, core.client, collectionName = BuildConfig.AMO_COLLECTION, maxCacheAgeInMinutes = DAY_IN_MINUTES ) - } else { + } + // Fall back to defaults + else { AddonCollectionProvider(context, core.client, maxCacheAgeInMinutes = DAY_IN_MINUTES) } } 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 6f0719487..df29c2916 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt @@ -6,13 +6,16 @@ package org.mozilla.fenix.settings import android.annotation.SuppressLint import android.content.ActivityNotFoundException +import android.content.DialogInterface import android.content.Intent import android.net.Uri import android.os.Build import android.os.Bundle import android.os.Handler -import android.provider.Settings +import android.view.LayoutInflater import android.widget.Toast +import androidx.annotation.VisibleForTesting +import androidx.appcompat.app.AlertDialog import androidx.lifecycle.lifecycleScope import androidx.navigation.NavDirections import androidx.navigation.findNavController @@ -20,6 +23,8 @@ import androidx.navigation.fragment.navArgs import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import androidx.recyclerview.widget.RecyclerView +import kotlinx.android.synthetic.main.amo_collection_override_dialog.view.* +import kotlinx.android.synthetic.main.fragment_installed_add_on_details.view.* import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -28,6 +33,7 @@ import mozilla.components.concept.sync.AuthType import mozilla.components.concept.sync.OAuthAccount import mozilla.components.concept.sync.Profile import mozilla.components.support.ktx.android.content.getColorFromAttr +import mozilla.components.support.ktx.android.view.showKeyboard import org.mozilla.fenix.BrowserDirection import org.mozilla.fenix.Config import org.mozilla.fenix.FeatureFlags @@ -43,6 +49,7 @@ import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.settings import org.mozilla.fenix.ext.showToolbar import org.mozilla.fenix.settings.account.AccountUiView +import org.mozilla.fenix.utils.Settings import kotlin.system.exitProcess @Suppress("LargeClass", "TooManyFunctions") @@ -304,6 +311,41 @@ class SettingsFragment : PreferenceFragmentCompat() { resources.getString(R.string.pref_key_debug_settings) -> { SettingsFragmentDirections.actionSettingsFragmentToSecretSettingsFragment() } + resources.getString(R.string.pref_key_override_amo_collection) -> { + val context = requireContext() + val dialogView = LayoutInflater.from(context).inflate(R.layout.amo_collection_override_dialog, null) + + AlertDialog.Builder(context).apply { + setTitle(context.getString(R.string.preferences_customize_amo_collection)) + setView(dialogView) + setNegativeButton(R.string.customize_addon_collection_cancel) { dialog: DialogInterface, _ -> + dialog.cancel() + } + + setPositiveButton(R.string.customize_addon_collection_ok) { _, _ -> + context.settings().overrideAmoUser = dialogView.custom_amo_user.text.toString() + context.settings().overrideAmoCollection = dialogView.custom_amo_collection.text.toString() + + Toast.makeText( + context, + getString(R.string.toast_customize_addon_collection_done), + Toast.LENGTH_LONG + ).show() + + Handler().postDelayed({ + exitProcess(0) + }, AMO_COLLECTION_OVERRIDE_EXIT_DELAY) + } + + dialogView.custom_amo_collection.setText(context.settings().overrideAmoCollection) + dialogView.custom_amo_user.setText(context.settings().overrideAmoUser) + dialogView.custom_amo_user.requestFocus() + dialogView.custom_amo_user.showKeyboard() + create() + }.show() + + null + } else -> null } directions?.let { navigateFromSettings(directions) } @@ -374,12 +416,14 @@ class SettingsFragment : PreferenceFragmentCompat() { findPreference( getPreferenceKey(R.string.pref_key_debug_settings) )?.isVisible = requireContext().settings().showSecretDebugMenuThisSession + + setupAmoCollectionOverridePreference(requireContext().settings()) } private fun getClickListenerForMakeDefaultBrowser(): Preference.OnPreferenceClickListener { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { Preference.OnPreferenceClickListener { - val intent = Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS) + val intent = Intent(android.provider.Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS) startActivity(intent) true } @@ -446,8 +490,23 @@ class SettingsFragment : PreferenceFragmentCompat() { } } + @VisibleForTesting + internal fun setupAmoCollectionOverridePreference(settings: Settings) { + val preferenceAmoCollectionOverride = + findPreference(getPreferenceKey(R.string.pref_key_override_amo_collection)) + + val show = (Config.channel.isNightlyOrDebug && ( + settings.amoCollectionOverrideConfigured() || settings.showSecretDebugMenuThisSession) + ) + preferenceAmoCollectionOverride?.apply { + isVisible = show + summary = settings.overrideAmoCollection.ifEmpty { null } + } + } + companion object { private const val SCROLL_INDICATOR_DELAY = 10L private const val FXA_SYNC_OVERRIDE_EXIT_DELAY = 2000L + private const val AMO_COLLECTION_OVERRIDE_EXIT_DELAY = 3000L } } 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 a0bd0ad76..16f07e438 100644 --- a/app/src/main/java/org/mozilla/fenix/utils/Settings.kt +++ b/app/src/main/java/org/mozilla/fenix/utils/Settings.kt @@ -886,6 +886,20 @@ class Settings(private val appContext: Context) : PreferencesHolder { default = "" ) + var overrideAmoUser by stringPreference( + appContext.getPreferenceKey(R.string.pref_key_override_amo_user), + default = "" + ) + + var overrideAmoCollection by stringPreference( + appContext.getPreferenceKey(R.string.pref_key_override_amo_collection), + default = "" + ) + + fun amoCollectionOverrideConfigured(): Boolean { + return overrideAmoUser.isNotEmpty() || overrideAmoCollection.isNotEmpty() + } + val topSitesSize by intPreference( appContext.getPreferenceKey(R.string.pref_key_top_sites_size), default = 0 diff --git a/app/src/main/res/layout/amo_collection_override_dialog.xml b/app/src/main/res/layout/amo_collection_override_dialog.xml new file mode 100644 index 000000000..b913795b4 --- /dev/null +++ b/app/src/main/res/layout/amo_collection_override_dialog.xml @@ -0,0 +1,32 @@ + + + + + + + + + diff --git a/app/src/main/res/values/preference_keys.xml b/app/src/main/res/values/preference_keys.xml index 63673a9b5..d455228fe 100644 --- a/app/src/main/res/values/preference_keys.xml +++ b/app/src/main/res/values/preference_keys.xml @@ -35,6 +35,8 @@ pref_key_delete_browsing_data_on_quit_categories pref_key_last_known_mode_private pref_key_addons + pref_key_override_amo_user + pref_key_override_amo_collection pref_key_last_maintenance pref_key_help pref_key_rate diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4a9e2c55b..1465e3623 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -337,6 +337,20 @@ Notifications + + + Custom Add-on collection + + OK + + Cancel + + Collection name + + Collection owner (User ID) + + Add-on collection modified. Quitting the application to apply changes… + Sync now diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index e1f18cd8c..9fcdcf316 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -136,6 +136,11 @@ android:key="@string/pref_key_addons" android:title="@string/preferences_addons" /> + + ( + settingsFragment.getPreferenceKey(R.string.pref_key_override_amo_collection) + ) + + settingsFragment.setupAmoCollectionOverridePreference(mockk(relaxed = true)) + assertNotNull(preferenceAmoCollectionOverride) + assertFalse(preferenceAmoCollectionOverride!!.isVisible) + + val settings: Settings = mockk(relaxed = true) + every { settings.showSecretDebugMenuThisSession } returns true + settingsFragment.setupAmoCollectionOverridePreference(settings) + assertTrue(preferenceAmoCollectionOverride.isVisible) + } + + @Test + fun `Add-on collection override pref is visible if already configured`() { + val settingsFragment = SettingsFragment() + val activity = Robolectric.buildActivity(FragmentActivity::class.java).create().get() + + activity.supportFragmentManager.beginTransaction() + .add(settingsFragment, "test") + .commitNow() + + val preferenceAmoCollectionOverride = settingsFragment.findPreference( + settingsFragment.getPreferenceKey(R.string.pref_key_override_amo_collection) + ) + + settingsFragment.setupAmoCollectionOverridePreference(mockk(relaxed = true)) + assertNotNull(preferenceAmoCollectionOverride) + assertFalse(preferenceAmoCollectionOverride!!.isVisible) + + val settings: Settings = mockk(relaxed = true) + every { settings.showSecretDebugMenuThisSession } returns false + + every { settings.amoCollectionOverrideConfigured() } returns false + settingsFragment.setupAmoCollectionOverridePreference(settings) + assertFalse(preferenceAmoCollectionOverride.isVisible) + + every { settings.amoCollectionOverrideConfigured() } returns true + settingsFragment.setupAmoCollectionOverridePreference(settings) + assertTrue(preferenceAmoCollectionOverride.isVisible) + } +} diff --git a/app/src/test/java/org/mozilla/fenix/utils/SettingsTest.kt b/app/src/test/java/org/mozilla/fenix/utils/SettingsTest.kt index 456b946e8..4ff101244 100644 --- a/app/src/test/java/org/mozilla/fenix/utils/SettingsTest.kt +++ b/app/src/test/java/org/mozilla/fenix/utils/SettingsTest.kt @@ -576,4 +576,34 @@ class SettingsTest { settings.getSitePermissionsCustomSettingsRules() ) } + + @Test + fun overrideAmoCollection() { + // When just created + // Then + assertEquals("", settings.overrideAmoCollection) + assertFalse(settings.amoCollectionOverrideConfigured()) + + // When + settings.overrideAmoCollection = "testCollection" + + // Then + assertEquals("testCollection", settings.overrideAmoCollection) + assertTrue(settings.amoCollectionOverrideConfigured()) + } + + @Test + fun overrideAmoUser() { + // When just created + // Then + assertEquals("", settings.overrideAmoUser) + assertFalse(settings.amoCollectionOverrideConfigured()) + + // When + settings.overrideAmoUser = "testAmoUser" + + // Then + assertEquals("testAmoUser", settings.overrideAmoUser) + assertTrue(settings.amoCollectionOverrideConfigured()) + } }