diff --git a/app/src/main/java/org/mozilla/fenix/IntentReceiverActivity.kt b/app/src/main/java/org/mozilla/fenix/IntentReceiverActivity.kt index 45e015067..b2cb7ee63 100644 --- a/app/src/main/java/org/mozilla/fenix/IntentReceiverActivity.kt +++ b/app/src/main/java/org/mozilla/fenix/IntentReceiverActivity.kt @@ -12,6 +12,7 @@ import kotlinx.coroutines.MainScope import kotlinx.coroutines.launch import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.customtabs.AuthCustomTabActivity +import org.mozilla.fenix.customtabs.AuthCustomTabActivity.Companion.EXTRA_AUTH_CUSTOM_TAB import org.mozilla.fenix.customtabs.CustomTabActivity import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.metrics @@ -62,7 +63,7 @@ class IntentReceiverActivity : Activity() { private fun setIntentActivity(intent: Intent) { val openToBrowser = when { components.utils.customTabIntentProcessor.matches(intent) -> { - val activityClass = if (intent.hasExtra(getString(R.string.intent_extra_auth))) { + val activityClass = if (intent.hasExtra(EXTRA_AUTH_CUSTOM_TAB)) { AuthCustomTabActivity::class } else { CustomTabActivity::class diff --git a/app/src/main/java/org/mozilla/fenix/customtabs/AuthCustomTabActivity.kt b/app/src/main/java/org/mozilla/fenix/customtabs/AuthCustomTabActivity.kt index d71e92aef..8e74944d4 100644 --- a/app/src/main/java/org/mozilla/fenix/customtabs/AuthCustomTabActivity.kt +++ b/app/src/main/java/org/mozilla/fenix/customtabs/AuthCustomTabActivity.kt @@ -28,4 +28,8 @@ class AuthCustomTabActivity : CustomTabActivity() { val accountManager = components.backgroundServices.accountManager accountManager.register(accountStateObserver, this, true) } + + companion object { + const val EXTRA_AUTH_CUSTOM_TAB = "support.customtabs.extra.AUTH" + } } diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingPrivacyNoticeViewHolder.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingPrivacyNoticeViewHolder.kt index 472e64558..24ab3e656 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingPrivacyNoticeViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingPrivacyNoticeViewHolder.kt @@ -19,7 +19,7 @@ class OnboardingPrivacyNoticeViewHolder(view: View) : RecyclerView.ViewHolder(vi view.description_text.text = view.context.getString(R.string.onboarding_privacy_notice_description, appName) view.read_button.setOnClickListener { - val intent = SupportUtils.createCustomTabIntent(view.context, SupportUtils.PRIVACY_NOTICE_URL) + val intent = SupportUtils.createCustomTabIntent(view.context, SupportUtils.getPrivacyNoticeUrl()) view.context.startActivity(intent) } } 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 54c9faf9a..d6565397b 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt @@ -201,7 +201,7 @@ class SettingsFragment : PreferenceFragmentCompat(), AccountObserver { } resources.getString(pref_key_privacy_link) -> { requireContext().let { context -> - val intent = SupportUtils.createCustomTabIntent(context, SupportUtils.PRIVACY_NOTICE_URL) + val intent = SupportUtils.createCustomTabIntent(context, SupportUtils.getPrivacyNoticeUrl()) startActivity(intent) } } diff --git a/app/src/main/java/org/mozilla/fenix/settings/SupportUtils.kt b/app/src/main/java/org/mozilla/fenix/settings/SupportUtils.kt index 40ab0126f..5222d68ad 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/SupportUtils.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/SupportUtils.kt @@ -6,11 +6,13 @@ package org.mozilla.fenix.settings import android.content.Context import android.content.Intent -import android.content.pm.PackageManager -import android.net.Uri +import androidx.browser.customtabs.CustomTabsIntent +import androidx.core.net.toUri +import mozilla.components.support.ktx.android.content.appVersionName import org.mozilla.fenix.BuildConfig import org.mozilla.fenix.IntentReceiverActivity import org.mozilla.fenix.R +import org.mozilla.fenix.customtabs.AuthCustomTabActivity.Companion.EXTRA_AUTH_CUSTOM_TAB import org.mozilla.fenix.ext.getColorFromAttr import java.io.UnsupportedEncodingException import java.net.URLEncoder @@ -20,12 +22,8 @@ object SupportUtils { const val RATE_APP_URL = "market://details?id=" + BuildConfig.APPLICATION_ID const val MOZILLA_MANIFESTO_URL = "https://www.mozilla.org/en-GB/about/manifesto/" const val FENIX_PLAY_STORE_URL = "https://play.google.com/store/apps/details?id=${BuildConfig.APPLICATION_ID}" - val PRIVACY_NOTICE_URL: String - get() = "https://www.mozilla.org/${getLanguageTag(Locale.getDefault())}/privacy/firefox/" - enum class SumoTopic( - internal val topicStr: String - ) { + enum class SumoTopic(internal val topicStr: String) { HELP("faq-android"), PRIVATE_BROWSING_MYTHS("common-myths-about-private-browsing"), YOUR_RIGHTS("your-rights"), @@ -33,38 +31,46 @@ object SupportUtils { WHATS_NEW("whats-new-firefox-preview") } - fun getSumoURLForTopic(context: Context, topic: SumoTopic): String { + /** + * Gets a support page URL for the corresponding topic. + */ + fun getSumoURLForTopic( + context: Context, + topic: SumoTopic, + locale: Locale = Locale.getDefault() + ): String { val escapedTopic = getEncodedTopicUTF8(topic.topicStr) // Remove the whitespace so a search is not triggered: - val appVersion = getAppVersion(context).replace(" ", "") + val appVersion = context.appVersionName?.replace(" ", "") val osTarget = "Android" - val langTag = getLanguageTag(Locale.getDefault()) + val langTag = getLanguageTag(locale) return "https://support.mozilla.org/1/mobile/$appVersion/$osTarget/$langTag/$escapedTopic" } - // Used when the app version and os are not part of the URL - fun getGenericSumoURLForTopic(topic: SumoTopic): String { + /** + * Gets a support page URL for the corresponding topic. + * Used when the app version and os are not part of the URL. + */ + fun getGenericSumoURLForTopic(topic: SumoTopic, locale: Locale = Locale.getDefault()): String { val escapedTopic = getEncodedTopicUTF8(topic.topicStr) - val langTag = getLanguageTag(Locale.getDefault()) + val langTag = getLanguageTag(locale) return "https://support.mozilla.org/$langTag/kb/$escapedTopic" } - fun createCustomTabIntent(context: Context, url: String) = Intent(Intent.ACTION_VIEW).apply { - putExtra(context.getString(R.string.intent_extra_toolbar_color), context.getColorFromAttr(R.attr.foundation)) - putExtra(context.getString(R.string.intent_extra_session), true) - setClassName(context.applicationContext, IntentReceiverActivity::class.java.name) - data = Uri.parse(url) - setPackage(context.packageName) - } + fun getPrivacyNoticeUrl(locale: Locale = Locale.getDefault()) = + "https://www.mozilla.org/${getLanguageTag(locale)}/privacy/firefox/" - fun createAuthCustomTabIntent(context: Context, url: String) = Intent(Intent.ACTION_VIEW).apply { - putExtra(context.getString(R.string.intent_extra_toolbar_color), context.getColorFromAttr(R.attr.foundation)) - putExtra(context.getString(R.string.intent_extra_session), true) - putExtra(context.getString(R.string.intent_extra_auth), true) - setClassName(context.applicationContext, IntentReceiverActivity::class.java.name) - data = Uri.parse(url) - setPackage(context.packageName) - } + fun createCustomTabIntent(context: Context, url: String): Intent = CustomTabsIntent.Builder() + .setInstantAppsEnabled(false) + .setToolbarColor(context.getColorFromAttr(R.attr.foundation)) + .build() + .intent + .setData(url.toUri()) + .setClassName(context.applicationContext, IntentReceiverActivity::class.java.name) + .setPackage(context.packageName) + + fun createAuthCustomTabIntent(context: Context, url: String): Intent = + createCustomTabIntent(context, url).putExtra(EXTRA_AUTH_CUSTOM_TAB, true) private fun getEncodedTopicUTF8(topic: String): String { try { @@ -74,20 +80,9 @@ object SupportUtils { } } - private fun getAppVersion(context: Context): String { - try { - return context.packageManager.getPackageInfo(context.packageName, 0).versionName - } catch (e: PackageManager.NameNotFoundException) { - // This should be impossible - we should always be able to get information about ourselves: - throw IllegalStateException("Unable find package details for Fenix", e) - } - } - private fun getLanguageTag(locale: Locale): String { val language = locale.language val country = locale.country // Can be an empty string. - return if (country == "") { - language - } else "$language-$country" + return if (country.isEmpty()) language else "$language-$country" } } diff --git a/app/src/test/java/org/mozilla/fenix/settings/SupportUtilsTest.kt b/app/src/test/java/org/mozilla/fenix/settings/SupportUtilsTest.kt new file mode 100644 index 000000000..2bf143eef --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/settings/SupportUtilsTest.kt @@ -0,0 +1,78 @@ +/* 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.settings + +import android.content.Context +import android.content.pm.PackageInfo +import android.content.pm.PackageManager +import io.mockk.every +import io.mockk.mockk +import kotlinx.coroutines.ObsoleteCoroutinesApi +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith +import org.mozilla.fenix.TestApplication +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config +import java.util.Locale + +@ObsoleteCoroutinesApi +@RunWith(RobolectricTestRunner::class) +@Config(application = TestApplication::class) +class SupportUtilsTest { + + @Test + fun getSumoURLForTopic() { + assertEquals( + "https://support.mozilla.org/1/mobile/1.6/Android/en-US/common-myths-about-private-browsing", + SupportUtils.getSumoURLForTopic(mockContext("1.6"), SupportUtils.SumoTopic.PRIVATE_BROWSING_MYTHS, Locale("en", "US")) + ) + assertEquals( + "https://support.mozilla.org/1/mobile/20/Android/fr/tracking-protection-firefox-preview", + SupportUtils.getSumoURLForTopic(mockContext("2 0"), SupportUtils.SumoTopic.TRACKING_PROTECTION, Locale("fr")) + ) + assertEquals( + "https://support.mozilla.org/1/mobile/three/Android/es-CL/whats-new-firefox-preview", + SupportUtils.getSumoURLForTopic(mockContext("three"), SupportUtils.SumoTopic.WHATS_NEW, Locale("es", "CL")) + ) + } + + @Test + fun getGenericSumoURLForTopic() { + assertEquals( + "https://support.mozilla.org/en-GB/kb/faq-android", + SupportUtils.getGenericSumoURLForTopic(SupportUtils.SumoTopic.HELP, Locale("en", "GB")) + ) + assertEquals( + "https://support.mozilla.org/de/kb/your-rights", + SupportUtils.getGenericSumoURLForTopic(SupportUtils.SumoTopic.YOUR_RIGHTS, Locale("de")) + ) + } + + @Test + fun getPrivacyNoticeUrl() { + assertEquals( + "https://www.mozilla.org/en-CA/privacy/firefox/", + SupportUtils.getPrivacyNoticeUrl(Locale("en", "CA")) + ) + assertEquals( + "https://www.mozilla.org/zh/privacy/firefox/", + SupportUtils.getPrivacyNoticeUrl(Locale("zh")) + ) + } + + private fun mockContext(versionName: String): Context { + val context: Context = mockk() + val packageManager: PackageManager = mockk() + val packageInfo = PackageInfo() + + every { context.packageName } returns "org.mozilla.fenix" + every { context.packageManager } returns packageManager + every { packageManager.getPackageInfo("org.mozilla.fenix", 0) } returns packageInfo + packageInfo.versionName = versionName + + return context + } +}