Loading components/feature/app-links/src/main/java/mozilla/components/feature/app/links/AppLinksInterceptor.kt +9 −2 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ import mozilla.components.concept.engine.request.RequestInterceptor * 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, Loading @@ -44,7 +45,8 @@ class AppLinksInterceptor( private val alwaysAllowedSchemes: Set<String> = setOf("mailto", "market", "sms", "tel"), private val alwaysDeniedSchemes: Set<String> = setOf("javascript", "about"), private val launchInApp: () -> Boolean = { false }, private val useCases: AppLinksUseCases = AppLinksUseCases(context, launchInApp) private val useCases: AppLinksUseCases = AppLinksUseCases(context, launchInApp), private val launchFromInterceptor: Boolean = false ) : RequestInterceptor { override fun onLoadRequest( Loading @@ -60,8 +62,13 @@ class AppLinksInterceptor( } val redirect = useCases.interceptedAppLinkRedirect(uri) val result = handleRedirect(redirect, uri) if (redirect.isRedirect()) { return handleRedirect(redirect, uri) if (launchFromInterceptor && result is RequestInterceptor.InterceptionResponse.AppIntent) { useCases.openAppLink(result.appIntent) } return result } return null Loading components/feature/app-links/src/main/java/mozilla/components/feature/app/links/AppLinksUseCases.kt +1 −1 Original line number Diff line number Diff line Loading @@ -131,7 +131,7 @@ class AppLinksUseCases( intent.addCategory(Intent.CATEGORY_BROWSABLE) intent.component = null intent.selector = null intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK } val fallbackIntent = intent.getStringExtra(EXTRA_BROWSER_FALLBACK_URL)?.let { Loading components/feature/app-links/src/test/java/mozilla/components/feature/app/links/AppLinksInterceptorTest.kt +21 −0 Original line number Diff line number Diff line Loading @@ -9,12 +9,14 @@ 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.support.test.any import mozilla.components.support.test.mock import mozilla.components.support.test.whenever import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.verify @RunWith(AndroidJUnit4::class) class AppLinksInterceptorTest { Loading @@ -22,6 +24,8 @@ class AppLinksInterceptorTest { 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" Loading @@ -36,7 +40,9 @@ class AppLinksInterceptorTest { mockUseCases = mock() mockEngineSession = mock() mockGetRedirect = mock() mockOpenRedirect = mock() whenever(mockUseCases.interceptedAppLinkRedirect).thenReturn(mockGetRedirect) whenever(mockUseCases.openAppLink).thenReturn(mockOpenRedirect) val webRedirect = AppLinkRedirect(null, webUrl, null) val appRedirect = AppLinkRedirect(Intent.parseUri(intentUrl, 0), null, null) Loading Loading @@ -125,4 +131,19 @@ class AppLinksInterceptorTest { val response = appLinksInterceptor.onLoadRequest(mockEngineSession, marketplaceUrl, true, false) assert(response is RequestInterceptor.InterceptionResponse.AppIntent) } @Test fun `external app is launched when launch from interceptor is set to true`() { appLinksInterceptor = AppLinksInterceptor( context = mockContext, interceptLinkClicks = true, launchInApp = { true }, useCases = mockUseCases, launchFromInterceptor = true ) val response = appLinksInterceptor.onLoadRequest(mockEngineSession, webUrlWithAppLink, true, false) assert(response is RequestInterceptor.InterceptionResponse.AppIntent) verify(mockOpenRedirect).invoke(any(), any()) } } docs/changelog.md +11 −0 Original line number Diff line number Diff line Loading @@ -31,6 +31,17 @@ permalink: /changelog/ and renamed to `PingType.submit()`. * Rename `deletion_request` ping to `deletion-request` ping after glean_parser update * **feature-app-links** * AppLinksInterceptor can now be used without the AppLinksFeature. Set the new parameter launchFromInterceptor = true ```kotlin AppLinksInterceptor( applicationContext, interceptLinkClicks = true, launchInApp = { true }, launchFromInterceptor = true ) ``` # 27.0.0 * [Commits](https://github.com/mozilla-mobile/android-components/compare/v26.0.0...v27.0.0) Loading samples/browser/src/main/java/org/mozilla/samples/browser/BaseBrowserFragment.kt +0 −16 Original line number Diff line number Diff line Loading @@ -9,13 +9,11 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.Toast import androidx.annotation.CallSuper import androidx.fragment.app.Fragment import kotlinx.android.synthetic.main.fragment_browser.view.* import mozilla.components.browser.session.SelectionAwareSessionObserver import mozilla.components.browser.session.Session import mozilla.components.feature.app.links.AppLinksFeature import mozilla.components.feature.contextmenu.ContextMenuCandidate import mozilla.components.feature.contextmenu.ContextMenuFeature import mozilla.components.feature.downloads.DownloadsFeature Loading Loading @@ -50,7 +48,6 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler { private val findInPageIntegration = ViewBoundFeatureWrapper<FindInPageIntegration>() private val sitePermissionsFeature = ViewBoundFeatureWrapper<SitePermissionsFeature>() private val swipeRefreshFeature = ViewBoundFeatureWrapper<SwipeRefreshFeature>() private val appLinksFeature = ViewBoundFeatureWrapper<AppLinksFeature>() protected val sessionId: String? get() = arguments?.getString(SESSION_ID_KEY) Loading Loading @@ -181,19 +178,6 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler { secureWindowFeature ) appLinksFeature.set( feature = AppLinksFeature( context = requireContext(), sessionManager = components.sessionManager, sessionId = sessionId, fragmentManager = requireFragmentManager(), launchInApp = { components.preferences.getBoolean(DefaultComponents.PREF_LAUNCH_EXTERNAL_APP, false) }, failedToLaunchAction = { Toast.makeText(context, "Failed to open in app", Toast.LENGTH_LONG).show() } ), owner = this, view = layout ) return layout } Loading Loading
components/feature/app-links/src/main/java/mozilla/components/feature/app/links/AppLinksInterceptor.kt +9 −2 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ import mozilla.components.concept.engine.request.RequestInterceptor * 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, Loading @@ -44,7 +45,8 @@ class AppLinksInterceptor( private val alwaysAllowedSchemes: Set<String> = setOf("mailto", "market", "sms", "tel"), private val alwaysDeniedSchemes: Set<String> = setOf("javascript", "about"), private val launchInApp: () -> Boolean = { false }, private val useCases: AppLinksUseCases = AppLinksUseCases(context, launchInApp) private val useCases: AppLinksUseCases = AppLinksUseCases(context, launchInApp), private val launchFromInterceptor: Boolean = false ) : RequestInterceptor { override fun onLoadRequest( Loading @@ -60,8 +62,13 @@ class AppLinksInterceptor( } val redirect = useCases.interceptedAppLinkRedirect(uri) val result = handleRedirect(redirect, uri) if (redirect.isRedirect()) { return handleRedirect(redirect, uri) if (launchFromInterceptor && result is RequestInterceptor.InterceptionResponse.AppIntent) { useCases.openAppLink(result.appIntent) } return result } return null Loading
components/feature/app-links/src/main/java/mozilla/components/feature/app/links/AppLinksUseCases.kt +1 −1 Original line number Diff line number Diff line Loading @@ -131,7 +131,7 @@ class AppLinksUseCases( intent.addCategory(Intent.CATEGORY_BROWSABLE) intent.component = null intent.selector = null intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK } val fallbackIntent = intent.getStringExtra(EXTRA_BROWSER_FALLBACK_URL)?.let { Loading
components/feature/app-links/src/test/java/mozilla/components/feature/app/links/AppLinksInterceptorTest.kt +21 −0 Original line number Diff line number Diff line Loading @@ -9,12 +9,14 @@ 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.support.test.any import mozilla.components.support.test.mock import mozilla.components.support.test.whenever import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.verify @RunWith(AndroidJUnit4::class) class AppLinksInterceptorTest { Loading @@ -22,6 +24,8 @@ class AppLinksInterceptorTest { 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" Loading @@ -36,7 +40,9 @@ class AppLinksInterceptorTest { mockUseCases = mock() mockEngineSession = mock() mockGetRedirect = mock() mockOpenRedirect = mock() whenever(mockUseCases.interceptedAppLinkRedirect).thenReturn(mockGetRedirect) whenever(mockUseCases.openAppLink).thenReturn(mockOpenRedirect) val webRedirect = AppLinkRedirect(null, webUrl, null) val appRedirect = AppLinkRedirect(Intent.parseUri(intentUrl, 0), null, null) Loading Loading @@ -125,4 +131,19 @@ class AppLinksInterceptorTest { val response = appLinksInterceptor.onLoadRequest(mockEngineSession, marketplaceUrl, true, false) assert(response is RequestInterceptor.InterceptionResponse.AppIntent) } @Test fun `external app is launched when launch from interceptor is set to true`() { appLinksInterceptor = AppLinksInterceptor( context = mockContext, interceptLinkClicks = true, launchInApp = { true }, useCases = mockUseCases, launchFromInterceptor = true ) val response = appLinksInterceptor.onLoadRequest(mockEngineSession, webUrlWithAppLink, true, false) assert(response is RequestInterceptor.InterceptionResponse.AppIntent) verify(mockOpenRedirect).invoke(any(), any()) } }
docs/changelog.md +11 −0 Original line number Diff line number Diff line Loading @@ -31,6 +31,17 @@ permalink: /changelog/ and renamed to `PingType.submit()`. * Rename `deletion_request` ping to `deletion-request` ping after glean_parser update * **feature-app-links** * AppLinksInterceptor can now be used without the AppLinksFeature. Set the new parameter launchFromInterceptor = true ```kotlin AppLinksInterceptor( applicationContext, interceptLinkClicks = true, launchInApp = { true }, launchFromInterceptor = true ) ``` # 27.0.0 * [Commits](https://github.com/mozilla-mobile/android-components/compare/v26.0.0...v27.0.0) Loading
samples/browser/src/main/java/org/mozilla/samples/browser/BaseBrowserFragment.kt +0 −16 Original line number Diff line number Diff line Loading @@ -9,13 +9,11 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.Toast import androidx.annotation.CallSuper import androidx.fragment.app.Fragment import kotlinx.android.synthetic.main.fragment_browser.view.* import mozilla.components.browser.session.SelectionAwareSessionObserver import mozilla.components.browser.session.Session import mozilla.components.feature.app.links.AppLinksFeature import mozilla.components.feature.contextmenu.ContextMenuCandidate import mozilla.components.feature.contextmenu.ContextMenuFeature import mozilla.components.feature.downloads.DownloadsFeature Loading Loading @@ -50,7 +48,6 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler { private val findInPageIntegration = ViewBoundFeatureWrapper<FindInPageIntegration>() private val sitePermissionsFeature = ViewBoundFeatureWrapper<SitePermissionsFeature>() private val swipeRefreshFeature = ViewBoundFeatureWrapper<SwipeRefreshFeature>() private val appLinksFeature = ViewBoundFeatureWrapper<AppLinksFeature>() protected val sessionId: String? get() = arguments?.getString(SESSION_ID_KEY) Loading Loading @@ -181,19 +178,6 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler { secureWindowFeature ) appLinksFeature.set( feature = AppLinksFeature( context = requireContext(), sessionManager = components.sessionManager, sessionId = sessionId, fragmentManager = requireFragmentManager(), launchInApp = { components.preferences.getBoolean(DefaultComponents.PREF_LAUNCH_EXTERNAL_APP, false) }, failedToLaunchAction = { Toast.makeText(context, "Failed to open in app", Toast.LENGTH_LONG).show() } ), owner = this, view = layout ) return layout } Loading