diff --git a/app/src/main/java/org/mozilla/fenix/FenixApplication.kt b/app/src/main/java/org/mozilla/fenix/FenixApplication.kt index f4b25852e..192f4b810 100644 --- a/app/src/main/java/org/mozilla/fenix/FenixApplication.kt +++ b/app/src/main/java/org/mozilla/fenix/FenixApplication.kt @@ -75,6 +75,8 @@ import org.mozilla.fenix.components.metrics.MetricServiceType import org.mozilla.fenix.components.metrics.MozillaProductDetector import org.mozilla.fenix.components.toolbar.ToolbarPosition import org.mozilla.fenix.ext.containsQueryParameters +import org.mozilla.fenix.ext.getCustomGleanServerUrlIfAvailable +import org.mozilla.fenix.ext.setCustomEndpointIfAvailable import org.mozilla.fenix.ext.isCustomEngine import org.mozilla.fenix.ext.isKnownSearchDomain import org.mozilla.fenix.ext.settings @@ -154,14 +156,24 @@ open class FenixApplication : LocaleAwareApplication(), Provider { logger.debug("Initializing Glean (uploadEnabled=$telemetryEnabled})") + // for performance reasons, this is only available in Nightly or Debug builds + val customEndpoint = if (Config.channel.isNightlyOrDebug) { + // for testing, if custom glean server url is set in the secret menu, use it to initialize Glean + getCustomGleanServerUrlIfAvailable(this) + } else { + null + } + + val configuration = Configuration( + channel = BuildConfig.BUILD_TYPE, + httpClient = ConceptFetchHttpUploader( + lazy(LazyThreadSafetyMode.NONE) { components.core.client }, + ), + ) + Glean.initialize( applicationContext = this, - configuration = Configuration( - channel = BuildConfig.BUILD_TYPE, - httpClient = ConceptFetchHttpUploader( - lazy(LazyThreadSafetyMode.NONE) { components.core.client }, - ), - ), + configuration = configuration.setCustomEndpointIfAvailable(customEndpoint), uploadEnabled = telemetryEnabled, buildInfo = GleanBuildInfo.buildInfo, ) diff --git a/app/src/main/java/org/mozilla/fenix/ext/Configuration.kt b/app/src/main/java/org/mozilla/fenix/ext/Configuration.kt new file mode 100644 index 000000000..79bb3e43e --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/ext/Configuration.kt @@ -0,0 +1,31 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.ext + +import android.content.Context +import androidx.preference.PreferenceManager +import mozilla.components.service.glean.config.Configuration +import org.mozilla.fenix.R + +/** + * Get custom Glean server URL if available. + */ +fun getCustomGleanServerUrlIfAvailable(context: Context): String? { + return PreferenceManager.getDefaultSharedPreferences(context).getString( + context.getPreferenceKey(R.string.pref_key_custom_glean_server_url), + null, + ) +} + +/** + * Applies the custom Glean server URL to the Configuration if available. + */ +fun Configuration.setCustomEndpointIfAvailable(serverEndpoint: String?): Configuration { + if (!serverEndpoint.isNullOrEmpty()) { + return copy(serverEndpoint = serverEndpoint) + } + + return this +} diff --git a/app/src/main/java/org/mozilla/fenix/settings/SecretSettingsFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/SecretSettingsFragment.kt index e2bbaaca5..97f3fd4b7 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/SecretSettingsFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/SecretSettingsFragment.kt @@ -5,9 +5,11 @@ package org.mozilla.fenix.settings import android.os.Bundle +import androidx.preference.EditTextPreference import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import androidx.preference.SwitchPreference +import org.mozilla.fenix.Config import org.mozilla.fenix.FeatureFlags import org.mozilla.fenix.R import org.mozilla.fenix.ext.components @@ -53,5 +55,10 @@ class SecretSettingsFragment : PreferenceFragmentCompat() { isChecked = context.settings().showUnifiedSearchFeature onPreferenceChangeListener = SharedPreferenceUpdater() } + + // for performance reasons, this is only available in Nightly or Debug builds + requirePreference(R.string.pref_key_custom_glean_server_url).apply { + isVisible = Config.channel.isNightlyOrDebug + } } } diff --git a/app/src/main/res/values/preference_keys.xml b/app/src/main/res/values/preference_keys.xml index 4b69d5dac..ca7b8a329 100644 --- a/app/src/main/res/values/preference_keys.xml +++ b/app/src/main/res/values/preference_keys.xml @@ -307,4 +307,5 @@ pref_key_nimbus_use_preview pref_key_history_metadata_feature pref_key_show_unified_search + pref_key_custom_glean_server_url diff --git a/app/src/main/res/values/static_strings.xml b/app/src/main/res/values/static_strings.xml index 7df846a95..c11b7cb60 100644 --- a/app/src/main/res/values/static_strings.xml +++ b/app/src/main/res/values/static_strings.xml @@ -44,6 +44,8 @@ Enable Task Continuity Enable Unified Search (requires restart) + + Custom Glean server URL (requires restart) Sync Debug diff --git a/app/src/main/res/xml/secret_settings_preferences.xml b/app/src/main/res/xml/secret_settings_preferences.xml index ff6af4699..da10b2948 100644 --- a/app/src/main/res/xml/secret_settings_preferences.xml +++ b/app/src/main/res/xml/secret_settings_preferences.xml @@ -25,4 +25,10 @@ android:key="@string/pref_key_show_unified_search" android:title="@string/preferences_debug_settings_unified_search" app:iconSpaceReserved="false" /> + diff --git a/app/src/test/java/org/mozilla/fenix/ext/ConfigurationKtTest.kt b/app/src/test/java/org/mozilla/fenix/ext/ConfigurationKtTest.kt new file mode 100644 index 000000000..909d38d72 --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/ext/ConfigurationKtTest.kt @@ -0,0 +1,34 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.ext + +import io.mockk.mockk +import mozilla.components.service.glean.config.Configuration +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotEquals +import org.junit.Test + +class ConfigurationKtTest { + @Test + fun `GIVEN server endpoint is null THEN return the same configuration`() { + val configuration = Configuration(httpClient = mockk()) + + assertEquals(configuration, configuration.setCustomEndpointIfAvailable(null)) + } + + @Test + fun `GIVEN server endpoint is not empty THEN make a copy of configuration with server endpoint`() { + val configuration = Configuration(httpClient = mockk()) + + assertNotEquals(configuration, configuration.setCustomEndpointIfAvailable("test")) + } + + @Test + fun `GIVEN server endpoint is empty THEN return the same configuration`() { + val configuration = Configuration(httpClient = mockk()) + + assertEquals(configuration, configuration.setCustomEndpointIfAvailable("")) + } +}