diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-15 03:34:42 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-15 03:34:42 +0000 |
commit | da4c7e7ed675c3bf405668739c3012d140856109 (patch) | |
tree | cdd868dba063fecba609a1d819de271f0d51b23e /mobile/android/android-components/components/feature/app-links | |
parent | Adding upstream version 125.0.3. (diff) | |
download | firefox-da4c7e7ed675c3bf405668739c3012d140856109.tar.xz firefox-da4c7e7ed675c3bf405668739c3012d140856109.zip |
Adding upstream version 126.0.upstream/126.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'mobile/android/android-components/components/feature/app-links')
129 files changed, 4576 insertions, 0 deletions
diff --git a/mobile/android/android-components/components/feature/app-links/README.md b/mobile/android/android-components/components/feature/app-links/README.md new file mode 100644 index 0000000000..a51afe251e --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/README.md @@ -0,0 +1,40 @@ +# [Android Components](../../../README.md) > Feature > App-Links + +A session component to support opening non-browser apps and `intent://` style URLs. + +## Usage + +From a `BrowserFragment`: +```kotlin +// Start listening to the intercepted and offer to open app banners +AppLinksFeature( + context = context, + sessionManager = sessionManager, + sessionId = customSessionId, + fragmentManager = fragmentManager +) +``` + +From elsewhere in the app: +```kotlin +val redirect = AppLinksUseCases.appLinkRedirect.invoke(redirect) + +if (redirect.isExternalApp()) { + AppLinkUseCases.openAppLink(redirect) +} +``` + +### Setting up the dependency + +Use Gradle to download the library from [maven.mozilla.org](https://maven.mozilla.org/) + ([Setup repository](../../../README.md#maven-repository)): + +```Groovy +implementation "org.mozilla.components:feature-app-links:{latest-version}" +``` + +## License + + 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/ diff --git a/mobile/android/android-components/components/feature/app-links/build.gradle b/mobile/android/android-components/components/feature/app-links/build.gradle new file mode 100644 index 0000000000..71f94f3bc8 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/build.gradle @@ -0,0 +1,60 @@ +/* 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/. */ + +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' +apply plugin: 'com.google.devtools.ksp' + +android { + defaultConfig { + minSdkVersion config.minSdkVersion + compileSdk config.compileSdkVersion + targetSdkVersion config.targetSdkVersion + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + + packagingOptions { + resources { + excludes += ['META-INF/proguard/androidx-annotations.pro'] + } + } + + namespace 'mozilla.components.feature.app.links' +} + +tasks.withType(KotlinCompile).configureEach { + kotlinOptions.freeCompilerArgs += "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi" +} + +dependencies { + implementation project(':browser-state') + implementation project(':concept-engine') + implementation project(':support-base') + implementation project(':support-ktx') + implementation project(':support-utils') + implementation project(':feature-session') + implementation project(':ui-widgets') + + implementation ComponentsDependencies.kotlin_coroutines + + testImplementation project(':support-test') + testImplementation project(':support-test-libstate') + testImplementation ComponentsDependencies.androidx_test_core + testImplementation ComponentsDependencies.androidx_test_junit + testImplementation ComponentsDependencies.testing_coroutines + testImplementation ComponentsDependencies.testing_robolectric +} + +apply from: '../../../android-lint.gradle' +apply from: '../../../publish.gradle' +ext.configurePublish(config.componentsGroupId, archivesBaseName, project.ext.description) diff --git a/mobile/android/android-components/components/feature/app-links/proguard-rules.pro b/mobile/android/android-components/components/feature/app-links/proguard-rules.pro new file mode 100644 index 0000000000..f1b424510d --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/mobile/android/android-components/components/feature/app-links/src/main/AndroidManifest.xml b/mobile/android/android-components/components/feature/app-links/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..e16cda1d34 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ +<!-- 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/. --> +<manifest /> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/java/mozilla/components/feature/app/links/AppLinkRedirect.kt b/mobile/android/android-components/components/feature/app-links/src/main/java/mozilla/components/feature/app/links/AppLinkRedirect.kt new file mode 100644 index 0000000000..e0f6f7f76b --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/java/mozilla/components/feature/app/links/AppLinkRedirect.kt @@ -0,0 +1,41 @@ +/* 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 mozilla.components.feature.app.links + +import android.content.Intent + +/** + * Data class for the external Intent or fallback URL a given URL encodes for. + */ +data class AppLinkRedirect( + val appIntent: Intent?, + val fallbackUrl: String?, + val marketplaceIntent: Intent?, +) { + /** + * If there is a third-party app intent. + */ + fun hasExternalApp() = appIntent != null + + /** + * If there is a fallback URL (should the intent fails). + */ + fun hasFallback() = fallbackUrl != null + + /** + * If there is a marketplace intent (should the external app is not installed). + */ + fun hasMarketplaceIntent() = marketplaceIntent != null + + /** + * If the app link is a redirect (to an app or URL). + */ + fun isRedirect() = hasExternalApp() || hasFallback() || hasMarketplaceIntent() + + /** + * Is the app link one that can be installed from a store. + */ + fun isInstallable() = appIntent?.data?.scheme == "market" +} diff --git a/mobile/android/android-components/components/feature/app-links/src/main/java/mozilla/components/feature/app/links/AppLinksFeature.kt b/mobile/android/android-components/components/feature/app-links/src/main/java/mozilla/components/feature/app/links/AppLinksFeature.kt new file mode 100644 index 0000000000..3d02265a3a --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/java/mozilla/components/feature/app/links/AppLinksFeature.kt @@ -0,0 +1,184 @@ +/* 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 mozilla.components.feature.app.links + +import android.content.Context +import android.content.Intent +import android.net.Uri +import androidx.annotation.VisibleForTesting +import androidx.fragment.app.FragmentManager +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.cancel +import kotlinx.coroutines.flow.distinctUntilChangedBy +import kotlinx.coroutines.flow.mapNotNull +import mozilla.components.browser.state.action.ContentAction +import mozilla.components.browser.state.selector.findTabOrCustomTabOrSelectedTab +import mozilla.components.browser.state.state.SessionState +import mozilla.components.browser.state.store.BrowserStore +import mozilla.components.concept.engine.EngineSession +import mozilla.components.concept.engine.EngineSession.LoadUrlFlags.Companion.EXTERNAL +import mozilla.components.concept.engine.EngineSession.LoadUrlFlags.Companion.LOAD_FLAGS_BYPASS_LOAD_URI_DELEGATE +import mozilla.components.feature.app.links.AppLinksUseCases.Companion.ENGINE_SUPPORTED_SCHEMES +import mozilla.components.feature.app.links.RedirectDialogFragment.Companion.FRAGMENT_TAG +import mozilla.components.feature.session.SessionUseCases +import mozilla.components.lib.state.ext.flowScoped +import mozilla.components.support.base.feature.LifecycleAwareFeature +import mozilla.components.support.ktx.android.content.appName + +/** + * This feature implements observer for handling redirects to external apps. The users are asked to + * confirm their intention before leaving the app if in private session. These include the Android + * Intents, custom schemes and support for [Intent.CATEGORY_BROWSABLE] `http(s)` URLs. + * + * It requires: a [Context], and a [FragmentManager]. + * + * @param context Context the feature is associated with. + * @param store Reference to the application's [BrowserStore]. + * @param sessionId The session ID to observe. + * @param fragmentManager FragmentManager for interacting with fragments. + * @param dialog The dialog for redirect. + * @param launchInApp If {true} then launch app links in third party app(s). Default to false because + * of security concerns. + * @param useCases These use cases allow for the detection of, and opening of links that other apps + * have registered to open. + * @param failedToLaunchAction Action to perform when failing to launch in third party app. + * @param loadUrlUseCase Used to load URL if user decides not to launch in third party app. + **/ +class AppLinksFeature( + private val context: Context, + private val store: BrowserStore, + private val sessionId: String? = null, + private val fragmentManager: FragmentManager? = null, + private val dialog: RedirectDialogFragment? = null, + private val launchInApp: () -> Boolean = { false }, + private val useCases: AppLinksUseCases = AppLinksUseCases(context, launchInApp), + private val failedToLaunchAction: (fallbackUrl: String?) -> Unit = {}, + private val loadUrlUseCase: SessionUseCases.DefaultLoadUrlUseCase? = null, + private val engineSupportedSchemes: Set<String> = ENGINE_SUPPORTED_SCHEMES, + private val shouldPrompt: () -> Boolean = { true }, +) : LifecycleAwareFeature { + + private var scope: CoroutineScope? = null + + /** + * Starts observing app links on the selected session. + */ + override fun start() { + scope = store.flowScoped { flow -> + flow.mapNotNull { state -> state.findTabOrCustomTabOrSelectedTab(sessionId) } + .distinctUntilChangedBy { + it.content.appIntent + } + .collect { tab -> + tab.content.appIntent?.let { + handleAppIntent(tab, it.url, it.appIntent) + store.dispatch(ContentAction.ConsumeAppIntentAction(tab.id)) + } + } + } + + findPreviousDialogFragment()?.let { + fragmentManager?.beginTransaction()?.remove(it)?.commit() + } + } + + override fun stop() { + scope?.cancel() + } + + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + internal fun handleAppIntent(tab: SessionState, url: String, appIntent: Intent?) { + if (appIntent == null) { + return + } + + val doNotOpenApp = { + AppLinksInterceptor.addUserDoNotIntercept(url, appIntent) + + loadUrlIfSchemeSupported(tab, url) + } + + val doOpenApp = { + useCases.openAppLink( + appIntent, + failedToLaunchAction = failedToLaunchAction, + ) + } + + @Suppress("ComplexCondition") + if (isSameCallerAndApp(tab, appIntent) || (!tab.content.private && !shouldPrompt()) || + fragmentManager == null + ) { + doOpenApp() + return + } + + val dialog = getOrCreateDialog(tab.content.private, url) + dialog.onConfirmRedirect = doOpenApp + dialog.onCancelRedirect = doNotOpenApp + + if (!isAlreadyADialogCreated()) { + dialog.showNow(fragmentManager, FRAGMENT_TAG) + } + } + + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + internal fun getOrCreateDialog(isPrivate: Boolean, url: String): RedirectDialogFragment { + if (dialog != null) { + return dialog + } + + val message = context.getString( + R.string.mozac_feature_applinks_normal_confirm_dialog_message, + context.appName, + ) + + return SimpleRedirectDialogFragment.newInstance( + dialogTitleText = if (isPrivate) { + R.string.mozac_feature_applinks_confirm_dialog_title + } else { + R.string.mozac_feature_applinks_normal_confirm_dialog_title + }, + dialogMessageString = if (isPrivate) { + url + } else { + message + }, + positiveButtonText = R.string.mozac_feature_applinks_confirm_dialog_confirm, + negativeButtonText = R.string.mozac_feature_applinks_confirm_dialog_deny, + ) + } + + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + internal fun loadUrlIfSchemeSupported(tab: SessionState, url: String) { + val schemeSupported = engineSupportedSchemes.contains(Uri.parse(url).scheme) + if (schemeSupported) { + loadUrlUseCase?.invoke( + url = url, + sessionId = tab.id, + flags = EngineSession.LoadUrlFlags.select(EXTERNAL, LOAD_FLAGS_BYPASS_LOAD_URI_DELEGATE), + ) + } + } + + private fun isAlreadyADialogCreated(): Boolean { + return findPreviousDialogFragment() != null + } + + private fun findPreviousDialogFragment(): RedirectDialogFragment? { + return fragmentManager?.findFragmentByTag(FRAGMENT_TAG) as? RedirectDialogFragment + } + + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + internal fun isSameCallerAndApp(tab: SessionState, appIntent: Intent): Boolean { + return (tab.source as? SessionState.Source.External)?.let { externalSource -> + when (externalSource.caller?.packageId) { + null -> false + appIntent.component?.packageName -> true + else -> false + } + } ?: false + } +} diff --git a/mobile/android/android-components/components/feature/app-links/src/main/java/mozilla/components/feature/app/links/AppLinksInterceptor.kt b/mobile/android/android-components/components/feature/app-links/src/main/java/mozilla/components/feature/app/links/AppLinksInterceptor.kt new file mode 100644 index 0000000000..66880a5935 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/java/mozilla/components/feature/app/links/AppLinksInterceptor.kt @@ -0,0 +1,237 @@ +/* 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 mozilla.components.feature.app.links + +import android.annotation.SuppressLint +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.os.SystemClock +import androidx.annotation.VisibleForTesting +import mozilla.components.concept.engine.EngineSession +import mozilla.components.concept.engine.request.RequestInterceptor +import mozilla.components.feature.app.links.AppLinksUseCases.Companion.ALWAYS_DENY_SCHEMES +import mozilla.components.feature.app.links.AppLinksUseCases.Companion.ENGINE_SUPPORTED_SCHEMES +import mozilla.components.support.ktx.android.net.isHttpOrHttps +import mozilla.components.support.ktx.kotlin.tryGetHostFromUrl + +private const val WWW = "www." +private const val M = "m." +private const val MOBILE = "mobile." +private const val MAPS = "maps." + +/** + * This feature implements use cases for detecting and handling redirects to external apps. The user + * is asked to confirm her intention before leaving the app. These include the Android Intents, + * custom schemes and support for [Intent.CATEGORY_BROWSABLE] `http(s)` URLs. + * + * In the case of Android Intents that are not installed, and with no fallback, the user is prompted + * to search the installed market place. + * + * It provides use cases to detect and open links openable in third party non-browser apps. + * + * It requires: a [Context]. + * + * A [Boolean] flag is provided at construction to allow the feature and use cases to be landed without + * adjoining UI. The UI will be activated in https://github.com/mozilla-mobile/android-components/issues/2974 + * and https://github.com/mozilla-mobile/android-components/issues/2975. + * + * @param context Context the feature is associated with. + * @param interceptLinkClicks If {true} then intercept link clicks. + * @param engineSupportedSchemes List of schemes that the engine supports. + * @param alwaysDeniedSchemes List of schemes that will never be opened in a third-party app even if + * [interceptLinkClicks] is `true`. + * @param launchInApp If {true} then launch app links in third party app(s). Default to false because + * of security concerns. + * @param useCases These use cases allow for the detection of, and opening of links that other apps + * have registered to open. + * @param launchFromInterceptor If {true} then the interceptor will launch the link in third-party apps if available. + */ +class AppLinksInterceptor( + private val context: Context, + private val interceptLinkClicks: Boolean = false, + private val engineSupportedSchemes: Set<String> = ENGINE_SUPPORTED_SCHEMES, + private val alwaysDeniedSchemes: Set<String> = ALWAYS_DENY_SCHEMES, + private var launchInApp: () -> Boolean = { false }, + private val useCases: AppLinksUseCases = AppLinksUseCases( + context, + launchInApp, + alwaysDeniedSchemes = alwaysDeniedSchemes, + ), + private val launchFromInterceptor: Boolean = false, +) : RequestInterceptor { + + /** + * Update launchInApp for this instance of AppLinksInterceptor + * @param launchInApp the new value of launchInApp + */ + fun updateLaunchInApp(launchInApp: () -> Boolean) { + this.launchInApp = launchInApp + useCases.updateLaunchInApp(launchInApp) + } + + @Suppress("ComplexMethod", "ReturnCount") + override fun onLoadRequest( + engineSession: EngineSession, + uri: String, + lastUri: String?, + hasUserGesture: Boolean, + isSameDomain: Boolean, + isRedirect: Boolean, + isDirectNavigation: Boolean, + isSubframeRequest: Boolean, + ): RequestInterceptor.InterceptionResponse? { + val encodedUri = Uri.parse(uri) + val uriScheme = encodedUri.scheme + val engineSupportsScheme = engineSupportedSchemes.contains(uriScheme) + val isAllowedRedirect = (isRedirect && !isSubframeRequest) + + val doNotIntercept = when { + uriScheme == null -> true + // A subframe request not triggered by the user should not go to an external app. + (!hasUserGesture && isSubframeRequest) -> true + // If request not from an user gesture, allowed redirect and direct navigation + // or if we're already on the site then let's not go to an external app. + ( + (!hasUserGesture && !isAllowedRedirect && !isDirectNavigation) || + isSameDomain(lastUri, uri) + ) && engineSupportsScheme -> true + // If scheme not in safelist then follow user preference + (!interceptLinkClicks || !launchInApp()) && engineSupportsScheme -> true + // Never go to an external app when scheme is in blocklist + alwaysDeniedSchemes.contains(uriScheme) -> true + else -> false + } + + if (doNotIntercept) { + return null + } + + val redirect = useCases.interceptedAppLinkRedirect(uri) + val result = handleRedirect(redirect, uri, engineSupportedSchemes.contains(uriScheme)) + + if (redirect.hasExternalApp()) { + val packageName = redirect.appIntent?.component?.packageName + + if ( + lastApplinksPackageWithTimestamp.first == packageName && lastApplinksPackageWithTimestamp.second + + APP_LINKS_DO_NOT_INTERCEPT_INTERVAL > SystemClock.elapsedRealtime() + ) { + return null + } + + lastApplinksPackageWithTimestamp = Pair(packageName, SystemClock.elapsedRealtime()) + } + + if (redirect.isRedirect()) { + if (launchFromInterceptor && result is RequestInterceptor.InterceptionResponse.AppIntent) { + result.appIntent.flags = result.appIntent.flags or Intent.FLAG_ACTIVITY_NEW_TASK + useCases.openAppLink(result.appIntent) + } + + return result + } + + return null + } + + @SuppressWarnings("ReturnCount") + @SuppressLint("MissingPermission") + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + internal fun handleRedirect( + redirect: AppLinkRedirect, + uri: String, + schemeSupported: Boolean, + ): RequestInterceptor.InterceptionResponse? { + if (!launchInApp() || inUserDoNotIntercept(uri, redirect.appIntent)) { + redirect.fallbackUrl?.let { + return RequestInterceptor.InterceptionResponse.Url(it) + } + } + + if (schemeSupported && inUserDoNotIntercept(uri, redirect.appIntent)) { + return null + } + + if (!redirect.hasExternalApp()) { + redirect.marketplaceIntent?.let { + return RequestInterceptor.InterceptionResponse.AppIntent(it, uri) + } + + redirect.fallbackUrl?.let { + return RequestInterceptor.InterceptionResponse.Url(it) + } + + return null + } + + redirect.appIntent?.let { + return RequestInterceptor.InterceptionResponse.AppIntent(it, uri) + } + + return null + } + + // Determines if the transition between the two URLs is related. If the two URLs + // are from the same website then the app links interceptor will not try to find an application to open it. + @VisibleForTesting + internal fun isSameDomain(url1: String?, url2: String?): Boolean { + return stripCommonSubDomains(url1?.tryGetHostFromUrl()) == stripCommonSubDomains(url2?.tryGetHostFromUrl()) + } + + // Remove subdomains that are ignored when determining if two URLs are from the same website. + private fun stripCommonSubDomains(url: String?): String? { + return when { + url == null -> return null + url.startsWith(WWW) -> url.replaceFirst(WWW, "") + url.startsWith(M) -> url.replaceFirst(M, "") + url.startsWith(MOBILE) -> url.replaceFirst(MOBILE, "") + url.startsWith(MAPS) -> url.replaceFirst(MAPS, "") + else -> url + } + } + + companion object { + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + internal var userDoNotInterceptCache: MutableMap<Int, Long> = mutableMapOf() + + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + internal var lastApplinksPackageWithTimestamp: Pair<String?, Long> = Pair(null, 0L) + + @VisibleForTesting + internal fun getCacheKey(url: String, appIntent: Intent?): Int? { + return Uri.parse(url)?.let { uri -> + when { + appIntent?.component?.packageName != null -> appIntent.component?.packageName + !uri.isHttpOrHttps -> uri.scheme + else -> uri.host // worst case we do not prompt again on this host + }.hashCode() + } + } + + @VisibleForTesting + internal fun inUserDoNotIntercept(url: String, appIntent: Intent?): Boolean { + val cacheKey = getCacheKey(url, appIntent) + val cacheTimeStamp = userDoNotInterceptCache[cacheKey] + val currentTimeStamp = SystemClock.elapsedRealtime() + + return cacheTimeStamp != null && + currentTimeStamp <= (cacheTimeStamp + APP_LINKS_DO_NOT_OPEN_CACHE_INTERVAL) + } + + internal fun addUserDoNotIntercept(url: String, appIntent: Intent?) { + val cacheKey = getCacheKey(url, appIntent) + cacheKey?.let { + userDoNotInterceptCache[it] = SystemClock.elapsedRealtime() + } + } + + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + internal const val APP_LINKS_DO_NOT_OPEN_CACHE_INTERVAL = 60 * 60 * 1000L // 1 hour + + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + internal const val APP_LINKS_DO_NOT_INTERCEPT_INTERVAL = 2000L // 2 second + } +} diff --git a/mobile/android/android-components/components/feature/app-links/src/main/java/mozilla/components/feature/app/links/AppLinksUseCases.kt b/mobile/android/android-components/components/feature/app-links/src/main/java/mozilla/components/feature/app/links/AppLinksUseCases.kt new file mode 100644 index 0000000000..8d45426909 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/java/mozilla/components/feature/app/links/AppLinksUseCases.kt @@ -0,0 +1,318 @@ +/* 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 mozilla.components.feature.app.links + +import android.content.ActivityNotFoundException +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.content.pm.PackageManager +import android.content.pm.ResolveInfo +import android.net.Uri +import android.os.SystemClock +import android.provider.Browser.EXTRA_APPLICATION_ID +import androidx.annotation.VisibleForTesting +import mozilla.components.support.base.log.logger.Logger +import mozilla.components.support.ktx.android.content.pm.isPackageInstalled +import mozilla.components.support.ktx.android.net.isHttpOrHttps +import mozilla.components.support.utils.Browsers +import mozilla.components.support.utils.BrowsersCache +import mozilla.components.support.utils.ext.queryIntentActivitiesCompat +import mozilla.components.support.utils.ext.resolveActivityCompat +import java.lang.Exception +import java.lang.NullPointerException +import java.lang.NumberFormatException +import java.net.URISyntaxException + +@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) +internal const val EXTRA_BROWSER_FALLBACK_URL = "browser_fallback_url" +private const val MARKET_INTENT_URI_PACKAGE_PREFIX = "market://details?id=" + +@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) +internal const val APP_LINKS_CACHE_INTERVAL = 30 * 1000L // 30 seconds +private const val ANDROID_RESOLVER_PACKAGE_NAME = "android" + +/** + * These use cases allow for the detection of, and opening of links that other apps have registered + * an [IntentFilter]s to open. + * + * Care is taken to: + * * resolve [intent://] links, including [S.browser_fallback_url] + * * provide a fallback to the installed marketplace app (e.g. on Google Android, the Play Store). + * * open HTTP(S) links with an external app. + * + * Since browsers are able to open HTTPS pages, existing browser apps are excluded from the list of + * apps that trigger a redirect to an external app. + * + * @param context Context the feature is associated with. + * @param launchInApp If {true} then launch app links in third party app(s). Default to false because + * of security concerns. + * @param alwaysDeniedSchemes List of schemes that will never be opened in a third-party app. + * @param installedBrowsers List of all installed browsers on the device. + */ +class AppLinksUseCases( + private val context: Context, + private var launchInApp: () -> Boolean = { false }, + private val alwaysDeniedSchemes: Set<String> = ALWAYS_DENY_SCHEMES, + private val installedBrowsers: Browsers = BrowsersCache.all(context), +) { + @Suppress( + "QueryPermissionsNeeded", // We expect our browsers to have the QUERY_ALL_PACKAGES permission + "TooGenericExceptionCaught", + ) + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + internal fun findActivities(intent: Intent): List<ResolveInfo> { + return try { + context.packageManager + .queryIntentActivitiesCompat(intent, PackageManager.GET_RESOLVED_FILTER) + } catch (e: RuntimeException) { + Logger("AppLinksUseCases").error("failed to query activities", e) + emptyList() + } + } + + /** + * Update launchInApp for this instance of AppLinksUseCases + * @param launchInApp the new value of launchInApp + */ + fun updateLaunchInApp(launchInApp: () -> Boolean) { + this.launchInApp = launchInApp + } + + private fun findDefaultActivity(intent: Intent): ResolveInfo? { + return context.packageManager.resolveActivityCompat(intent, PackageManager.MATCH_DEFAULT_ONLY) + } + + /** + * Parse a URL and check if it can be handled by an app elsewhere on the Android device. + * If that app is not available, then a market place intent is also provided. + * + * It will also provide a fallback. + * + * @param includeHttpAppLinks If {true} then test URLs that start with {http} and {https}. + * @param includeInstallAppFallback If {true} then offer an app-link to the installed market app + * if no web fallback is available. + */ + @Suppress("ComplexMethod") + inner class GetAppLinkRedirect internal constructor( + private val includeHttpAppLinks: Boolean = false, + private val includeInstallAppFallback: Boolean = false, + ) { + operator fun invoke(url: String): AppLinkRedirect { + val urlHash = (url + includeHttpAppLinks + includeHttpAppLinks).hashCode() + val currentTimeStamp = SystemClock.elapsedRealtime() + // since redirectCache is mutable, get the latest + val cache = redirectCache + if (cache != null && urlHash == cache.cachedUrlHash && + currentTimeStamp <= cache.cacheTimeStamp + APP_LINKS_CACHE_INTERVAL + ) { + return cache.cachedAppLinkRedirect + } + + val redirectData = createBrowsableIntents(url) + val isAppIntentHttpOrHttps = redirectData.appIntent?.data?.isHttpOrHttps ?: false + val isEngineSupportedScheme = ENGINE_SUPPORTED_SCHEMES.contains(Uri.parse(url).scheme) + val isBrowserRedirect = redirectData.resolveInfo?.activityInfo?.packageName?.let { packageName -> + installedBrowsers.isInstalled(packageName) + } ?: false + + val fallbackUrl = when { + redirectData.fallbackIntent?.data?.isHttpOrHttps == true -> + redirectData.fallbackIntent.dataString + else -> null + } + + val appIntent = when { + redirectData.resolveInfo == null -> null + isBrowserRedirect && isEngineSupportedScheme -> null + includeHttpAppLinks && isAppIntentHttpOrHttps -> redirectData.appIntent + !launchInApp() && (isEngineSupportedScheme || fallbackUrl != null) -> null + else -> redirectData.appIntent + } + + // no need to check marketplace intent since it is only set if a package is set in the intent + val appLinkRedirect = AppLinkRedirect(appIntent, fallbackUrl, redirectData.marketplaceIntent) + redirectCache = AppLinkRedirectCache(currentTimeStamp, urlHash, appLinkRedirect) + return appLinkRedirect + } + + private fun createBrowsableIntents(url: String): RedirectData { + val intent = safeParseUri(url, Intent.URI_INTENT_SCHEME) + val fallbackIntent = intent?.getStringExtra(EXTRA_BROWSER_FALLBACK_URL)?.let { + safeParseUri(it, 0) + } + + val marketplaceIntent = intent?.`package`?.let { + if (includeInstallAppFallback && + !context.packageManager.isPackageInstalled(it) + ) { + safeParseUri(MARKET_INTENT_URI_PACKAGE_PREFIX + it, 0) + } else { + null + } + } + + if (marketplaceIntent != null) { + marketplaceIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + } + + val appIntent = when { + intent?.data == null -> null + alwaysDeniedSchemes.contains(intent.data?.scheme) -> null + else -> intent + } + + appIntent?.let { + it.addCategory(Intent.CATEGORY_BROWSABLE) + it.component = null + it.flags = Intent.FLAG_ACTIVITY_NEW_TASK + it.selector?.addCategory(Intent.CATEGORY_BROWSABLE) + it.selector?.component = null + it.putExtra(EXTRA_APPLICATION_ID, context.packageName) + } + + val resolveInfo = appIntent?.let { + findDefaultActivity(it) + }?.let { resolveInfo -> + when (resolveInfo.activityInfo?.packageName) { + // don't self target when it is an app link + context.packageName -> null + // no default app found but Android resolver shows there are multiple applications + // that can open this app link + ANDROID_RESOLVER_PACKAGE_NAME, null -> { + findActivities(appIntent).firstOrNull { + it.filter != null + } + } + // use default app + else -> { + appIntent.component = + ComponentName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name) + resolveInfo + } + } + } + + return RedirectData(appIntent, fallbackIntent, marketplaceIntent, resolveInfo) + } + } + + /** + * Open an external app with the redirect created by the [GetAppLinkRedirect]. + * + * This does not do any additional UI other than the chooser that Android may provide the user. + */ + @Suppress("TooGenericExceptionCaught") + inner class OpenAppLinkRedirect internal constructor( + private val context: Context, + ) { + /** + * Tries to open an external app for the provided [appIntent]. Invokes [failedToLaunchAction] + * in case an exception is thrown opening the app. + * + * @param appIntent the [Intent] to open the external app for. + * @param launchInNewTask whether or not the app should be launched in a new task. + * @param failedToLaunchAction callback invoked in case opening the external app fails. + */ + operator fun invoke( + appIntent: Intent?, + launchInNewTask: Boolean = true, + failedToLaunchAction: (fallbackUrl: String?) -> Unit = {}, + ) { + appIntent?.let { + try { + val scheme = appIntent.data?.scheme + if (scheme != null && alwaysDeniedSchemes.contains(scheme)) { + return + } + + if (launchInNewTask) { + it.flags = it.flags or Intent.FLAG_ACTIVITY_NEW_TASK + } + context.startActivity(it) + } catch (e: Exception) { + when (e) { + is ActivityNotFoundException, is SecurityException, is NullPointerException -> { + failedToLaunchAction(it.getStringExtra(EXTRA_BROWSER_FALLBACK_URL)) + Logger.error("failed to start third party app activity", e) + } + else -> throw e + } + } + } + } + } + + @VisibleForTesting + internal fun safeParseUri(uri: String, flags: Int): Intent? { + return try { + val intent = Intent.parseUri(uri, flags) + if (context.packageName != null && context.packageName == intent?.`package`) { + // Ignore intents that would open in the browser itself + null + } else { + intent + } + } catch (e: URISyntaxException) { + Logger.error("failed to parse URI", e) + null + } catch (e: NumberFormatException) { + Logger.error("failed to parse URI", e) + null + } + } + + val openAppLink: OpenAppLinkRedirect by lazy { OpenAppLinkRedirect(context) } + val interceptedAppLinkRedirect: GetAppLinkRedirect by lazy { + GetAppLinkRedirect( + includeHttpAppLinks = false, + includeInstallAppFallback = true, + ) + } + val appLinkRedirect: GetAppLinkRedirect by lazy { + GetAppLinkRedirect( + includeHttpAppLinks = true, + includeInstallAppFallback = false, + ) + } + val appLinkRedirectIncludeInstall: GetAppLinkRedirect by lazy { + GetAppLinkRedirect( + includeHttpAppLinks = true, + includeInstallAppFallback = true, + ) + } + private data class RedirectData( + val appIntent: Intent? = null, + val fallbackIntent: Intent? = null, + val marketplaceIntent: Intent? = null, + val resolveInfo: ResolveInfo? = null, + ) + + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + internal data class AppLinkRedirectCache( + var cacheTimeStamp: Long, + var cachedUrlHash: Int, + var cachedAppLinkRedirect: AppLinkRedirect, + ) + + companion object { + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + internal var redirectCache: AppLinkRedirectCache? = null + + @VisibleForTesting + internal fun clearRedirectCache() { + redirectCache = null + } + + // list of scheme from https://searchfox.org/mozilla-central/source/netwerk/build/components.conf + internal val ENGINE_SUPPORTED_SCHEMES: Set<String> = setOf( + "about", "data", "file", "ftp", "http", + "https", "moz-extension", "moz-safe-about", "resource", "view-source", "ws", "wss", "blob", + ) + + internal val ALWAYS_DENY_SCHEMES: Set<String> = setOf("jar", "file", "javascript", "data", "about", "content") + } +} diff --git a/mobile/android/android-components/components/feature/app-links/src/main/java/mozilla/components/feature/app/links/RedirectDialogFragment.kt b/mobile/android/android-components/components/feature/app-links/src/main/java/mozilla/components/feature/app/links/RedirectDialogFragment.kt new file mode 100644 index 0000000000..70a3ddab83 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/java/mozilla/components/feature/app/links/RedirectDialogFragment.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 mozilla.components.feature.app.links + +import androidx.fragment.app.DialogFragment + +/** + * This is a general representation of a dialog meant to be used in collaboration with [AppLinksInterceptor] + * to show a dialog before an external link is opened. + * If [SimpleRedirectDialogFragment] is not flexible enough for your use case you should inherit for this class. + * Be mindful to call [onConfirmRedirect] when you want to open the linked app. + */ +abstract class RedirectDialogFragment : DialogFragment() { + + /** + * A callback to trigger a download, call it when you are ready to open the linked app. For instance, + * a valid use case can be in confirmation dialog, after the positive button is clicked, + * this callback must be called. + */ + var onConfirmRedirect: () -> Unit = {} + + /** + * A callback to trigger when user dismisses the dialog. + * For instance, a valid use case can be in confirmation dialog, after the negative button is clicked, + * this callback must be called. + */ + var onCancelRedirect: () -> Unit? = {} + + companion object { + const val FRAGMENT_TAG = "SHOULD_OPEN_APP_LINK_PROMPT_DIALOG" + } +} diff --git a/mobile/android/android-components/components/feature/app-links/src/main/java/mozilla/components/feature/app/links/SimpleRedirectDialogFragment.kt b/mobile/android/android-components/components/feature/app-links/src/main/java/mozilla/components/feature/app/links/SimpleRedirectDialogFragment.kt new file mode 100644 index 0000000000..793c8c2ef6 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/java/mozilla/components/feature/app/links/SimpleRedirectDialogFragment.kt @@ -0,0 +1,109 @@ +/* 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 mozilla.components.feature.app.links + +import android.app.Dialog +import android.content.Context +import android.os.Bundle +import androidx.annotation.StringRes +import androidx.annotation.StyleRes +import androidx.annotation.VisibleForTesting +import androidx.appcompat.app.AlertDialog +import mozilla.components.ui.widgets.withCenterAlignedButtons + +/** + * This is the default implementation of the [RedirectDialogFragment]. + * + * It provides an [AlertDialog] giving the user the choice to allow or deny the opening of a + * third party app. + * + * Intents passed are guaranteed to be openable by a non-browser app. + */ +class SimpleRedirectDialogFragment : RedirectDialogFragment() { + + @VisibleForTesting + internal var testingContext: Context? = null + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + fun getBuilder(themeID: Int): AlertDialog.Builder { + val context = testingContext ?: requireContext() + return if (themeID == 0) AlertDialog.Builder(context) else AlertDialog.Builder(context, themeID) + } + + return with(requireBundle()) { + val dialogTitleText = getInt(KEY_TITLE_TEXT, R.string.mozac_feature_applinks_normal_confirm_dialog_title) + val dialogMessageString = getString(KEY_MESSAGE_STRING, "") + val positiveButtonText = getInt(KEY_POSITIVE_TEXT, R.string.mozac_feature_applinks_confirm_dialog_confirm) + val negativeButtonText = getInt(KEY_NEGATIVE_TEXT, R.string.mozac_feature_applinks_confirm_dialog_deny) + val themeResId = getInt(KEY_THEME_ID, 0) + val cancelable = getBoolean(KEY_CANCELABLE, false) + + getBuilder(themeResId) + .setTitle(dialogTitleText) + .setMessage(dialogMessageString) + .setPositiveButton(positiveButtonText) { _, _ -> + onConfirmRedirect() + } + .setNegativeButton(negativeButtonText) { _, _ -> + onCancelRedirect() + } + .setCancelable(cancelable) + .create() + .withCenterAlignedButtons() + } + } + + companion object { + /** + * A builder method for creating a [SimpleRedirectDialogFragment] + */ + fun newInstance( + @StringRes dialogTitleText: Int = R.string.mozac_feature_applinks_normal_confirm_dialog_title, + dialogMessageString: String = "", + @StringRes positiveButtonText: Int = R.string.mozac_feature_applinks_confirm_dialog_confirm, + @StringRes negativeButtonText: Int = R.string.mozac_feature_applinks_confirm_dialog_deny, + @StyleRes themeResId: Int = 0, + cancelable: Boolean = false, + ): RedirectDialogFragment { + val fragment = SimpleRedirectDialogFragment() + val arguments = fragment.arguments ?: Bundle() + + with(arguments) { + putInt(KEY_TITLE_TEXT, dialogTitleText) + + putString(KEY_MESSAGE_STRING, dialogMessageString) + + putInt(KEY_POSITIVE_TEXT, positiveButtonText) + + putInt(KEY_NEGATIVE_TEXT, negativeButtonText) + + putInt(KEY_THEME_ID, themeResId) + + putBoolean(KEY_CANCELABLE, cancelable) + } + + fragment.arguments = arguments + fragment.isCancelable = false + + return fragment + } + + const val KEY_POSITIVE_TEXT = "KEY_POSITIVE_TEXT" + + const val KEY_NEGATIVE_TEXT = "KEY_NEGATIVE_TEXT" + + const val KEY_TITLE_TEXT = "KEY_TITLE_TEXT" + + const val KEY_MESSAGE_STRING = "KEY_MESSAGE_STRING" + + const val KEY_THEME_ID = "KEY_THEME_ID" + + const val KEY_CANCELABLE = "KEY_CANCELABLE" + } + + private fun requireBundle(): Bundle { + return arguments ?: throw IllegalStateException("Fragment $this arguments is not set.") + } +} diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-am/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-am/strings.xml new file mode 100644 index 0000000000..ff69fd0fc0 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-am/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">ክፈት በ…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">በመተግበሪያ ውስጥ ክፈት? እንቅስቃሴዎ ከአሁን በኋላ ግላዊ ላይሆን ይችላል።</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">በሌላ መተግበሪያ ውስጥ ክፈት</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">ይህን ይዘት ለማየት %sን መተው ይፈልጋሉ?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">ክፈት</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">ተወው</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-an/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-an/strings.xml new file mode 100644 index 0000000000..780abd5674 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-an/strings.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Ubrir en…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Ubrir en aplicación? Ye posible que la tuya actividat deixe d’estar privada.</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Ubrir</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Cancelar</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-ar/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-ar/strings.xml new file mode 100644 index 0000000000..61c7d51975 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-ar/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">افتح في…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">أنفتحه في التطبيق؟ قد لا يكون نشاطك خاصا بعد الآن.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">افتح في تطبيق آخر</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">أتريد مغادرة %s لعرض هذا المحتوى؟</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">افتح</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">ألغِ</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-ast/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-ast/strings.xml new file mode 100644 index 0000000000..b0e7e03ca9 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-ast/strings.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Abrir en…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">¿Quies abrir el conteníu na aplicación? La to actividá yá nun va ser privada.</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Abrir</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Encaboxar</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-az/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-az/strings.xml new file mode 100644 index 0000000000..532a7d8027 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-az/strings.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Bununla aç…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Tətbiqdə açılsın? Aktivliyiniz artıq məxfi qalmaya bilər.</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Aç</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Ləğv et</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-azb/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-azb/strings.xml new file mode 100644 index 0000000000..34639febb9 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-azb/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">… دا آچین</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">اپده آچیلسین؟ فعالیتیز آرتیق گیزلی اولمایا بیلیر.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">آیری اپده آچین</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">بو موحتوایا باخماق اوچون %s -دن آیریلماق ایستییرسیز؟</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">آچ</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">لغو</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-be/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-be/strings.xml new file mode 100644 index 0000000000..9895f225e6 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-be/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Адкрыць у…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Адкрыць у праграме? Вашы дзеянні, магчыма, больш не будуць прыватнымі.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Адкрыць у іншай праграме</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Хочаце выйсці з %s, каб паглядзець гэтае змесціва?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Адкрыць</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Адмена</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-bg/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-bg/strings.xml new file mode 100644 index 0000000000..e9d4200619 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-bg/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Отваряне в…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Отваряне в приложение? Вашите действия може вече да не са поверителни.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Отваряне в друго приложение</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Желаете ли да напуснете %s, за да прегледате съдържанието?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Отваряне</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Отказ</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-bn/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-bn/strings.xml new file mode 100644 index 0000000000..73f07daddf --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-bn/strings.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">খোলা…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">অ্যাপে খুলবেন? আপনার ক্রিয়াকলাপ আর ব্যক্তিগত নাও থাকতে পারে।</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">খুলুন</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">বাতিল</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-br/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-br/strings.xml new file mode 100644 index 0000000000..37583e59e1 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-br/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Digeriñ e…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Digeriñ en arload? Gallout a rafe ocʼh oberiantiz paouez da vezañ prevez.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Digeriñ en un arload all</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Fellout a ra deoc’h leuskel %s da welet an dra-mañ?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Digeriñ</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Nullañ</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-bs/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-bs/strings.xml new file mode 100644 index 0000000000..30083615a2 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-bs/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Otvori u…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Otvori u aplikaciji? Vaš rad možda više neće biti privatan.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Otvorite u drugoj aplikaciji</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Želite li napustiti %s da pogledate ovaj sadržaj?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Otvori</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Otkaži</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-ca/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-ca/strings.xml new file mode 100644 index 0000000000..cfd1c79b2a --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-ca/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Obre amb…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Voleu obrir-ho en l’aplicació? És possible que la vostra activitat deixi de ser privada.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Obre en una altra aplicació</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Voleu sortir del %s per a veure aquest contingut?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Obre</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Cancel·la</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-cak/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-cak/strings.xml new file mode 100644 index 0000000000..a2b6d94329 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-cak/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Tijaq pa…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">¿La nijaq pa ri chokoy? Rik\'in jub\'a\' man xtichinäx ta chik ri asamaj.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Tijaq pa jun chik chokoy</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">¿La nawajo\' chi ri %s? nuk\'üt re rupam re\'?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Tijaq</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Tiq\'at</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-ceb/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-ceb/strings.xml new file mode 100644 index 0000000000..9ec6bca557 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-ceb/strings.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">i-Open sa…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">i-Open sa app? Basin imong mga lihok dili na pribado.</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Open</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Cancel</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-ckb/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-ckb/strings.xml new file mode 100644 index 0000000000..abc334425f --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-ckb/strings.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">کردنەوە لە …</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">کردنەوە لە بەرنامە؟ چالاکیەکانت لەوانەیە چیتر تایبەت و شاراوە نەبن.</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">کردنەوە</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">پاشگەزبوونەوە</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-co/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-co/strings.xml new file mode 100644 index 0000000000..1a396ca554 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-co/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Apre cù…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Apre cù l’appiecazione ? A vostra attività puderia ùn esse più privata.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Apre in un’altra appiecazione</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Vulete lascià %s per affissà stu cuntenutu ?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Apre</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Abbandunà</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-cs/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-cs/strings.xml new file mode 100644 index 0000000000..375f1ef655 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-cs/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Otevřít v…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Chcete odkaz otevřít v jiné aplikaci? Vaše prohlížení nemusí zůstat anonymní.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Otevřít v jiné aplikaci</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Chcete aplikaci %s dovolit zobrazit tento obsah?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Otevřít</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Zrušit</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-cy/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-cy/strings.xml new file mode 100644 index 0000000000..ab9964cfac --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-cy/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Agor yn…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Agor yn yr ap? Efallai na fydd eich gweithgaredd yn breifat mwyach.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Agor mewn ap arall</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Hoffech chi adael %s i weld y cynnwys hwn?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Agor</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Diddymu</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-da/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-da/strings.xml new file mode 100644 index 0000000000..4a066f5e02 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-da/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Åbn i…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Åbn i app? Din aktivitet er muligvis ikke længere privat.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Åbn i en anden app</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Vil du forlade %s for at se dette indhold?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Åbn</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Annuller</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-de/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-de/strings.xml new file mode 100644 index 0000000000..4bf0ab2df2 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-de/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Öffnen in…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">In App öffnen? Ihre Aktivitäten sind dann möglicherweise nicht mehr privat.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">In einer anderen App öffnen</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Möchten Sie %s verlassen, um diesen Inhalt anzuzeigen?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Öffnen</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Abbrechen</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-dsb/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-dsb/strings.xml new file mode 100644 index 0000000000..70ab0f28c3 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-dsb/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Wócyniś w…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">W nałoženju wócyniś? Waša aktiwita wěcej njamóžo priwatna byś.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">W drugem nałoženju wócyniś</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Cośo %s skóńcyś, aby se wopśimjeśe woglědał?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Wócyniś</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Pśetergnuś</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-el/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-el/strings.xml new file mode 100644 index 0000000000..02ed2b1c91 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-el/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Άνοιγμα σε…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Άνοιγμα στην εφαρμογή; Η δραστηριότητά σας ενδέχεται να μην είναι πλέον ιδιωτική.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Άνοιγμα σε άλλη εφαρμογή</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Θέλετε να αποχωρήσετε από το %s για την προβολή αυτού του περιεχομένου;</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Άνοιγμα</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Ακύρωση</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-en-rCA/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-en-rCA/strings.xml new file mode 100644 index 0000000000..4e9466cac8 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-en-rCA/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Open in…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Open in app? Your activity may no longer be private.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Open in another app</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Would you like to leave %s to view this content?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Open</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Cancel</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-en-rGB/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-en-rGB/strings.xml new file mode 100644 index 0000000000..4e9466cac8 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-en-rGB/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Open in…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Open in app? Your activity may no longer be private.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Open in another app</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Would you like to leave %s to view this content?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Open</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Cancel</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-eo/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-eo/strings.xml new file mode 100644 index 0000000000..0a4ef491f5 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-eo/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Malfermi per…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Ĉu malfermi en programo? Via retumo povus ne plu esti privata.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Malfermi per alia apo</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Ĉu vi ŝatus forlasi %s por vidi tiun ĉi enhavon?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Malfermi</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Nuligi</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-es-rAR/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-es-rAR/strings.xml new file mode 100644 index 0000000000..fdf53466fb --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-es-rAR/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Abrir en…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">¿Abrir en aplicación? Es posible que tu actividad deje de ser privada.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Abrir en otra aplicación</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">¿Querés dejar que %s muestre este contenido?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Abrir</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Cancelar</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-es-rCL/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-es-rCL/strings.xml new file mode 100644 index 0000000000..1a749b1dcc --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-es-rCL/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Abrir en…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">¿Abrir en la aplicación? Puede que tu actividad deje de ser privada.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Abrir en otra app</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">¿Te gustaría dejar %s para ver este contenido?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Abrir</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Cancelar</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-es-rES/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-es-rES/strings.xml new file mode 100644 index 0000000000..8a7fd96185 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-es-rES/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Abrir en…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">¿Abrir en aplicación? Es posible que tu actividad deje de ser privada.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Abrir en otra aplicación</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">¿Quiere dejar %s para ver este contenido?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Abrir</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Cancelar</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-es-rMX/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-es-rMX/strings.xml new file mode 100644 index 0000000000..21f4054bfe --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-es-rMX/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Abrir en…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">¿Abrir en la aplicación? Puede que tu actividad deje de ser privada.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Abrir en otra aplicación</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">¿Te gustaría dejar %s para ver este contenido?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Abrir</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Cancelar</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-es/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-es/strings.xml new file mode 100644 index 0000000000..8a7fd96185 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-es/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Abrir en…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">¿Abrir en aplicación? Es posible que tu actividad deje de ser privada.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Abrir en otra aplicación</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">¿Quiere dejar %s para ver este contenido?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Abrir</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Cancelar</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-et/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-et/strings.xml new file mode 100644 index 0000000000..3f5389f6c0 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-et/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Ava link äpiga…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Kas soovid avada äpis? Sinu tegevus ei pruugi siis enam privaatne olla.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Ava teises äpis</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Kas soovid selle sisu vaatamiseks %sist lahkuda?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Ava</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Loobu</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-eu/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-eu/strings.xml new file mode 100644 index 0000000000..321bc172f4 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-eu/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Ireki honekin…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Aplikazioan ireki? Baliteke zure jarduera pribatua ez izatea hemendik aurrera.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Ireki beste aplikazio batean</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">%s aplikazioa utzi nahi duzu eduki hau ikusteko?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Ireki</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Utzi</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-fa/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-fa/strings.xml new file mode 100644 index 0000000000..d2f7f56bcc --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-fa/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">گشودن در…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">گشودن در کاره؟ فعالیت شما ممکن است دیگر خصوصی نباشد.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">باز کردن در برنامه دیگر</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">آیا مایلید %s را ترک کنید تا این محتوا را ببینید؟</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">گشودن</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">لغو</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-ff/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-ff/strings.xml new file mode 100644 index 0000000000..fb2352635b --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-ff/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Uddit e…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Uddit-de e nder jaaɓnirgal? Golle maina mbaawi nattude wonde cuuriiɗe.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Uddit e jaaɓngal goɗngal</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Aɗa yiɗi yaltude %s ngam yiyde ndii loowdi?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Uddit</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Haaytu</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-fi/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-fi/strings.xml new file mode 100644 index 0000000000..8cd90c26b4 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-fi/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Avaa sovelluksella…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Avaa sovelluksella? Toimesi eivät välttämättä ole enää yksityisiä.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Avaa toisessa sovelluksessa</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Siirrytäänkö sovelluksesta %s tämän sisällön katseluun?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Avaa</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Peruuta</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-fr/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-fr/strings.xml new file mode 100644 index 0000000000..64ad89e7e6 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-fr/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Ouvrir dans…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Ouvrir dans l’application ? Votre activité pourrait ne plus être privée.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Ouvrir dans une autre application</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Souhaitez-vous quitter %s pour afficher ce contenu ?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Ouvrir</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Annuler</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-fur/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-fur/strings.xml new file mode 100644 index 0000000000..336dc6b276 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-fur/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Vierç in…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Vierzi te aplicazion? Al è pussibil che lis tôs ativitâts no restin plui privadis.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Vierç intune altre app</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Desideristu lâ fûr di %s par visualizâ chest contignût?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Vierç</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Anule</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-fy-rNL/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-fy-rNL/strings.xml new file mode 100644 index 0000000000..239bbb058d --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-fy-rNL/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Iepenje yn…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Iepenje yn in app? Jo aktiviteit is miskien net langer privee.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Iepenje yn oare app</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Wolle jo %s ferlitte om dizze ynhâld te besjen?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Iepenje</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Annulearje</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-ga-rIE/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-ga-rIE/strings.xml new file mode 100644 index 0000000000..1e64cbab2d --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-ga-rIE/strings.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Oscail i…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Oscail in aip? Seans nach mbeidh do chuid gníomhaíochtaí príobháideach a thuilleadh.</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Oscail</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Cealaigh</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-gd/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-gd/strings.xml new file mode 100644 index 0000000000..7f9b198a09 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-gd/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Fosgail an-seo…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">A bheil thu airson fhosgladh ann an aplacaid? Dh’fhaoidte nach bi na nì thu prìobhaideach tuilleadh.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Fosgail ann an aplacaid eile</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">A bheil thu airson %s fhàgail gus an t-susbaint seo a leughadh?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Fosgail</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Sguir dheth</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-gl/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-gl/strings.xml new file mode 100644 index 0000000000..eee2f00cb1 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-gl/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Abrir en…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Queres abrir na aplicación? É posible que a súa actividade xa non sexa privada.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Abrir noutro aplicativo</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Quere deixar %s para ver este contido?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Abrir</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Cancelar</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-gn/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-gn/strings.xml new file mode 100644 index 0000000000..f772e611dd --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-gn/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Embojuruja amo…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Embojurujápa tembiporu’i. Ikatu ne rembiapo osẽ ñemihágui.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Embojuruja ambue tembiporu’ípe</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">¿Añetépa ehejase %s ehecha hag̃ua ko tetepy?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Mbojuruja</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Heja</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-gu-rIN/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-gu-rIN/strings.xml new file mode 100644 index 0000000000..4a4cd19689 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-gu-rIN/strings.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">આમાં ખોલો…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">એપ્લિકેશનમાં ખોલો? તમારી પ્રવૃત્તિ હવે ખાનગી રહેશે નહીં.</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">ખોલો</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">રદ કરો</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-hi-rIN/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-hi-rIN/strings.xml new file mode 100644 index 0000000000..58773f9f3a --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-hi-rIN/strings.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">इसमें खोलें…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">ऐप में खोलें? आपके गतिविधि शायद अब निजी नहीं रह सकते।</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">खोलें</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">रद्द करें</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-hil/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-hil/strings.xml new file mode 100644 index 0000000000..92009ec37e --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-hil/strings.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Pagabuksan sa…</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Buksan</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Kanselahon</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-hr/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-hr/strings.xml new file mode 100644 index 0000000000..ddd2353142 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-hr/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Otvori u …</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Otvoriti u aplikaciji? Tvoja aktivnost možda više neće biti privatna.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Otvori u drugoj aplikaciji</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Želite li napustiti %s da vidite ovaj sadržaj?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Otvori</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Odustani</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-hsb/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-hsb/strings.xml new file mode 100644 index 0000000000..f2200e0692 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-hsb/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Wočinić w…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">W nałoženju wočinić? Waša aktiwita hižo njemóže priwatna być.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">W druhim nałoženju wočinić</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Chceće %s skónčić, zo byšće sej wobsah wobhladał?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Wočinić</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Přetorhnyć</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-hu/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-hu/strings.xml new file mode 100644 index 0000000000..5118745c3e --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-hu/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Megnyitás a következővel…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Megnyitja az alkalmazásban? Lehet, hogy tevékenysége már nem lesz privát.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Megnyitás egy másik alkalmazásban</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Elhagyja a %sot a tartalom megtekintéséhez?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Megnyitás</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Mégse</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-hy-rAM/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-hy-rAM/strings.xml new file mode 100644 index 0000000000..f6adb07c3b --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-hy-rAM/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Բացել հետևյալում…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Բացե՞լ եք հավելվածում: Ձեր գործունեությունն այլևս չի կարող լինել մասնավոր:</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Բացել այլ հավելվածում</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Ցանկանու՞մ եք հեռանալ %s-ից՝ այս բովանդակությունը դիտելու համար:</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Բացել</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Չեղարկել</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-ia/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-ia/strings.xml new file mode 100644 index 0000000000..c27deca6a9 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-ia/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Aperir in…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Aperir in le app? Tu activitate poterea devenir non private</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Aperir in un altere application</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Desira tu permitter que %s vide iste contento?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Aperir</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Cancellar</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-in/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-in/strings.xml new file mode 100644 index 0000000000..30194e3a14 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-in/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Buka di…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Buka di aplikasi? Aktivitas Anda mungkin tidak lagi pribadi.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Buka di aplikasi lainnya</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Ingin meninggalkan %s untuk melihat konten ini?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Buka</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Batal</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-is/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-is/strings.xml new file mode 100644 index 0000000000..86fbd7b19d --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-is/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Opna með…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Opna í smáforriti? Vera má að athafnir þínar verði opinberar.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Opna með öðru forriti</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Viltu yfirgefa %s til að skoða þetta efni?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Opna</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Hætta við</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-it/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-it/strings.xml new file mode 100644 index 0000000000..59b022a2d0 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-it/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Apri in…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Aprire con questa app? Le tue attività potrebbero non rimanere private.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Apri in un’altra app</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Uscire da %s per visualizzare questo contenuto?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Apri</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Annulla</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-iw/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-iw/strings.xml new file mode 100644 index 0000000000..933d4fba76 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-iw/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">פתיחה ב…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">האם לפתוח ביישומון? ייתכן שהפעילות שלך כבר לא תהיה פרטית.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">פתיחה ביישומון אחר</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">האם ברצונך לעזוב את %s כדי לצפות בתוכן זה?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">פתיחה</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">ביטול</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-ja/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-ja/strings.xml new file mode 100644 index 0000000000..e20cb72eaa --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-ja/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">外部アプリで開く…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">外部アプリで開く場合、その行動はプライベートにはなりません。</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">他のアプリで開く</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">%s を離れてこのコンテンツを表示しますか?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">開く</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">キャンセル</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-ka/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-ka/strings.xml new file mode 100644 index 0000000000..791e2e1586 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-ka/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">ბმულის გახსნა…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">ხსნით პროგრამაში? თქვენი მოქმედებები შეიძლება გამჟღავნდეს.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">სხვა პროგრამით გახსნა</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">გსურთ დატოვოთ %s ამ შიგთავსის სანახავად?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">გახსნა</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">გაუქმება</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-kaa/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-kaa/strings.xml new file mode 100644 index 0000000000..89468a8e2f --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-kaa/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">…da ashıw</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Baǵdarlamada ashılsın ba? Endi háreketińiz jeke bolmawı múmkin.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Basqa baǵdarlamada ashıw</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Bul kontentti kóriw ushın %s dan shıǵıwdı qáleysiz be?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Ashıw</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Biykarlaw</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-kab/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-kab/strings.xml new file mode 100644 index 0000000000..6c96ab6468 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-kab/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Ldi deg…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Ldi deg usnas? Armud-ik yezmer ur yettili ara d abaḍni.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Ldi deg usnas-nniḍen</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Tebɣiḍ ad teǧǧeḍ %s i uskan n ugbur-a?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Ldi</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Sefsex</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-kk/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-kk/strings.xml new file mode 100644 index 0000000000..a2a1495f9c --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-kk/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Көмегімен ашу…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Қолданбада ашу керек пе? Әрекетіңіз енді жеке болмауы мүмкін.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Басқа қолданбада ашу</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Осы мазмұнды көру үшін %s қалдырғыңыз келе ме?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Ашу</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Бас тарту</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-kmr/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-kmr/strings.xml new file mode 100644 index 0000000000..3f75f3c7b4 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-kmr/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Veke di…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Di sepanê de veke? Dibe ku çalakiyên te veşartî nemînin.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Di appeke din de veke</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Gelo tu dixwazî ji bo dîtina vê naverokê ji %sê derkevî?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Veke</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Betal bike</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-kn/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-kn/strings.xml new file mode 100644 index 0000000000..228fbba25e --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-kn/strings.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">ಇದರಲ್ಲಿ ತೆರೆ…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">ಅಪ್ಲಿಕೇಶನ್ನಲ್ಲಿ ತೆರೆಯುವುದೇ? ನಿಮ್ಮ ಚಟುವಟಿಕೆ ಇನ್ನು ಮುಂದೆ ಖಾಸಗಿಯಾಗಿರಬಾರದು.</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">ತೆರೆ</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">ರದ್ದು ಮಾಡು</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-ko/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-ko/strings.xml new file mode 100644 index 0000000000..6331322422 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-ko/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">앱에서 열기…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">앱에서 여시겠습니까? 사용자의 활동이 더 이상 보호되지 않을 수 있습니다.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">다른 앱에서 열기</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">이 콘텐츠를 보기 위해 %s에서 나가시겠습니까?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">열기</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">취소</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-lij/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-lij/strings.xml new file mode 100644 index 0000000000..f5d32160c9 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-lij/strings.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Arvi in…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Arvî con sta app? Dòppo e teu ativitæ porieivan no ese ciù privæ.</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Arvi</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Anulla</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-lo/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-lo/strings.xml new file mode 100644 index 0000000000..8a5b283d03 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-lo/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">ເປີດໃນ…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">ເປີດໃນແອັບນີ້ບໍ່? ການເຄື່ອນໄຫວຂອງທ່ານອາດຈະບໍ່ເປັນສ່ວນຕົວອີກຕໍ່ໄປ.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">ເປີດໃນແອັບອື່ນ</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">ທ່ານຕ້ອງການອອກຈາກ %s ເພື່ອເບິ່ງເນື້ອຫານີ້ບໍ?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">ເປີດ</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">ຍົກເລີກ</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-lt/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-lt/strings.xml new file mode 100644 index 0000000000..7eccf4ceb1 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-lt/strings.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Atverti per…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Atverti programoje? Jūs veikla galimai nebus privati.</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Atverti</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Atsisakyti</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-mix/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-mix/strings.xml new file mode 100644 index 0000000000..4a5791ab9c --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-mix/strings.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Kuna tsi…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">¿Kuna nu aplicación? ntyina ni ku kuntye^e ña sau.</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Kuna</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Kunchatu</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-ml/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-ml/strings.xml new file mode 100644 index 0000000000..8b0c9711bd --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-ml/strings.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">ഇതിൽ തുറക്കുക…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">അപ്ലിക്കേഷനിൽ തുറക്കണോ? നിങ്ങളുടെ പ്രവർത്തനം ഇനിമേൽ സ്വകാര്യമായിരിക്കില്ല.</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">തുറക്കുക</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">റദ്ദാക്കുക</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-mr/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-mr/strings.xml new file mode 100644 index 0000000000..c2973030a4 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-mr/strings.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">मध्ये उघडा…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">अॅपमध्ये उघडायचे आहे का? आपली कृती यापुढे गोपनीय राहणार नाही.</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">उघडा</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">रद्द करा</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-my/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-my/strings.xml new file mode 100644 index 0000000000..03a631f703 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-my/strings.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">…တွင် ဖွင့်ရန်</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">အက်ပ်ဖွင့်ပါသလား။ သင်၏လုပ်ဆောင်မှုသည် မလျှို့ဝှက်ပါ။</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">ဖွင့်ပါ</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">ပယ်ဖျက်ပါ</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-nb-rNO/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-nb-rNO/strings.xml new file mode 100644 index 0000000000..376b821089 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-nb-rNO/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Åpne i…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Åpne i app? Aktiviteten din er muligens ikke lenger privat.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Åpne i en annen app</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Vil du forlate %s for å se dette innholdet?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Åpne</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Avbryt</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-ne-rNP/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-ne-rNP/strings.xml new file mode 100644 index 0000000000..ba26f27e88 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-ne-rNP/strings.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">… मा खोल्नुहोस्</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">एपमा खोल्न चाहानुहुन्छ ? तपाईका गतिबिधीहरु अब उप्रान्त गोप्य नहुन सक्छन्।</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">खोल्नुहोस्</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">रद्द गर्नुहोस्</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-nl/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-nl/strings.xml new file mode 100644 index 0000000000..8ef9a0217c --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-nl/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Openen in…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Openen in een app? Uw activiteit is mogelijk niet langer privé.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Openen in andere app</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Wilt u %s verlaten om deze inhoud te bekijken?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Openen</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Annuleren</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-nn-rNO/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-nn-rNO/strings.xml new file mode 100644 index 0000000000..2f167375f7 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-nn-rNO/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Opne i…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Opne i app? Aktiviteten din er kanskje ikkje lenger privat.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Opne i ein annan app</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Vil du forlate %s for å sjå dette innhaldet?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Opne</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Avbryt</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-oc/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-oc/strings.xml new file mode 100644 index 0000000000..c842e1490d --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-oc/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Dobrir amb…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Dobrir dins l’aplicacion ? Vòstra activitat poiriá quitar d’èsser privada.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Dobrir dins una autra aplicacion</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Volètz quitar %s per afichar aqueste contengut ?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Dobrir</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Anullar</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-or/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-or/strings.xml new file mode 100644 index 0000000000..f050324b07 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-or/strings.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">…ରେ ଖୋଲନ୍ତୁ</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">ଖୋଲନ୍ତୁ</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">ବାତିଲ କରନ୍ତୁ</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-pa-rIN/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-pa-rIN/strings.xml new file mode 100644 index 0000000000..4020752925 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-pa-rIN/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">…ਨਾਲ ਖੋਲ੍ਹੋ</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">ਐਪ ‘ਚ ਖੋਲ੍ਹਣਾ ਹੈ? ਤੁਹਾਡੀ ਸਰਗਰਮੀ ਨਿੱਜੀ ਨਹੀਂ ਵੀ ਰਹਿ ਸਕਦੀ ਹੈ।</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">ਹੋਰ ਐਪ ਵਿੱਚ ਖੋਲ੍ਹੋ</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">ਕੀ ਤੁਸੀਂ ਇਹ ਸਮੱਗਰੀ ਵੇਖਣ ਲਈ %s ਤੋਂ ਬਾਹਰ ਜਾਣਾ ਚਾਹੁੰਦੇ ਹੋ?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">ਖੋਲ੍ਹੋ</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">ਰੱਦ ਕਰੋ</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-pa-rPK/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-pa-rPK/strings.xml new file mode 100644 index 0000000000..5582e5e082 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-pa-rPK/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">کیہنوں کھولھو…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">ایپ چ کھولھݨا اے؟ تہاڈی ورتوں نجی نہیں وی رہ سکدی اے۔</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">دوجی ایپ نال کھولھو</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">کیہہ تسیں %s توں باہر جاوݨ چاہندے او؟</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">کھولھو</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">رد کرو</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-pl/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-pl/strings.xml new file mode 100644 index 0000000000..f761f3bc3f --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-pl/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Otwórz w…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Otworzyć w aplikacji? Twoje działania mogą nie być już prywatne.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Otwórz w innej aplikacji</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Czy opuścić aplikację %s, aby wyświetlić tę treść?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Otwórz</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Anuluj</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-pt-rBR/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-pt-rBR/strings.xml new file mode 100644 index 0000000000..894a5ffac6 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-pt-rBR/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Abrir no…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Abrir em aplicativo? Sua atividade pode não ser mais privativa.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Abrir em outro aplicativo</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Quer deixar %s ver este conteúdo?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Abrir</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Cancelar</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-pt-rPT/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-pt-rPT/strings.xml new file mode 100644 index 0000000000..7e2e9c3692 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-pt-rPT/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Abrir em…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Abrir na aplicação? A sua atividade pode deixar de ser privada.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Abrir noutra aplicação</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Gostaria de deixar %s para ver este conteúdo?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Abrir</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Cancelar</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-rm/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-rm/strings.xml new file mode 100644 index 0000000000..4ca52de0bc --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-rm/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Avrir en…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Avrir en ina app? Tia activitad n\'è lura forsa betg pli privata.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Avrir en ina autra app</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Vuls ti bandunar %s per laschar mussar quest cuntegn?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Avrir</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Interrumper</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-ro/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-ro/strings.xml new file mode 100644 index 0000000000..b3359b3b12 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-ro/strings.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Deschide în…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Deschizi în aplicație? Este posibil ca activitatea ta să nu mai fie privată.</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Deschide</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Anulează</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-ru/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-ru/strings.xml new file mode 100644 index 0000000000..c78bc38057 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-ru/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Открыть в…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Открыть в приложении? Возможно, ваши действия перестанут быть приватными.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Открыть в другом приложении</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Вы хотите покинуть %s для просмотра этого содержимого?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Открыть</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Отмена</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-sat/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-sat/strings.xml new file mode 100644 index 0000000000..ef1819dbaa --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-sat/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">… ᱨᱮ ᱡᱷᱤᱡᱽ ᱢᱮ</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">ᱮᱯ ᱨᱮ ᱡᱷᱤᱡᱽ ᱢᱮ? ᱟᱢᱟᱜ ᱠᱟᱹᱢᱤ ᱟᱨ ᱡᱟᱹᱥᱛᱤ ᱜᱷᱟᱹᱬᱤᱡ ᱱᱤᱡᱮᱨᱟᱜ ᱵᱟᱝ ᱛᱟᱦᱮᱸᱱ-ᱟ ᱾</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">ᱮᱴᱟᱜ ᱮᱯ ᱨᱮ ᱡᱷᱤᱡᱽ ᱢᱮ</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">ᱟᱢ ᱫᱚ ᱱᱚᱶᱟ ᱧᱮᱞ ᱞᱟᱹᱜᱤᱫ %s ᱟᱲᱟᱜ ᱥᱟᱱᱟᱢ ᱠᱟᱱᱟ ᱥᱮ ?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">ᱡᱷᱤᱡᱽ ᱢᱮ</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">ᱵᱟᱹᱰᱨᱟᱹ</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-sc/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-sc/strings.xml new file mode 100644 index 0000000000..ca74934899 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-sc/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Aberi in…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Boles abèrrere su cuntenutu in s’aplicatzione? Podet èssere chi s’atividade tua non siat prus privada.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Aberi in un’àtera aplicatzione</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Boles lassare %s pro bìdere custu cuntenutu?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Aberi</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Annulla</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-si/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-si/strings.xml new file mode 100644 index 0000000000..251176ffaf --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-si/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">මෙහි අරින්න…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">යෙදුමෙහි අරින්නද? ඔබගේ ක්රියාකාරකම් තවදුරටත් පෞද්. නොවීමට හැකිය.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">අන් යෙදුමකින් අරින්න</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">මෙම අන්තර්ගතය බැලීමට %s හැර යාමට කැමතිද?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">අරින්න</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">අවලංගු</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-sk/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-sk/strings.xml new file mode 100644 index 0000000000..0dbc181a50 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-sk/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Otvoriť pomocou…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Chcete tento odkaz otvoriť v aplikácii? Môže sa tým znížiť úroveň vášho súkromia.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Otvoriť v inej aplikácii</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Chcete tento obsah zobraziť v aplikácii %s?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Otvoriť</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Zrušiť</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-skr/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-skr/strings.xml new file mode 100644 index 0000000000..78faaff48b --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-skr/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">۔۔۔ وچ کھولو</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">ایپ وچ کھولوں؟ تہاݙی سرگرمی ہݨ نجی کائناں ہوسی۔</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">ہک ٻئی ایپ وچ کھولو</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">بھلا تساں ایہ مواد ݙیکھݨ کیتے %s کوں چھوڑݨ پسند کریسو؟</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">کھولو</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">منسوخ</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-sl/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-sl/strings.xml new file mode 100644 index 0000000000..1e61c3da29 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-sl/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Odpri v …</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Odprem v aplikaciji? Vaša dejavnost morda ne bo več zasebna.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Odpiranje v drugi aplikaciji</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Ali želite za ogled te vsebine zapustiti %s?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Odpri</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Prekliči</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-sq/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-sq/strings.xml new file mode 100644 index 0000000000..38db6daf14 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-sq/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Hapeni në…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Të hapet në aplikacion? Veprimtaria juaj mund të mos jetë më private.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Hape me një aplikacion tjetër</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Do të donit ta linit %s të shohë këtë lëndë?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Hape</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Anuloje</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-sr/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-sr/strings.xml new file mode 100644 index 0000000000..bda63de1f9 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-sr/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Отвори у…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Отвори у апликацији? Ваше радње можда неће више бити приватне.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Отвори у другој апликацији</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Желите ли да напустите %s да видите овај садржај?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Отвори</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Откажи</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-su/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-su/strings.xml new file mode 100644 index 0000000000..1253240696 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-su/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Buka di…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Buka dina aplikasi? Réngkak anjeun bisa jadi henteu nyamuni.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Buka di séjén aplikasi</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Badé ninggalkeun %s pikeun muka ieu kontén?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Buka</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Bolay</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-sv-rSE/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-sv-rSE/strings.xml new file mode 100644 index 0000000000..3c39a5eb58 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-sv-rSE/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Öppna med…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Öppna i appen? Din aktivitet kanske inte längre är privat.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Öppna i en annan app</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Vill du lämna %s för att se detta innehåll?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Öppna</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Avbryt</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-ta/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-ta/strings.xml new file mode 100644 index 0000000000..2268adbf51 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-ta/strings.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">இதில் திற…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">செயலியில் திறக்கவா? உங்கள் செயல்பாடு இனி தனிப்பட்டதாக இருக்காது.</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">திற</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">இரத்து</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-te/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-te/strings.xml new file mode 100644 index 0000000000..5e5bb1e0f8 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-te/strings.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">దీనిలో తెరువు…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">అనువర్తనంలో తెరవాలా? మీ కార్యాచరణ ఇకపై అంతరంగికంగా ఉండకపోవచ్చు.</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">తెరువు</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">రద్దుచేయి</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-tg/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-tg/strings.xml new file mode 100644 index 0000000000..da0b24aecd --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-tg/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Кушодан дар…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Дар барнома кушода шавад? Фаъолияти шумо метавонад дигар хусусӣ набошад.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Кушодан дар барномаи дигар</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Барои дидани ин муҳтаво шумо мехоҳед, ки %s-ро тарк кунед?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Кушодан</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Бекор кардан</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-th/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-th/strings.xml new file mode 100644 index 0000000000..88f38d1f46 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-th/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">เปิดใน…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">ต้องการเปิดในแอปหรือไม่? กิจกรรมของคุณอาจไม่เป็นส่วนตัวอีกต่อไป</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">เปิดในแอปอื่น</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">คุณต้องการออกจาก %s เพื่อดูเนื้อหานี้หรือไม่?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">เปิด</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">ยกเลิก</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-tl/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-tl/strings.xml new file mode 100644 index 0000000000..669bc89bf0 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-tl/strings.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Buksan sa…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Buksan sa app? Maaaring hindi na maging pribado ang iyong aktibidad.</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Buksan</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Kanselahin</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-tr/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-tr/strings.xml new file mode 100644 index 0000000000..2d6239cfa8 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-tr/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Birlikte aç…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Uygulamada açılsın mı? İşleminiz gizli kalmayabilir.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Başka bir uygulamada aç</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Bu içeriği görüntülemek için %s tarayıcısından ayrılmak istiyor musunuz?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Aç</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">İptal</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-trs/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-trs/strings.xml new file mode 100644 index 0000000000..43958b8ee7 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-trs/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Nā\'nīn riña…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Nā\'nīnt riña aplikasiûn nan anj. Ga\'ue gīni\'iāj a\'ngô nej si sa \'iát.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Nā’nïn riña a’ngô app</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Ruhuât dūnâjt %s da’ gā’hue ni’hiājt sa mà riña nan anj.</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Nā\'nīn</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Duyichin\'</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-tt/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-tt/strings.xml new file mode 100644 index 0000000000..92e461c2bc --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-tt/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">… белән ачу</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Кушымтада ачу кирәкме? Гамәлләрегез бүтән хосусый булмаска да мөмкин.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Башка кушымтада ачу</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Бу эчтәлекне карау өчен %s программасыннан чыгарга телисезме?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Ачу</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Баш тарту</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-tzm/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-tzm/strings.xml new file mode 100644 index 0000000000..bea48edbd7 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-tzm/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Rẓem g…</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Rẓem</string> + </resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-ug/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-ug/strings.xml new file mode 100644 index 0000000000..d763c0fff2 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-ug/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">ئېچىش…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">ئەپتە ئاچامسىز؟ پائالىيىتىڭىز ئەمدى خۇپىيانە بولماسلىقى مۇمكىن.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">باشقا ئەپتە ئاچ</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">بۇ مەزمۇننى كۆرۈش ئۈچۈن %s دىن ئايرىلامسىز؟</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">ئېچىش</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">بىكار قىلىش</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-uk/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-uk/strings.xml new file mode 100644 index 0000000000..49630c3a6b --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-uk/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Відкрити в…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Відкрити в програмі? Ваша діяльність може більше не бути приватною.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Відкрити в іншій програмі</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Бажаєте вийти з %s для перегляду цього вмісту?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Відкрити</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Скасувати</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-ur/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-ur/strings.xml new file mode 100644 index 0000000000..0a2932d39d --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-ur/strings.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">… میں کھولیں</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">ایپ میں کھولیں؟ آپکی سرگرمی اب ذاتی نہیں ہوگی۔</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">کھولیں</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">منسوخ کریں</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-uz/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-uz/strings.xml new file mode 100644 index 0000000000..3ea8055ac7 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-uz/strings.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Ochish:</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Ilovada ochilsinmi? Faoliyatingizning maxfiyligi yoʻqoladi.</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Ochish</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Bekor qilish</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-vec/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-vec/strings.xml new file mode 100644 index 0000000000..99cf7d769d --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-vec/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Vèrxi in…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Vèrxere co sta app? Ƚe to atività ƚe poderia no restare private.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Verxi en on altra app</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Nare fora da %s par vixualixare cuesto contenudo?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Vèrxi</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Anuƚa</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-vi/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-vi/strings.xml new file mode 100644 index 0000000000..04ca57ca9d --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-vi/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Mở trong…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Mở trong ứng dụng? Hoạt động của bạn có thể không còn riêng tư.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Mở trong ứng dụng khác</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Bạn có muốn rời khỏi %s để xem nội dung này không?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Mở</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Hủy bỏ</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-yo/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-yo/strings.xml new file mode 100644 index 0000000000..cee62dfb36 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-yo/strings.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Ṣi nínú…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Ṣí sílẹ̀ lórí áàpù? Ohun tí ò ń ṣe lè má jẹ́ ìkọ̀kọ̀ mọ́.</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Ṣi</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Fagile</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-zh-rCN/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-zh-rCN/strings.xml new file mode 100644 index 0000000000..7be862b73b --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-zh-rCN/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">打开于…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">要在应用中打开吗?您的上网行为可能不再保持私密。</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">其他应用打开</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">您想要离开 %s 来查看此内容吗?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">打开</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">取消</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values-zh-rTW/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values-zh-rTW/strings.xml new file mode 100644 index 0000000000..034c22b91c --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values-zh-rTW/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">開啟於…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">要使用 App 開啟?您的上網行為可能不再能保持隱私。</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">用其他應用程式開啟</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">您想要離開 %s 來檢視此內容嗎?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">開啟</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">取消</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/main/res/values/strings.xml b/mobile/android/android-components/components/feature/app-links/src/main/res/values/strings.xml new file mode 100644 index 0000000000..04d45e5412 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/main/res/values/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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/. + --> + +<resources> + <!-- The tile for the list of external apps to open the link in --> + <string name="mozac_feature_applinks_open_in">Open in…</string> + <!-- The description to warn users that their private browsing session changes --> + <string name="mozac_feature_applinks_confirm_dialog_title">Open in app? Your activity may no longer be private.</string> + <!-- The title of the prompt that warns users their normal browsing session is trying to open another app --> + <string name="mozac_feature_applinks_normal_confirm_dialog_title">Open in another app</string> + <!-- The message of the prompt to confirm with users that they want to open the link in another app + %s is a placeholder that will be replaced by the app name --> + <string name="mozac_feature_applinks_normal_confirm_dialog_message">Would you like to leave %s to view this content?</string> + <!-- Opens the selected time --> + <string name="mozac_feature_applinks_confirm_dialog_confirm">Open</string> + <!-- Cancels the prompt --> + <string name="mozac_feature_applinks_confirm_dialog_deny">Cancel</string> +</resources> diff --git a/mobile/android/android-components/components/feature/app-links/src/test/java/mozilla/components/feature/app/links/AppLinkRedirectTest.kt b/mobile/android/android-components/components/feature/app-links/src/test/java/mozilla/components/feature/app/links/AppLinkRedirectTest.kt new file mode 100644 index 0000000000..dcd40035fb --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/test/java/mozilla/components/feature/app/links/AppLinkRedirectTest.kt @@ -0,0 +1,77 @@ +/* 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 mozilla.components.feature.app.links + +import android.content.Intent +import android.net.Uri +import mozilla.components.support.test.mock +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test +import org.mockito.Mockito.`when` + +class AppLinkRedirectTest { + + @Test + fun hasExternalApp() { + var appLink = AppLinkRedirect(appIntent = mock(), fallbackUrl = null, marketplaceIntent = null) + assertTrue(appLink.hasExternalApp()) + assertTrue(appLink.isRedirect()) + + appLink = AppLinkRedirect(appIntent = null, fallbackUrl = null, marketplaceIntent = null) + assertFalse(appLink.hasExternalApp()) + assertFalse(appLink.isRedirect()) + } + + @Test + fun hasFallback() { + var appLink = AppLinkRedirect(appIntent = mock(), fallbackUrl = null, marketplaceIntent = null) + assertFalse(appLink.hasFallback()) + assertTrue(appLink.isRedirect()) + + appLink = AppLinkRedirect(appIntent = mock(), fallbackUrl = "https://example.com", marketplaceIntent = null) + assertTrue(appLink.hasFallback()) + assertTrue(appLink.isRedirect()) + } + + @Test + fun isRedirect() { + var appLink = AppLinkRedirect(appIntent = null, fallbackUrl = null, marketplaceIntent = null) + assertFalse(appLink.isRedirect()) + + appLink = AppLinkRedirect(appIntent = mock(), fallbackUrl = null, marketplaceIntent = null) + assertTrue(appLink.isRedirect()) + + appLink = AppLinkRedirect(appIntent = null, fallbackUrl = "https://example.com", marketplaceIntent = null) + assertTrue(appLink.isRedirect()) + + appLink = AppLinkRedirect(appIntent = mock(), fallbackUrl = "https://example.com", marketplaceIntent = null) + assertTrue(appLink.isRedirect()) + } + + @Test + fun isInstallable() { + val intent: Intent = mock() + val uri: Uri = mock() + `when`(intent.data).thenReturn(uri) + `when`(uri.scheme).thenReturn("market") + + var appLink = AppLinkRedirect(appIntent = null, fallbackUrl = "https://example.com", marketplaceIntent = null) + assertFalse(appLink.isInstallable()) + assertTrue(appLink.isRedirect()) + + appLink = AppLinkRedirect(appIntent = intent, fallbackUrl = "https://example.com", marketplaceIntent = null) + assertTrue(appLink.isInstallable()) + assertTrue(appLink.isRedirect()) + } + + @Test + fun hasMarketplaceIntent() { + var appLink = AppLinkRedirect(appIntent = null, fallbackUrl = null, marketplaceIntent = mock()) + assertTrue(appLink.hasMarketplaceIntent()) + assertTrue(appLink.isRedirect()) + assertTrue(appLink.hasMarketplaceIntent()) + } +} diff --git a/mobile/android/android-components/components/feature/app-links/src/test/java/mozilla/components/feature/app/links/AppLinksFeatureTest.kt b/mobile/android/android-components/components/feature/app-links/src/test/java/mozilla/components/feature/app/links/AppLinksFeatureTest.kt new file mode 100644 index 0000000000..a48af2da58 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/test/java/mozilla/components/feature/app/links/AppLinksFeatureTest.kt @@ -0,0 +1,330 @@ +/* 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 mozilla.components.feature.app.links + +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import androidx.fragment.app.FragmentManager +import androidx.test.ext.junit.runners.AndroidJUnit4 +import mozilla.components.browser.state.action.ContentAction +import mozilla.components.browser.state.action.TabListAction +import mozilla.components.browser.state.selector.findTab +import mozilla.components.browser.state.state.AppIntentState +import mozilla.components.browser.state.state.ExternalPackage +import mozilla.components.browser.state.state.PackageCategory +import mozilla.components.browser.state.state.SessionState +import mozilla.components.browser.state.state.createCustomTab +import mozilla.components.browser.state.state.createTab +import mozilla.components.browser.state.store.BrowserStore +import mozilla.components.concept.engine.EngineSession +import mozilla.components.feature.session.SessionUseCases +import mozilla.components.support.test.any +import mozilla.components.support.test.eq +import mozilla.components.support.test.ext.joinBlocking +import mozilla.components.support.test.libstate.ext.waitUntilIdle +import mozilla.components.support.test.mock +import mozilla.components.support.test.rule.MainCoroutineRule +import org.junit.After +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyBoolean +import org.mockito.ArgumentMatchers.anyString +import org.mockito.Mockito.doReturn +import org.mockito.Mockito.never +import org.mockito.Mockito.spy +import org.mockito.Mockito.times +import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` + +@RunWith(AndroidJUnit4::class) +class AppLinksFeatureTest { + + @get:Rule + val coroutinesTestRule = MainCoroutineRule() + + private lateinit var store: BrowserStore + private lateinit var mockContext: Context + private lateinit var mockFragmentManager: FragmentManager + private lateinit var mockUseCases: AppLinksUseCases + private lateinit var mockGetRedirect: AppLinksUseCases.GetAppLinkRedirect + private lateinit var mockOpenRedirect: AppLinksUseCases.OpenAppLinkRedirect + private lateinit var mockEngineSession: EngineSession + private lateinit var mockDialog: RedirectDialogFragment + private lateinit var mockLoadUrlUseCase: SessionUseCases.DefaultLoadUrlUseCase + private lateinit var feature: AppLinksFeature + + private val webUrl = "https://example.com" + private val webUrlWithAppLink = "https://soundcloud.com" + private val intentUrl = "zxing://scan" + private val aboutUrl = "about://scan" + + @Before + fun setup() { + store = BrowserStore() + mockContext = mock() + + mockFragmentManager = mock() + `when`(mockFragmentManager.beginTransaction()).thenReturn(mock()) + mockUseCases = mock() + mockEngineSession = mock() + mockDialog = mock() + mockLoadUrlUseCase = mock() + + mockGetRedirect = mock() + mockOpenRedirect = mock() + `when`(mockUseCases.interceptedAppLinkRedirect).thenReturn(mockGetRedirect) + `when`(mockUseCases.openAppLink).thenReturn(mockOpenRedirect) + + val webRedirect = AppLinkRedirect(null, webUrl, null) + val appRedirect = AppLinkRedirect(Intent.parseUri(intentUrl, 0), null, null) + val appRedirectFromWebUrl = AppLinkRedirect(Intent.parseUri(webUrlWithAppLink, 0), null, null) + + `when`(mockGetRedirect.invoke(webUrl)).thenReturn(webRedirect) + `when`(mockGetRedirect.invoke(intentUrl)).thenReturn(appRedirect) + `when`(mockGetRedirect.invoke(webUrlWithAppLink)).thenReturn(appRedirectFromWebUrl) + + feature = spy( + AppLinksFeature( + context = mockContext, + store = store, + fragmentManager = mockFragmentManager, + useCases = mockUseCases, + dialog = mockDialog, + loadUrlUseCase = mockLoadUrlUseCase, + ), + ).also { + it.start() + } + } + + @After + fun teardown() { + feature.stop() + } + + @Test + fun `feature observes app intents when started`() { + val tab = createTab(webUrl) + store.dispatch(TabListAction.AddTabAction(tab)).joinBlocking() + verify(feature, never()).handleAppIntent(any(), any(), any()) + + val intent: Intent = mock() + val appIntent = AppIntentState(intentUrl, intent) + store.dispatch(ContentAction.UpdateAppIntentAction(tab.id, appIntent)).joinBlocking() + + store.waitUntilIdle() + verify(feature).handleAppIntent(any(), any(), any()) + + val tabWithConsumedAppIntent = store.state.findTab(tab.id)!! + assertNull(tabWithConsumedAppIntent.content.appIntent) + } + + @Test + fun `feature doesn't observes app intents when stopped`() { + val tab = createTab(webUrl) + store.dispatch(TabListAction.AddTabAction(tab)).joinBlocking() + verify(feature, never()).handleAppIntent(any(), any(), any()) + + feature.stop() + + val intent: Intent = mock() + val appIntent = AppIntentState(intentUrl, intent) + store.dispatch(ContentAction.UpdateAppIntentAction(tab.id, appIntent)).joinBlocking() + + verify(feature, never()).handleAppIntent(any(), any(), any()) + } + + @Test + fun `WHEN should prompt AND in non-private mode THEN an external app dialog is shown`() { + feature = spy( + AppLinksFeature( + context = mockContext, + store = store, + fragmentManager = mockFragmentManager, + useCases = mockUseCases, + dialog = mockDialog, + loadUrlUseCase = mockLoadUrlUseCase, + shouldPrompt = { true }, + ), + ).also { + it.start() + } + + val tab = createTab(webUrl) + feature.handleAppIntent(tab, intentUrl, mock()) + + verify(mockDialog).showNow(eq(mockFragmentManager), anyString()) + verify(mockOpenRedirect, never()).invoke(any(), anyBoolean(), any()) + } + + @Test + fun `WHEN should not prompt AND in non-private mode THEN an external app dialog is not shown`() { + feature = spy( + AppLinksFeature( + context = mockContext, + store = store, + fragmentManager = mockFragmentManager, + useCases = mockUseCases, + dialog = mockDialog, + loadUrlUseCase = mockLoadUrlUseCase, + shouldPrompt = { false }, + ), + ).also { + it.start() + } + + val tab = createTab(webUrl) + feature.handleAppIntent(tab, intentUrl, mock()) + + verify(mockDialog, never()).showNow(eq(mockFragmentManager), anyString()) + } + + @Test + fun `WHEN custom tab and caller is the same as external app THEN an external app dialog is not shown`() { + feature = spy( + AppLinksFeature( + context = mockContext, + store = store, + fragmentManager = mockFragmentManager, + useCases = mockUseCases, + dialog = mockDialog, + loadUrlUseCase = mockLoadUrlUseCase, + shouldPrompt = { true }, + ), + ).also { + it.start() + } + + val tab = + createCustomTab( + id = "c", + url = webUrl, + source = SessionState.Source.External.CustomTab( + ExternalPackage("com.zxing.app", PackageCategory.PRODUCTIVITY), + ), + ) + + val appIntent: Intent = mock() + val componentName: ComponentName = mock() + doReturn(componentName).`when`(appIntent).component + doReturn("com.zxing.app").`when`(componentName).packageName + + feature.handleAppIntent(tab, intentUrl, appIntent) + + verify(mockDialog, never()).showNow(eq(mockFragmentManager), anyString()) + } + + @Test + fun `WHEN should prompt and in private mode THEN an external app dialog is shown`() { + feature = spy( + AppLinksFeature( + context = mockContext, + store = store, + fragmentManager = mockFragmentManager, + useCases = mockUseCases, + dialog = mockDialog, + loadUrlUseCase = mockLoadUrlUseCase, + shouldPrompt = { true }, + ), + ).also { + it.start() + } + + val tab = createTab(webUrl, private = true) + feature.handleAppIntent(tab, intentUrl, mock()) + + verify(mockDialog).showNow(eq(mockFragmentManager), anyString()) + verify(mockOpenRedirect, never()).invoke(any(), anyBoolean(), any()) + } + + @Test + fun `WHEN should not prompt and in private mode THEN an external app dialog is shown`() { + feature = spy( + AppLinksFeature( + context = mockContext, + store = store, + fragmentManager = mockFragmentManager, + useCases = mockUseCases, + dialog = mockDialog, + loadUrlUseCase = mockLoadUrlUseCase, + shouldPrompt = { false }, + ), + ).also { + it.start() + } + + val tab = createTab(webUrl, private = true) + feature.handleAppIntent(tab, intentUrl, mock()) + + verify(mockDialog).showNow(eq(mockFragmentManager), anyString()) + verify(mockOpenRedirect, never()).invoke(any(), anyBoolean(), any()) + } + + @Test + fun `redirect dialog is only added once`() { + val tab = createTab(webUrl, private = true) + feature.handleAppIntent(tab, intentUrl, mock()) + + verify(mockDialog).showNow(eq(mockFragmentManager), anyString()) + + doReturn(mockDialog).`when`(feature).getOrCreateDialog(false, "") + doReturn(mockDialog).`when`(mockFragmentManager).findFragmentByTag(RedirectDialogFragment.FRAGMENT_TAG) + feature.handleAppIntent(tab, intentUrl, mock()) + verify(mockDialog, times(1)).showNow(mockFragmentManager, RedirectDialogFragment.FRAGMENT_TAG) + } + + @Test + fun `only loads URL if scheme is supported`() { + val tab = createTab(webUrl, private = true) + + feature.loadUrlIfSchemeSupported(tab, intentUrl) + verify(mockLoadUrlUseCase, never()).invoke(anyString(), anyString(), any(), any()) + + feature.loadUrlIfSchemeSupported(tab, webUrl) + verify(mockLoadUrlUseCase, times(1)).invoke(anyString(), anyString(), any(), any()) + + feature.loadUrlIfSchemeSupported(tab, aboutUrl) + verify(mockLoadUrlUseCase, times(2)).invoke(anyString(), anyString(), any(), any()) + } + + @Test + fun `WHEN caller and intent have the same package name THEN return true`() { + val customTab = + createCustomTab( + id = "c", + url = webUrl, + source = SessionState.Source.External.CustomTab( + ExternalPackage("com.zxing.app", PackageCategory.PRODUCTIVITY), + ), + ) + val appIntent: Intent = mock() + val componentName: ComponentName = mock() + doReturn(componentName).`when`(appIntent).component + doReturn("com.zxing.app").`when`(componentName).packageName + assertTrue(feature.isSameCallerAndApp(customTab, appIntent)) + + val tab = createTab(webUrl, private = true) + assertFalse(feature.isSameCallerAndApp(tab, appIntent)) + + val customTab2 = + createCustomTab( + id = "c", + url = webUrl, + source = SessionState.Source.External.CustomTab( + ExternalPackage("com.example.app", PackageCategory.PRODUCTIVITY), + ), + ) + assertFalse(feature.isSameCallerAndApp(customTab2, appIntent)) + + doReturn(null).`when`(componentName).packageName + assertFalse(feature.isSameCallerAndApp(customTab, appIntent)) + } +} diff --git a/mobile/android/android-components/components/feature/app-links/src/test/java/mozilla/components/feature/app/links/AppLinksInterceptorTest.kt b/mobile/android/android-components/components/feature/app-links/src/test/java/mozilla/components/feature/app/links/AppLinksInterceptorTest.kt new file mode 100644 index 0000000000..ca3a101cdf --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/test/java/mozilla/components/feature/app/links/AppLinksInterceptorTest.kt @@ -0,0 +1,679 @@ +/* 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 mozilla.components.feature.app.links + +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import androidx.test.ext.junit.runners.AndroidJUnit4 +import mozilla.components.concept.engine.EngineSession +import mozilla.components.concept.engine.request.RequestInterceptor +import mozilla.components.feature.app.links.AppLinksInterceptor.Companion.APP_LINKS_DO_NOT_INTERCEPT_INTERVAL +import mozilla.components.feature.app.links.AppLinksInterceptor.Companion.APP_LINKS_DO_NOT_OPEN_CACHE_INTERVAL +import mozilla.components.feature.app.links.AppLinksInterceptor.Companion.addUserDoNotIntercept +import mozilla.components.feature.app.links.AppLinksInterceptor.Companion.inUserDoNotIntercept +import mozilla.components.feature.app.links.AppLinksInterceptor.Companion.lastApplinksPackageWithTimestamp +import mozilla.components.feature.app.links.AppLinksInterceptor.Companion.userDoNotInterceptCache +import mozilla.components.support.test.any +import mozilla.components.support.test.mock +import mozilla.components.support.test.whenever +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyBoolean +import org.mockito.Mockito.doReturn +import org.mockito.Mockito.verify + +@RunWith(AndroidJUnit4::class) +class AppLinksInterceptorTest { + private lateinit var mockContext: Context + private lateinit var mockUseCases: AppLinksUseCases + private lateinit var mockGetRedirect: AppLinksUseCases.GetAppLinkRedirect + private lateinit var mockEngineSession: EngineSession + private lateinit var mockOpenRedirect: AppLinksUseCases.OpenAppLinkRedirect + + private lateinit var appLinksInterceptor: AppLinksInterceptor + + private val webUrl = "https://example.com" + private val webUrlWithAppLink = "https://soundcloud.com" + private val intentUrl = "zxing://scan;S.browser_fallback_url=example.com" + private val fallbackUrl = "https://getpocket.com" + private val marketplaceUrl = "market://details?id=example.com" + + @Before + fun setup() { + mockContext = mock() + mockUseCases = mock() + mockEngineSession = mock() + mockGetRedirect = mock() + mockOpenRedirect = mock() + whenever(mockUseCases.interceptedAppLinkRedirect).thenReturn(mockGetRedirect) + whenever(mockUseCases.openAppLink).thenReturn(mockOpenRedirect) + userDoNotInterceptCache.clear() + lastApplinksPackageWithTimestamp = Pair(null, -APP_LINKS_DO_NOT_INTERCEPT_INTERVAL) + + val webRedirect = AppLinkRedirect(null, webUrl, null) + val appRedirect = AppLinkRedirect(Intent.parseUri(intentUrl, 0), null, null) + val appRedirectFromWebUrl = AppLinkRedirect(Intent.parseUri(webUrlWithAppLink, 0), null, null) + val fallbackRedirect = AppLinkRedirect(null, fallbackUrl, null) + val marketRedirect = AppLinkRedirect(null, null, Intent.parseUri(marketplaceUrl, 0)) + + whenever(mockGetRedirect.invoke(webUrl)).thenReturn(webRedirect) + whenever(mockGetRedirect.invoke(intentUrl)).thenReturn(appRedirect) + whenever(mockGetRedirect.invoke(webUrlWithAppLink)).thenReturn(appRedirectFromWebUrl) + whenever(mockGetRedirect.invoke(fallbackUrl)).thenReturn(fallbackRedirect) + whenever(mockGetRedirect.invoke(marketplaceUrl)).thenReturn(marketRedirect) + + appLinksInterceptor = AppLinksInterceptor( + context = mockContext, + interceptLinkClicks = true, + launchInApp = { true }, + useCases = mockUseCases, + ) + } + + @Test + fun `request is intercepted by user clicking on a link`() { + val response = appLinksInterceptor.onLoadRequest(mockEngineSession, webUrlWithAppLink, null, true, false, false, false, false) + assert(response is RequestInterceptor.InterceptionResponse.AppIntent) + } + + @Test + fun `request is intercepted by redirect`() { + val response = appLinksInterceptor.onLoadRequest(mockEngineSession, webUrlWithAppLink, null, false, false, true, false, false) + assert(response is RequestInterceptor.InterceptionResponse.AppIntent) + } + + @Test + fun `request is not intercepted by a subframe redirect`() { + val response = appLinksInterceptor.onLoadRequest(mockEngineSession, webUrl, null, false, false, true, false, true) + assertEquals(null, response) + } + + @Test + fun `request is intercepted by direct navigation`() { + val response = appLinksInterceptor.onLoadRequest(mockEngineSession, webUrlWithAppLink, null, false, false, false, true, false) + assert(response is RequestInterceptor.InterceptionResponse.AppIntent) + } + + @Test + fun `request is not intercepted when interceptLinkClicks is false`() { + appLinksInterceptor = AppLinksInterceptor( + context = mockContext, + interceptLinkClicks = false, + launchInApp = { true }, + useCases = mockUseCases, + ) + + val response = appLinksInterceptor.onLoadRequest(mockEngineSession, webUrlWithAppLink, null, true, false, false, false, false) + assertEquals(null, response) + } + + @Test + fun `request is not intercepted when launchInApp preference is false`() { + appLinksInterceptor = AppLinksInterceptor( + context = mockContext, + interceptLinkClicks = true, + launchInApp = { false }, + useCases = mockUseCases, + ) + + val response = appLinksInterceptor.onLoadRequest(mockEngineSession, webUrlWithAppLink, null, true, false, false, false, false) + assertEquals(null, response) + } + + @Test + fun `request is not intercepted when launchInApp preference is updated to false`() { + appLinksInterceptor = AppLinksInterceptor( + context = mockContext, + interceptLinkClicks = true, + launchInApp = { false }, + useCases = mockUseCases, + ) + + val response = appLinksInterceptor.onLoadRequest(mockEngineSession, webUrlWithAppLink, null, true, false, false, false, false) + assertEquals(null, response) + + appLinksInterceptor.updateLaunchInApp { true } + verify(mockUseCases).updateLaunchInApp(any()) + val response2 = appLinksInterceptor.onLoadRequest(mockEngineSession, webUrlWithAppLink, null, true, false, false, false, false) + assert(response2 is RequestInterceptor.InterceptionResponse.AppIntent) + } + + @Test + fun `request is not intercepted when not user clicking on a link`() { + val response = appLinksInterceptor.onLoadRequest(mockEngineSession, webUrlWithAppLink, null, false, false, false, false, false) + assertEquals(null, response) + } + + @Test + fun `request is not intercepted if the current session is already on the same host`() { + val response = appLinksInterceptor.onLoadRequest(mockEngineSession, webUrlWithAppLink, webUrlWithAppLink, true, true, false, false, false) + assertEquals(null, response) + } + + @Test + fun `request is not intercepted by a redirect on same domain`() { + val response = appLinksInterceptor.onLoadRequest(mockEngineSession, webUrlWithAppLink, webUrlWithAppLink, true, true, true, false, false) + assertEquals(null, response) + } + + @Test + fun `domain is stripped before checking`() { + var response = appLinksInterceptor.onLoadRequest(mockEngineSession, "http://example.com", "example.com", true, true, true, false, false) + assertEquals(null, response) + + response = appLinksInterceptor.onLoadRequest(mockEngineSession, "https://example.com", "http://example.com", true, true, true, false, false) + assertEquals(null, response) + + response = appLinksInterceptor.onLoadRequest(mockEngineSession, "https://www.example.com", "http://example.com", true, true, true, false, false) + assertEquals(null, response) + + response = appLinksInterceptor.onLoadRequest(mockEngineSession, "http://www.example.com", "https://www.example.com", true, true, true, false, false) + assertEquals(null, response) + + response = appLinksInterceptor.onLoadRequest(mockEngineSession, "http://m.example.com", "https://www.example.com", true, true, true, false, false) + assertEquals(null, response) + + response = appLinksInterceptor.onLoadRequest(mockEngineSession, "http://mobile.example.com", "http://m.example.com", true, true, true, false, false) + assertEquals(null, response) + } + + @Test + fun `request is not intercepted if a subframe request and not triggered by user`() { + val response = appLinksInterceptor.onLoadRequest(mockEngineSession, webUrlWithAppLink, null, false, false, false, true, true) + assertEquals(null, response) + } + + @Test + fun `request is not intercepted if not user gesture, not redirect and not direct navigation`() { + val response = appLinksInterceptor.onLoadRequest(mockEngineSession, webUrlWithAppLink, null, false, false, false, false, false) + assertEquals(null, response) + } + + @Test + fun `block listed schemes request not intercepted when triggered by user clicking on a link`() { + val engineSession: EngineSession = mock() + val blocklistedScheme = "blocklisted" + val feature = AppLinksInterceptor( + context = mockContext, + interceptLinkClicks = true, + alwaysDeniedSchemes = setOf(blocklistedScheme), + launchInApp = { true }, + useCases = mockUseCases, + ) + + val blocklistedUrl = "$blocklistedScheme://example.com" + val blocklistedRedirect = AppLinkRedirect(Intent.parseUri(blocklistedUrl, 0), blocklistedUrl, null) + whenever(mockGetRedirect.invoke(blocklistedUrl)).thenReturn(blocklistedRedirect) + var response = feature.onLoadRequest(engineSession, blocklistedUrl, null, true, false, false, false, false) + assertEquals(null, response) + } + + @Test + fun `supported schemes request not launched if launchInApp is false`() { + val engineSession: EngineSession = mock() + val supportedScheme = "supported" + val feature = AppLinksInterceptor( + context = mockContext, + interceptLinkClicks = true, + engineSupportedSchemes = setOf(supportedScheme), + launchInApp = { false }, + useCases = mockUseCases, + ) + + val supportedUrl = "$supportedScheme://example.com" + val supportedRedirect = AppLinkRedirect(Intent.parseUri(supportedUrl, 0), null, null) + whenever(mockGetRedirect.invoke(supportedUrl)).thenReturn(supportedRedirect) + val response = feature.onLoadRequest(engineSession, supportedUrl, null, true, false, false, false, false) + assertEquals(null, response) + } + + @Test + fun `supported schemes request not launched if interceptLinkClicks is false`() { + val engineSession: EngineSession = mock() + val supportedScheme = "supported" + val feature = AppLinksInterceptor( + context = mockContext, + interceptLinkClicks = false, + engineSupportedSchemes = setOf(supportedScheme), + launchInApp = { true }, + useCases = mockUseCases, + ) + + val supportedUrl = "$supportedScheme://example.com" + val supportedRedirect = AppLinkRedirect(Intent.parseUri(supportedUrl, 0), null, null) + whenever(mockGetRedirect.invoke(supportedUrl)).thenReturn(supportedRedirect) + val response = feature.onLoadRequest(engineSession, supportedUrl, null, true, false, false, false, false) + assertEquals(null, response) + } + + @Test + fun `supported schemes request not launched if not triggered by user`() { + val engineSession: EngineSession = mock() + val supportedScheme = "supported" + val feature = AppLinksInterceptor( + context = mockContext, + interceptLinkClicks = true, + engineSupportedSchemes = setOf(supportedScheme), + launchInApp = { true }, + useCases = mockUseCases, + ) + + val supportedUrl = "$supportedScheme://example.com" + val supportedRedirect = AppLinkRedirect(Intent.parseUri(supportedUrl, 0), null, null) + whenever(mockGetRedirect.invoke(supportedUrl)).thenReturn(supportedRedirect) + val response = feature.onLoadRequest(engineSession, supportedUrl, null, false, false, false, false, false) + assertEquals(null, response) + } + + @Test + fun `not supported schemes request always intercepted regardless of hasUserGesture, interceptLinkClicks or launchInApp`() { + val engineSession: EngineSession = mock() + val supportedScheme = "supported" + val notSupportedScheme = "not_supported" + val blocklistedScheme = "blocklisted" + val feature = AppLinksInterceptor( + context = mockContext, + interceptLinkClicks = false, + engineSupportedSchemes = setOf(supportedScheme), + alwaysDeniedSchemes = setOf(blocklistedScheme), + launchInApp = { false }, + useCases = mockUseCases, + ) + + val notSupportedUrl = "$notSupportedScheme://example.com" + val notSupportedRedirect = AppLinkRedirect(Intent.parseUri(notSupportedUrl, 0), null, null) + whenever(mockGetRedirect.invoke(notSupportedUrl)).thenReturn(notSupportedRedirect) + val response = feature.onLoadRequest(engineSession, notSupportedUrl, null, false, false, false, false, false) + assert(response is RequestInterceptor.InterceptionResponse.AppIntent) + } + + @Test + fun `blocklisted schemes request always ignored even if the engine does not support it`() { + val engineSession: EngineSession = mock() + val supportedScheme = "supported" + val notSupportedScheme = "not_supported" + val feature = AppLinksInterceptor( + context = mockContext, + interceptLinkClicks = false, + engineSupportedSchemes = setOf(supportedScheme), + alwaysDeniedSchemes = setOf(notSupportedScheme), + launchInApp = { false }, + useCases = mockUseCases, + ) + + val notSupportedUrl = "$notSupportedScheme://example.com" + val notSupportedRedirect = AppLinkRedirect(Intent.parseUri(notSupportedUrl, 0), null, null) + whenever(mockGetRedirect.invoke(notSupportedUrl)).thenReturn(notSupportedRedirect) + val response = feature.onLoadRequest(engineSession, notSupportedUrl, null, false, false, false, false, false) + assertEquals(null, response) + } + + @Test + fun `not supported schemes request should not use fallback if user preference is launch in app`() { + val engineSession: EngineSession = mock() + val supportedScheme = "supported" + val notSupportedScheme = "not_supported" + val blocklistedScheme = "blocklisted" + val feature = AppLinksInterceptor( + context = mockContext, + interceptLinkClicks = false, + engineSupportedSchemes = setOf(supportedScheme), + alwaysDeniedSchemes = setOf(blocklistedScheme), + launchInApp = { true }, + useCases = mockUseCases, + ) + + val notSupportedUrl = "$notSupportedScheme://example.com" + val notSupportedRedirect = AppLinkRedirect(Intent.parseUri(notSupportedUrl, 0), fallbackUrl, null) + whenever(mockGetRedirect.invoke(notSupportedUrl)).thenReturn(notSupportedRedirect) + val response = feature.onLoadRequest(engineSession, notSupportedUrl, null, false, false, false, false, false) + assert(response is RequestInterceptor.InterceptionResponse.AppIntent) + } + + @Test + fun `not supported schemes request uses fallback URL if available and launchInApp is set to false`() { + val engineSession: EngineSession = mock() + val supportedScheme = "supported" + val notSupportedScheme = "not_supported" + val blocklistedScheme = "blocklisted" + val feature = AppLinksInterceptor( + context = mockContext, + interceptLinkClicks = true, + engineSupportedSchemes = setOf(supportedScheme), + alwaysDeniedSchemes = setOf(blocklistedScheme), + launchInApp = { false }, + useCases = mockUseCases, + ) + + val notSupportedUrl = "$notSupportedScheme://example.com" + val fallbackUrl = "https://example.com" + val notSupportedRedirect = AppLinkRedirect(Intent.parseUri(notSupportedUrl, 0), fallbackUrl, null) + whenever(mockGetRedirect.invoke(notSupportedUrl)).thenReturn(notSupportedRedirect) + val response = feature.onLoadRequest(engineSession, notSupportedUrl, null, true, false, false, false, false) + assert(response is RequestInterceptor.InterceptionResponse.Url) + } + + @Test + fun `not supported schemes request uses fallback URL not market intent if launchInApp is set to false`() { + val engineSession: EngineSession = mock() + val supportedScheme = "supported" + val notSupportedScheme = "not_supported" + val blocklistedScheme = "blocklisted" + val feature = AppLinksInterceptor( + context = mockContext, + interceptLinkClicks = true, + engineSupportedSchemes = setOf(supportedScheme), + alwaysDeniedSchemes = setOf(blocklistedScheme), + launchInApp = { false }, + useCases = mockUseCases, + ) + + val notSupportedUrl = "$notSupportedScheme://example.com" + val fallbackUrl = "https://example.com" + val notSupportedRedirect = AppLinkRedirect(null, fallbackUrl, Intent.parseUri(marketplaceUrl, 0)) + whenever(mockGetRedirect.invoke(notSupportedUrl)).thenReturn(notSupportedRedirect) + val response = feature.onLoadRequest(engineSession, notSupportedUrl, null, true, false, false, false, false) + assert(response is RequestInterceptor.InterceptionResponse.Url) + } + + @Test + fun `intent scheme launch intent if fallback URL is unavailable and launchInApp is set to false`() { + val engineSession: EngineSession = mock() + val feature = AppLinksInterceptor( + context = mockContext, + interceptLinkClicks = false, + launchInApp = { false }, + useCases = mockUseCases, + ) + + val intentUrl = "intent://example.com" + val intentRedirect = AppLinkRedirect(Intent.parseUri(intentUrl, 0), null, null) + whenever(mockGetRedirect.invoke(intentUrl)).thenReturn(intentRedirect) + val response = feature.onLoadRequest(engineSession, intentUrl, null, true, false, false, false, false) + assert(response is RequestInterceptor.InterceptionResponse.AppIntent) + } + + @Test + fun `intent scheme uses fallback URL if available and launchInApp is set to false`() { + val engineSession: EngineSession = mock() + val feature = AppLinksInterceptor( + context = mockContext, + interceptLinkClicks = false, + launchInApp = { false }, + useCases = mockUseCases, + ) + + val intentUrl = "intent://example.com" + val fallbackUrl = "https://example.com" + val intentRedirect = AppLinkRedirect(Intent.parseUri(intentUrl, 0), fallbackUrl, null) + whenever(mockGetRedirect.invoke(intentUrl)).thenReturn(intentRedirect) + val response = feature.onLoadRequest(engineSession, intentUrl, null, true, false, false, false, false) + assert(response is RequestInterceptor.InterceptionResponse.Url) + } + + @Test + fun `request is not intercepted for URLs with javascript scheme`() { + val javascriptUri = "javascript:;" + + val appRedirect = AppLinkRedirect(Intent.parseUri(javascriptUri, 0), null, null) + whenever(mockGetRedirect.invoke(javascriptUri)).thenReturn(appRedirect) + + val response = appLinksInterceptor.onLoadRequest(mockEngineSession, javascriptUri, null, true, true, false, false, false) + assertEquals(null, response) + } + + @Test + fun `Use the fallback URL when no non-browser app is installed`() { + val response = appLinksInterceptor.onLoadRequest(mockEngineSession, fallbackUrl, null, true, false, false, false, false) + assert(response is RequestInterceptor.InterceptionResponse.Url) + } + + @Test + fun `use the market intent if target app is not installed`() { + val response = appLinksInterceptor.onLoadRequest(mockEngineSession, marketplaceUrl, null, true, false, false, false, false) + assert(response is RequestInterceptor.InterceptionResponse.AppIntent) + } + + @Test + fun `external app is launched when launch in app is set to true and it is user triggered`() { + appLinksInterceptor = AppLinksInterceptor( + context = mockContext, + interceptLinkClicks = true, + launchInApp = { true }, + useCases = mockUseCases, + launchFromInterceptor = true, + ) + + val response = appLinksInterceptor.onLoadRequest(mockEngineSession, webUrlWithAppLink, null, true, false, false, false, false) + assert(response is RequestInterceptor.InterceptionResponse.AppIntent) + verify(mockOpenRedirect).invoke(any(), anyBoolean(), any()) + } + + @Test + fun `try to use fallback url if user preference is not to launch in third party app`() { + appLinksInterceptor = AppLinksInterceptor( + context = mockContext, + interceptLinkClicks = true, + launchInApp = { false }, + useCases = mockUseCases, + launchFromInterceptor = true, + ) + + val testRedirect = AppLinkRedirect(Intent.parseUri(intentUrl, 0), fallbackUrl, null) + val response = appLinksInterceptor.handleRedirect(testRedirect, intentUrl, true) + assert(response is RequestInterceptor.InterceptionResponse.Url) + } + + @Test + fun `external app is launched when url scheme is not supported by the engine`() { + appLinksInterceptor = AppLinksInterceptor( + context = mockContext, + interceptLinkClicks = true, + launchInApp = { false }, + useCases = mockUseCases, + launchFromInterceptor = true, + ) + + val response = appLinksInterceptor.onLoadRequest(mockEngineSession, intentUrl, null, false, true, false, false, false) + assert(response is RequestInterceptor.InterceptionResponse.AppIntent) + verify(mockOpenRedirect).invoke(any(), anyBoolean(), any()) + } + + @Test + fun `do not use fallback url if trigger by user gesture and preference is to launch in app`() { + appLinksInterceptor = AppLinksInterceptor( + context = mockContext, + interceptLinkClicks = true, + launchInApp = { true }, + useCases = mockUseCases, + launchFromInterceptor = true, + ) + + val testRedirect = AppLinkRedirect(Intent.parseUri(intentUrl, 0), fallbackUrl, null) + val response = appLinksInterceptor.handleRedirect(testRedirect, intentUrl, true) + assert(response is RequestInterceptor.InterceptionResponse.AppIntent) + } + + @Test + fun `launch marketplace intent if available and no external app`() { + appLinksInterceptor = AppLinksInterceptor( + context = mockContext, + interceptLinkClicks = true, + launchInApp = { true }, + useCases = mockUseCases, + launchFromInterceptor = true, + ) + + val testRedirect = AppLinkRedirect(null, fallbackUrl, Intent.parseUri(marketplaceUrl, 0)) + val response = appLinksInterceptor.handleRedirect(testRedirect, webUrl, true) + assert(response is RequestInterceptor.InterceptionResponse.AppIntent) + } + + @Test + fun `use fallback url if available and no external app`() { + appLinksInterceptor = AppLinksInterceptor( + context = mockContext, + interceptLinkClicks = true, + launchInApp = { true }, + useCases = mockUseCases, + launchFromInterceptor = true, + ) + + val testRedirect = AppLinkRedirect(null, fallbackUrl, null) + val response = appLinksInterceptor.handleRedirect(testRedirect, webUrl, true) + assert(response is RequestInterceptor.InterceptionResponse.Url) + } + + @Test + fun `WHEN url have same domain THEN is same domain returns true ELSE false`() { + appLinksInterceptor = AppLinksInterceptor( + context = mockContext, + interceptLinkClicks = true, + launchInApp = { true }, + useCases = mockUseCases, + launchFromInterceptor = true, + ) + + assert(appLinksInterceptor.isSameDomain("maps.google.com", "www.google.com")) + assert(appLinksInterceptor.isSameDomain("mobile.mozilla.com", "www.mozilla.com")) + assert(appLinksInterceptor.isSameDomain("m.mozilla.com", "maps.mozilla.com")) + + assertFalse(appLinksInterceptor.isSameDomain("www.google.ca", "www.google.com")) + assertFalse(appLinksInterceptor.isSameDomain("maps.google.ca", "m.google.com")) + assertFalse(appLinksInterceptor.isSameDomain("accounts.google.com", "www.google.com")) + } + + @Test + fun `WHEN request is in user do not intercept cache THEN request is not intercepted`() { + appLinksInterceptor = AppLinksInterceptor( + context = mockContext, + interceptLinkClicks = true, + launchInApp = { true }, + useCases = mockUseCases, + ) + + addUserDoNotIntercept("https://soundcloud.com", null) + + val response = appLinksInterceptor.onLoadRequest(mockEngineSession, webUrlWithAppLink, null, true, false, false, false, false) + assertNull(response) + } + + @Test + fun `WHEN request is in user do not intercept cache but there is a fallback THEN fallback is used`() { + appLinksInterceptor = AppLinksInterceptor( + context = mockContext, + interceptLinkClicks = true, + launchInApp = { false }, + useCases = mockUseCases, + launchFromInterceptor = true, + ) + + addUserDoNotIntercept(intentUrl, null) + val testRedirect = AppLinkRedirect(Intent.parseUri(intentUrl, 0), fallbackUrl, null) + val response = appLinksInterceptor.handleRedirect(testRedirect, intentUrl, true) + assert(response is RequestInterceptor.InterceptionResponse.Url) + } + + @Test + fun `WHEN request is in user do not intercept cache but engine doesn't support scheme THEN request is intercepted`() { + val engineSession: EngineSession = mock() + val supportedScheme = "supported" + val notSupportedScheme = "not_supported" + val blocklistedScheme = "blocklisted" + val feature = AppLinksInterceptor( + context = mockContext, + interceptLinkClicks = false, + engineSupportedSchemes = setOf(supportedScheme), + alwaysDeniedSchemes = setOf(blocklistedScheme), + launchInApp = { true }, + useCases = mockUseCases, + ) + + val notSupportedUrl = "$notSupportedScheme://example.com" + addUserDoNotIntercept(notSupportedUrl, null) + val notSupportedRedirect = AppLinkRedirect(Intent.parseUri(notSupportedUrl, 0), null, null) + whenever(mockGetRedirect.invoke(notSupportedUrl)).thenReturn(notSupportedRedirect) + val response = feature.onLoadRequest(engineSession, notSupportedUrl, null, false, false, false, false, false) + assert(response is RequestInterceptor.InterceptionResponse.AppIntent) + } + + @Test + fun `WHEN added to user do not open cache THEN return true if user do no intercept cache exists`() { + addUserDoNotIntercept("test://test.com", null) + assertTrue(inUserDoNotIntercept("test://test.com", null)) + assertFalse(inUserDoNotIntercept("https://test.com", null)) + + addUserDoNotIntercept("http://test.com", null) + assertTrue(inUserDoNotIntercept("https://test.com", null)) + assertFalse(inUserDoNotIntercept("https://example.com", null)) + + val testIntent: Intent = mock() + val componentName: ComponentName = mock() + doReturn(componentName).`when`(testIntent).component + doReturn("app.example.com").`when`(componentName).packageName + + addUserDoNotIntercept("https://example.com", testIntent) + assertTrue(inUserDoNotIntercept("https://example.com", testIntent)) + assertTrue(inUserDoNotIntercept("https://test.com", testIntent)) + + doReturn("app.test.com").`when`(componentName).packageName + assertFalse(inUserDoNotIntercept("https://test.com", testIntent)) + assertFalse(inUserDoNotIntercept("https://mozilla.org", null)) + } + + @Test + fun `WHEN user do not open cache expires THEN return false`() { + val testIntent: Intent = mock() + val componentName: ComponentName = mock() + doReturn(componentName).`when`(testIntent).component + doReturn("app.example.com").`when`(componentName).packageName + + addUserDoNotIntercept("https://example.com", testIntent) + assertTrue(inUserDoNotIntercept("https://example.com", testIntent)) + assertTrue(inUserDoNotIntercept("https://test.com", testIntent)) + + userDoNotInterceptCache["app.example.com".hashCode()] = -APP_LINKS_DO_NOT_OPEN_CACHE_INTERVAL + assertFalse(inUserDoNotIntercept("https://example.com", testIntent)) + assertFalse(inUserDoNotIntercept("https://test.com", testIntent)) + } + + @Test + fun `WHEN request is redirecting to external app quickly THEN request is not intercepted`() { + appLinksInterceptor = AppLinksInterceptor( + context = mockContext, + interceptLinkClicks = true, + launchInApp = { true }, + useCases = mockUseCases, + ) + + var response = appLinksInterceptor.onLoadRequest(mockEngineSession, webUrlWithAppLink, null, true, false, false, false, false) + assertTrue(response is RequestInterceptor.InterceptionResponse.AppIntent) + + response = appLinksInterceptor.onLoadRequest(mockEngineSession, webUrlWithAppLink, null, true, false, false, false, false) + assertNull(response) + } + + @Test + fun `WHEN request is redirecting to different app quickly THEN request is intercepted`() { + appLinksInterceptor = AppLinksInterceptor( + context = mockContext, + interceptLinkClicks = true, + launchInApp = { true }, + useCases = mockUseCases, + ) + + var response = appLinksInterceptor.onLoadRequest(mockEngineSession, webUrl, null, true, false, false, false, false) + assert(response is RequestInterceptor.InterceptionResponse.Url) + + response = appLinksInterceptor.onLoadRequest(mockEngineSession, webUrlWithAppLink, null, true, false, false, false, false) + assertTrue(response is RequestInterceptor.InterceptionResponse.AppIntent) + } +} diff --git a/mobile/android/android-components/components/feature/app-links/src/test/java/mozilla/components/feature/app/links/AppLinksUseCasesTest.kt b/mobile/android/android-components/components/feature/app-links/src/test/java/mozilla/components/feature/app/links/AppLinksUseCasesTest.kt new file mode 100644 index 0000000000..12348433c3 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/test/java/mozilla/components/feature/app/links/AppLinksUseCasesTest.kt @@ -0,0 +1,671 @@ +/* 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 mozilla.components.feature.app.links + +import android.content.ActivityNotFoundException +import android.content.Context +import android.content.Intent +import android.content.pm.ActivityInfo +import android.content.pm.PackageInfo +import android.content.pm.ResolveInfo +import android.net.Uri +import androidx.test.ext.junit.runners.AndroidJUnit4 +import mozilla.components.support.test.mock +import mozilla.components.support.test.robolectric.testContext +import mozilla.components.support.test.whenever +import mozilla.components.support.utils.Browsers +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.any +import org.mockito.Mockito.never +import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` +import org.robolectric.Shadows.shadowOf +import java.io.File + +@RunWith(AndroidJUnit4::class) +class AppLinksUseCasesTest { + + private val appUrl = "https://example.com" + private val appIntent = "intent://example.com" + private val appSchemeIntent = "example://example.com" + private val appPackage = "com.example.app" + private val browserSchemeUrl = "browser://test" + private val browserPackage = Browsers.KnownBrowser.ANDROID_STOCK_BROWSER.packageName + private val testBrowserPackage = "com.current.browser" + private val filePath = "file:///storage/abc/test.mp3" + private val dataUrl = "data:text/plain;base64,SGVsbG8sIFdvcmxkIQ==" + private val aboutUrl = "about:config" + private val javascriptUrl = "javascript:'hello, world'" + private val jarUrl = "jar:file://some/path/test.html" + private val contentUrl = "content://media/external_primary/downloads/12345" + private val fileType = "audio/mpeg" + private val layerUrl = "https://example.com" + private val layerPackage = "com.example.app" + private val layerActivity = "com.example2.app.intentActivity" + private val appIntentWithPackageAndFallback = + "intent://com.example.app#Intent;package=com.example.com;S.browser_fallback_url=https://example.com;end" + + @Before + fun setup() { + AppLinksUseCases.redirectCache = null + } + + private fun createContext( + vararg urlToPackages: Triple<String, String, String>, + default: Boolean = false, + installedApps: List<String> = emptyList(), + ): Context { + val pm = testContext.packageManager + val packageManager = shadowOf(pm) + + urlToPackages.forEach { (urlString, pkgName, className) -> + val intent = Intent.parseUri(urlString, 0).addCategory(Intent.CATEGORY_BROWSABLE) + + val info = ActivityInfo().apply { + packageName = pkgName + name = className + icon = android.R.drawable.btn_default + } + + val resolveInfo = ResolveInfo().apply { + labelRes = android.R.string.ok + activityInfo = info + } + @Suppress("DEPRECATION") // Deprecation will be handled in https://github.com/mozilla-mobile/android-components/issues/11832 + packageManager.addResolveInfoForIntent(intent, resolveInfo) + packageManager.addDrawableResolution(pkgName, android.R.drawable.btn_default, mock()) + } + + val context = mock<Context>() + `when`(context.packageManager).thenReturn(pm) + if (!default) { + `when`(context.packageName).thenReturn(testBrowserPackage) + } + + installedApps.forEach { name -> + val packageInfo = PackageInfo().apply { + packageName = name + } + packageManager.addPackageNoDefaults(packageInfo) + } + + return context + } + + @Test + fun `WHEN receiving a malformed URL THEN will not cause a crash`() { + val context = createContext() + val subject = AppLinksUseCases(context, { true }) + val redirect = subject.interceptedAppLinkRedirect("test://test#Intent;") + assertFalse(redirect.isRedirect()) + } + + @Test + fun `A URL that matches app with activity is an app link with correct component`() { + val context = createContext(Triple(layerUrl, layerPackage, layerActivity)) + val subject = AppLinksUseCases(context, { true }) + + val redirect = subject.interceptedAppLinkRedirect(layerUrl) + assertTrue(redirect.isRedirect()) + assertEquals(redirect.appIntent?.component?.packageName, layerPackage) + assertEquals(redirect.appIntent?.component?.className, layerActivity) + } + + @Test + fun `A URL that matches zero apps is not an app link`() { + val context = createContext() + val subject = AppLinksUseCases(context, { true }) + + val redirect = subject.interceptedAppLinkRedirect(appUrl) + assertFalse(redirect.isRedirect()) + } + + @Test + fun `A web URL that matches more than zero apps is an app link`() { + val context = createContext(Triple(appUrl, appPackage, "")) + val subject = AppLinksUseCases(context, { true }) + + // We will redirect to it if browser option set to true. + val redirect = subject.interceptedAppLinkRedirect(appUrl) + assertTrue(redirect.isRedirect()) + } + + @Test + fun `A intent that targets a specific package but installed will not uses market intent`() { + val context = createContext(installedApps = listOf("com.example.com")) + val subject = AppLinksUseCases(context, { true }) + + val redirect = subject.interceptedAppLinkRedirect(appIntentWithPackageAndFallback) + assertFalse(redirect.hasMarketplaceIntent()) + assertTrue(redirect.hasFallback()) + } + + @Test + fun `A intent that targets a specific package but not installed will uses market intent`() { + val context = createContext() + val subject = AppLinksUseCases(context, { true }) + + val redirect = subject.interceptedAppLinkRedirect(appIntentWithPackageAndFallback) + assertFalse(redirect.hasExternalApp()) + assertTrue(redirect.hasMarketplaceIntent()) + assertTrue(redirect.hasFallback()) + } + + @Test + fun `A file is not an app link`() { + val context = createContext(Triple(filePath, appPackage, "")) + val subject = AppLinksUseCases(context, { true }) + + // We will redirect to it if browser option set to true. + val redirect = subject.interceptedAppLinkRedirect(filePath) + assertFalse(redirect.isRedirect()) + } + + @Test + fun `A data url is not an app link`() { + val context = createContext(Triple(dataUrl, appPackage, "")) + val subject = AppLinksUseCases(context, { true }) + + val redirect = subject.interceptedAppLinkRedirect(dataUrl) + assertFalse(redirect.isRedirect()) + } + + @Test + fun `A javascript url is not an app link`() { + val context = createContext(Triple(javascriptUrl, appPackage, "")) + val subject = AppLinksUseCases(context, { true }) + + val redirect = subject.interceptedAppLinkRedirect(javascriptUrl) + assertFalse(redirect.isRedirect()) + } + + @Test + fun `An about url is not an app link`() { + val context = createContext(Triple(aboutUrl, appPackage, "")) + val subject = AppLinksUseCases(context, { true }) + + val redirect = subject.interceptedAppLinkRedirect(aboutUrl) + assertFalse(redirect.isRedirect()) + } + + @Test + fun `A jar url is not an app link`() { + val context = createContext(Triple(jarUrl, appPackage, "")) + val subject = AppLinksUseCases(context, { true }) + + val redirect = subject.interceptedAppLinkRedirect(jarUrl) + assertFalse(redirect.isRedirect()) + } + + @Test + fun `A content url is not an app link`() { + val context = createContext(Triple(contentUrl, appPackage, "")) + val subject = AppLinksUseCases(context, { true }) + + val redirect = subject.interceptedAppLinkRedirect(contentUrl) + assertFalse(redirect.isRedirect()) + } + + @Test + fun `Will not redirect app link if browser option set to false and scheme is supported`() { + val context = createContext(Triple(appUrl, appPackage, "")) + val subject = AppLinksUseCases(context, { false }) + + val redirect = subject.interceptedAppLinkRedirect(appUrl) + assertFalse(redirect.isRedirect()) + + val menuRedirect = subject.appLinkRedirect(appUrl) + assertTrue(menuRedirect.isRedirect()) + } + + @Test + fun `Will redirect app link if browser option set to false and scheme is not supported`() { + val context = createContext(Triple(appIntent, appPackage, "")) + val subject = AppLinksUseCases(context, { false }) + + val redirect = subject.interceptedAppLinkRedirect(appIntent) + assertTrue(redirect.isRedirect()) + + val menuRedirect = subject.appLinkRedirect(appIntent) + assertTrue(menuRedirect.isRedirect()) + } + + @Test + fun `WHEN A URL that matches a browser AND the scheme is not supported THEN is an app link`() { + val context = createContext(Triple(browserSchemeUrl, browserPackage, "")) + val browsers: Browsers = mock() + whenever(browsers.isInstalled(browserPackage)).thenReturn(true) + val subject = AppLinksUseCases(context = context, launchInApp = { true }, installedBrowsers = browsers) + + val redirect = subject.interceptedAppLinkRedirect(browserSchemeUrl) + assertTrue(redirect.isRedirect()) + + val menuRedirect = subject.appLinkRedirect(browserSchemeUrl) + assertTrue(menuRedirect.isRedirect()) + } + + @Test + fun `WHEN A URL that matches a browser AND the scheme is supported THEN is not an app link`() { + val context = createContext(Triple(appUrl, browserPackage, "")) + val browsers: Browsers = mock() + whenever(browsers.isInstalled(browserPackage)).thenReturn(true) + val subject = AppLinksUseCases(context = context, launchInApp = { true }, installedBrowsers = browsers) + + val redirect = subject.interceptedAppLinkRedirect(appUrl) + assertFalse(redirect.isRedirect()) + + val menuRedirect = subject.appLinkRedirect(appUrl) + assertFalse(menuRedirect.isRedirect()) + } + + @Test + fun `A intent scheme uri with an installed app is an app link`() { + val uri = "intent://scan/#Intent;scheme=zxing;package=com.google.zxing.client.android;end" + val context = createContext(Triple(uri, appPackage, "")) + val subject = AppLinksUseCases(context, { true }) + + val redirect = subject.interceptedAppLinkRedirect(uri) + assertTrue(redirect.hasExternalApp()) + assertNotNull(redirect.appIntent) + assertNotNull(redirect.marketplaceIntent) + + assertEquals("zxing://scan/", redirect.appIntent!!.dataString) + } + + @Test + fun `A bad intent scheme uri should not cause a crash`() { + val uri = "intent://blank#Intent;package=com.twitter.android%23Intent%3B;end" + val context = createContext(Triple(uri, appPackage, "")) + val subject = AppLinksUseCases(context, { true }) + + val redirect = subject.appLinkRedirectIncludeInstall.invoke(uri) + + assertTrue(redirect.hasExternalApp()) + assertFalse(redirect.isInstallable()) + } + + @Test + fun `A market scheme uri with no installed app is an install link`() { + val uri = "intent://details/#Intent;scheme=market;package=com.google.play;end" + val context = createContext(Triple(uri, appPackage, "")) + val subject = AppLinksUseCases(context, { true }) + + val redirect = subject.interceptedAppLinkRedirect.invoke(uri) + + assertTrue(redirect.hasExternalApp()) + assertTrue(redirect.isInstallable()) + assert( + redirect.marketplaceIntent!!.flags and Intent.FLAG_ACTIVITY_NEW_TASK + == Intent.FLAG_ACTIVITY_NEW_TASK, + ) + } + + @Test + fun `A intent scheme uri without an installed app is not an app link`() { + val uri = "intent://scan/#Intent;scheme=zxing;package=com.google.zxing.client.android;end" + val context = createContext() + val subject = AppLinksUseCases(context, { true }) + + val redirect = subject.interceptedAppLinkRedirect(uri) + assertFalse(redirect.hasExternalApp()) + assertFalse(redirect.hasFallback()) + assertNull(redirect.fallbackUrl) + assertFalse(redirect.isInstallable()) + } + + @Test + fun `A intent scheme uri with a fallback without an installed app is not an app link`() { + val uri = + "intent://scan/#Intent;scheme=zxing;package=com.google.zxing.client.android;S.browser_fallback_url=http%3A%2F%2Fzxing.org;end" + val context = createContext() + val subject = AppLinksUseCases(context, { true }) + + val redirect = subject.interceptedAppLinkRedirect(uri) + assertFalse(redirect.hasExternalApp()) + assertTrue(redirect.hasFallback()) + + assertEquals("http://zxing.org", redirect.fallbackUrl) + } + + @Test + fun `A intent scheme denied should return no app intent`() { + val uri = "intent://details/#Intent" + val context = createContext(Triple(uri, appPackage, "")) + val subject = AppLinksUseCases(context, { true }, alwaysDeniedSchemes = setOf("intent")) + + val redirect = subject.interceptedAppLinkRedirect.invoke(uri) + + assertNull(redirect.appIntent) + assertFalse(redirect.hasExternalApp()) + } + + @Test + fun `An openAppLink use case starts an activity`() { + val context = createContext() + val appIntent = Intent() + val redirect = AppLinkRedirect(appIntent, appUrl, null) + val subject = AppLinksUseCases(context, { true }) + + subject.openAppLink(redirect.appIntent) + + verify(context).startActivity(any()) + } + + @Test + fun `Start activity fails will perform failure action`() { + val context = createContext() + val appIntent = Intent() + appIntent.putExtra(EXTRA_BROWSER_FALLBACK_URL, appUrl) + val redirect = AppLinkRedirect(appIntent, appUrl, null) + val subject = AppLinksUseCases(context, { true }) + + var failedToLaunch: String? = null + val failedAction = { fallbackUrl: String? -> failedToLaunch = fallbackUrl } + `when`(context.startActivity(any())).thenThrow(ActivityNotFoundException("failed")) + subject.openAppLink(redirect.appIntent, failedToLaunchAction = failedAction) + + verify(context).startActivity(any()) + assertEquals(failedToLaunch, appUrl) + } + + @Test + fun `Security exception perform failure action`() { + val context = createContext() + val appIntent = Intent() + appIntent.putExtra(EXTRA_BROWSER_FALLBACK_URL, appUrl) + val redirect = AppLinkRedirect(appIntent, appUrl, null) + val subject = AppLinksUseCases(context, { true }) + + var failedToLaunch: String? = null + val failedAction = { fallbackUrl: String? -> failedToLaunch = fallbackUrl } + `when`(context.startActivity(any())).thenThrow(SecurityException("failed")) + subject.openAppLink(redirect.appIntent, failedToLaunchAction = failedAction) + + verify(context).startActivity(any()) + assertEquals(failedToLaunch, appUrl) + } + + @Test + fun `Null pointer exception perform failure action`() { + val context = createContext() + val appIntent = Intent() + appIntent.putExtra(EXTRA_BROWSER_FALLBACK_URL, appUrl) + val redirect = AppLinkRedirect(appIntent, appUrl, null) + val subject = AppLinksUseCases(context, { true }) + + var failedToLaunch: String? = null + val failedAction = { fallbackUrl: String? -> failedToLaunch = fallbackUrl } + `when`(context.startActivity(any())).thenThrow(NullPointerException("failed")) + subject.openAppLink(redirect.appIntent, failedToLaunchAction = failedAction) + + verify(context).startActivity(any()) + assertEquals(failedToLaunch, appUrl) + } + + @Test + fun `AppLinksUsecases uses cache`() { + val context = createContext(Triple(appUrl, appPackage, "")) + + var subject = AppLinksUseCases(context, { true }) + var redirect = subject.interceptedAppLinkRedirect(appUrl) + assertTrue(redirect.isRedirect()) + val timestamp = AppLinksUseCases.redirectCache?.cacheTimeStamp + + subject = AppLinksUseCases(context, { true }) + redirect = subject.interceptedAppLinkRedirect(appUrl) + assertTrue(redirect.isRedirect()) + assert(timestamp == AppLinksUseCases.redirectCache?.cacheTimeStamp) + + AppLinksUseCases.clearRedirectCache() + subject = AppLinksUseCases(context, { true }) + redirect = subject.interceptedAppLinkRedirect(appUrl) + assertTrue(redirect.isRedirect()) + } + + @Test + fun `OpenAppLinkRedirect should not try to open files`() { + val context = createContext() + val uri = Uri.fromFile(File(filePath)) + val intent = Intent(Intent.ACTION_VIEW) + intent.setDataAndType(uri, fileType) + val subject = AppLinksUseCases(context, { true }) + + subject.openAppLink(intent) + + verify(context, never()).startActivity(any()) + } + + @Test + fun `OpenAppLinkRedirect should not try to open data URIs`() { + val context = createContext() + val uri = Uri.parse(dataUrl) + val intent = Intent(Intent.ACTION_VIEW) + intent.setDataAndType(uri, fileType) + val subject = AppLinksUseCases(context, { true }) + + subject.openAppLink(intent) + + verify(context, never()).startActivity(any()) + } + + @Test + fun `OpenAppLinkRedirect should not try to open javascript URIs`() { + val context = createContext() + val uri = Uri.parse(javascriptUrl) + val intent = Intent(Intent.ACTION_VIEW) + intent.setDataAndType(uri, fileType) + val subject = AppLinksUseCases(context, { true }) + + subject.openAppLink(intent) + + verify(context, never()).startActivity(any()) + } + + @Test + fun `OpenAppLinkRedirect should not try to open about URIs`() { + val context = createContext() + val uri = Uri.parse(aboutUrl) + val intent = Intent(Intent.ACTION_VIEW) + intent.setDataAndType(uri, fileType) + val subject = AppLinksUseCases(context, { true }) + + subject.openAppLink(intent) + + verify(context, never()).startActivity(any()) + } + + @Test + fun `OpenAppLinkRedirect should not try to open jar URIs`() { + val context = createContext() + val uri = Uri.parse(jarUrl) + val intent = Intent(Intent.ACTION_VIEW) + intent.setDataAndType(uri, fileType) + val subject = AppLinksUseCases(context, { true }) + + subject.openAppLink(intent) + + verify(context, never()).startActivity(any()) + } + + @Test + fun `WHEN receiving a app scheme uri WITH target package THEN will have marketplace intent`() { + val context = createContext() + val uri = "intent://scan/#Intent;scheme=zxing;package=com.google.zxing.client.android;end" + var subject = AppLinksUseCases(context, { false }) + var redirect = subject.interceptedAppLinkRedirect(uri) + assertFalse(redirect.hasExternalApp()) + assertFalse(redirect.hasFallback()) + assertNotNull(redirect.marketplaceIntent) + assertNull(redirect.fallbackUrl) + + subject = AppLinksUseCases(context, { true }) + redirect = subject.interceptedAppLinkRedirect(uri) + assertFalse(redirect.hasExternalApp()) + assertFalse(redirect.hasFallback()) + assertNotNull(redirect.marketplaceIntent) + assertNull(redirect.fallbackUrl) + } + + @Test + fun `WHEN receiving a app scheme uri THEN should try to redirect`() { + val context = createContext(Triple(appSchemeIntent, appPackage, "")) + + var subject = AppLinksUseCases(context, { false }) + var redirect = subject.interceptedAppLinkRedirect(appSchemeIntent) + assertTrue(redirect.hasExternalApp()) + assertFalse(redirect.hasFallback()) + assertNull(redirect.marketplaceIntent) + assertNull(redirect.fallbackUrl) + assertTrue(redirect.appIntent?.flags?.and(Intent.FLAG_ACTIVITY_CLEAR_TASK) == 0) + + subject = AppLinksUseCases(context, { true }) + redirect = subject.interceptedAppLinkRedirect(appSchemeIntent) + assertTrue(redirect.hasExternalApp()) + assertFalse(redirect.hasFallback()) + assertNull(redirect.marketplaceIntent) + assertNull(redirect.fallbackUrl) + assertTrue(redirect.appIntent?.flags?.and(Intent.FLAG_ACTIVITY_CLEAR_TASK) == 0) + } + + @Test + fun `WHEN opening a app scheme uri WITH fallback URL THEN use fallback if needed`() { + val context = createContext(Triple(appIntentWithPackageAndFallback, appPackage, "")) + + var subject = AppLinksUseCases(context, { false }) + var redirect = subject.interceptedAppLinkRedirect(appIntentWithPackageAndFallback) + assertFalse(redirect.hasExternalApp()) + assertTrue(redirect.hasFallback()) + assertTrue(redirect.marketplaceIntent != null) + assertEquals(redirect.fallbackUrl, "https://example.com") + + AppLinksUseCases.clearRedirectCache() + subject = AppLinksUseCases(context, { true }) + redirect = subject.interceptedAppLinkRedirect(appIntentWithPackageAndFallback) + assertTrue(redirect.hasExternalApp()) + assertTrue(redirect.hasFallback()) + assertTrue(redirect.marketplaceIntent != null) + assertEquals(redirect.fallbackUrl, "https://example.com") + assertTrue(redirect.appIntent?.flags?.and(Intent.FLAG_ACTIVITY_CLEAR_TASK) == 0) + } + + @Test + fun `WHEN opening a app scheme uri THEN tries to redirect`() { + val context = createContext(Triple(appIntent, appPackage, "")) + + var subject = AppLinksUseCases(context, { false }) + var redirect = subject.interceptedAppLinkRedirect(appIntent) + assertTrue(redirect.hasExternalApp()) + assertFalse(redirect.hasFallback()) + assertNull(redirect.marketplaceIntent) + assertNull(redirect.fallbackUrl) + assertTrue(redirect.appIntent?.flags?.and(Intent.FLAG_ACTIVITY_CLEAR_TASK) == 0) + + subject = AppLinksUseCases(context, { true }) + redirect = subject.interceptedAppLinkRedirect(appIntent) + assertTrue(redirect.hasExternalApp()) + assertFalse(redirect.hasFallback()) + assertNull(redirect.marketplaceIntent) + assertNull(redirect.fallbackUrl) + assertTrue(redirect.appIntent?.flags?.and(Intent.FLAG_ACTIVITY_CLEAR_TASK) == 0) + } + + @Test + fun `WHEN opening a app scheme uri WITHOUT package installed THEN do not try to redirect`() { + val context = createContext() + + var subject = AppLinksUseCases(context, { false }) + var redirect = subject.interceptedAppLinkRedirect(appIntent) + assertFalse(redirect.hasExternalApp()) + assertFalse(redirect.hasFallback()) + assertNull(redirect.marketplaceIntent) + assertNull(redirect.fallbackUrl) + + subject = AppLinksUseCases(context, { true }) + redirect = subject.interceptedAppLinkRedirect(appIntent) + assertFalse(redirect.hasExternalApp()) + assertFalse(redirect.hasFallback()) + assertNull(redirect.marketplaceIntent) + assertNull(redirect.fallbackUrl) + } + + @Test + fun `WHEN opening a app scheme uri without a host WITH package installed THEN try to redirect`() { + val context = createContext(urlToPackages = arrayOf(Triple("my.scheme", appPackage, "")), default = true, installedApps = listOf(appPackage)) + + var subject = AppLinksUseCases(context, { false }) + var redirect = subject.interceptedAppLinkRedirect("my.scheme") + assertTrue(redirect.hasExternalApp()) + assertFalse(redirect.hasFallback()) + assertNull(redirect.marketplaceIntent) + assertNull(redirect.fallbackUrl) + assertTrue(redirect.appIntent?.flags?.and(Intent.FLAG_ACTIVITY_CLEAR_TASK) == 0) + + subject = AppLinksUseCases(context, { true }) + redirect = subject.interceptedAppLinkRedirect("my.scheme") + assertTrue(redirect.hasExternalApp()) + assertFalse(redirect.hasFallback()) + assertNull(redirect.marketplaceIntent) + assertNull(redirect.fallbackUrl) + assertTrue(redirect.appIntent?.flags?.and(Intent.FLAG_ACTIVITY_CLEAR_TASK) == 0) + } + + @Test + fun `Failed to parse uri should not cause a crash`() { + val context = createContext() + val subject = AppLinksUseCases(context, { true }) + var uri = "intent://blank#Intent;package=test" + var result = subject.safeParseUri(uri, 0) + + assertNull(result) + + uri = + "intent://blank#Intent;package=test;i.android.support.customtabs.extra.TOOLBAR_COLOR=2239095040;end" + result = subject.safeParseUri(uri, 0) + + assertNull(result) + } + + @Test + fun `Intent targeting same package should return null`() { + val context = createContext() + val subject = AppLinksUseCases(context, { true }) + val uri = "intent://blank#Intent;package=$testBrowserPackage;end" + val result = subject.safeParseUri(uri, 0) + + assertNull(result) + } + + @Test + fun `Intent targeting external package should not return null`() { + val context = createContext() + val subject = AppLinksUseCases(context, { true }) + val uri = "intent://blank#Intent;package=org.mozilla.test;end" + val result = subject.safeParseUri(uri, 0) + + assertNotNull(result) + assertEquals(result?.`package`, "org.mozilla.test") + } + + @Test + fun `WHEN launch in app is updated to true THEN should redirect`() { + val context = createContext(Triple(appUrl, appPackage, "")) + val subject = AppLinksUseCases(context, { false }) + + var redirect = subject.interceptedAppLinkRedirect(appUrl) + assertFalse(redirect.isRedirect()) + + AppLinksUseCases.clearRedirectCache() + subject.updateLaunchInApp { true } + redirect = subject.interceptedAppLinkRedirect(appUrl) + assertTrue(redirect.isRedirect()) + } +} diff --git a/mobile/android/android-components/components/feature/app-links/src/test/java/mozilla/components/feature/app/links/SimpleRedirectDialogFragmentTest.kt b/mobile/android/android-components/components/feature/app-links/src/test/java/mozilla/components/feature/app/links/SimpleRedirectDialogFragmentTest.kt new file mode 100644 index 0000000000..268e4df4d9 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/test/java/mozilla/components/feature/app/links/SimpleRedirectDialogFragmentTest.kt @@ -0,0 +1,113 @@ +/* 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 mozilla.components.feature.app.links + +import android.os.Looper.getMainLooper +import android.widget.Button +import androidx.fragment.app.FragmentManager +import androidx.fragment.app.FragmentTransaction +import androidx.test.ext.junit.runners.AndroidJUnit4 +import mozilla.components.support.test.mock +import mozilla.components.support.test.robolectric.testContext +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.doNothing +import org.mockito.Mockito.doReturn +import org.mockito.Mockito.spy +import org.robolectric.Shadows.shadowOf +import androidx.appcompat.R as appcompatR + +@RunWith(AndroidJUnit4::class) +class SimpleRedirectDialogFragmentTest { + private val webUrl = "https://example.com" + private val themeResId = appcompatR.style.Theme_AppCompat_Light + + @Test + fun `Dialog confirmed callback is called correctly`() { + var onConfirmCalled = false + var onCancelCalled = false + + val onConfirm = { onConfirmCalled = true } + val onCancel = { onCancelCalled = true } + + val fragment = spy(SimpleRedirectDialogFragment.newInstance(themeResId = themeResId)) + doNothing().`when`(fragment).dismiss() + + doReturn(testContext).`when`(fragment).requireContext() + + fragment.onConfirmRedirect = onConfirm + fragment.onCancelRedirect = onCancel + + val dialog = fragment.onCreateDialog(null) + dialog.show() + + val confirmButton = dialog.findViewById<Button>(android.R.id.button1) + confirmButton?.performClick() + shadowOf(getMainLooper()).idle() + + assertTrue(onConfirmCalled) + assertFalse(onCancelCalled) + } + + @Test + fun `Dialog cancel callback is called correctly`() { + var onConfirmCalled = false + var onCancelCalled = false + + val onConfirm = { onConfirmCalled = true } + val onCancel = { onCancelCalled = true } + + val fragment = spy(SimpleRedirectDialogFragment.newInstance(themeResId = themeResId)) + doNothing().`when`(fragment).dismiss() + + doReturn(testContext).`when`(fragment).requireContext() + + fragment.onConfirmRedirect = onConfirm + fragment.onCancelRedirect = onCancel + + val dialog = fragment.onCreateDialog(null) + dialog.show() + + val confirmButton = dialog.findViewById<Button>(android.R.id.button2) + confirmButton?.performClick() + shadowOf(getMainLooper()).idle() + + assertFalse(onConfirmCalled) + assertTrue(onCancelCalled) + } + + @Test + fun `Dialog confirm and cancel is not called when dismissed`() { + var onConfirmCalled = false + var onCancelCalled = false + + val onConfirm = { onConfirmCalled = true } + val onCancel = { onCancelCalled = true } + + val fragment = spy(SimpleRedirectDialogFragment.newInstance(themeResId = themeResId)) + doNothing().`when`(fragment).dismiss() + + doReturn(testContext).`when`(fragment).requireContext() + + fragment.onConfirmRedirect = onConfirm + fragment.onCancelRedirect = onCancel + + val dialog = fragment.onCreateDialog(null) + dialog.show() + dialog.dismiss() + + assertFalse(onConfirmCalled) + assertFalse(onCancelCalled) + } + + private fun mockFragmentManager(): FragmentManager { + val fragmentManager: FragmentManager = mock() + val transaction: FragmentTransaction = mock() + doReturn(transaction).`when`(fragmentManager).beginTransaction() + return fragmentManager + } +} diff --git a/mobile/android/android-components/components/feature/app-links/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/mobile/android/android-components/components/feature/app-links/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker new file mode 100644 index 0000000000..cf1c399ea8 --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker @@ -0,0 +1,2 @@ +mock-maker-inline +// This allows mocking final classes (classes are final by default in Kotlin) diff --git a/mobile/android/android-components/components/feature/app-links/src/test/resources/robolectric.properties b/mobile/android/android-components/components/feature/app-links/src/test/resources/robolectric.properties new file mode 100644 index 0000000000..932b01b9eb --- /dev/null +++ b/mobile/android/android-components/components/feature/app-links/src/test/resources/robolectric.properties @@ -0,0 +1 @@ +sdk=28 |