diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/ExternalAppBrowserFragment.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/ExternalAppBrowserFragment.kt index 26e81c87..b9b24147 100644 --- a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/ExternalAppBrowserFragment.kt +++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/ExternalAppBrowserFragment.kt @@ -26,6 +26,7 @@ import com.mikepenz.iconics.typeface.IIcon import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial import com.mikepenz.iconics.utils.colorInt import com.mikepenz.iconics.utils.sizeDp +import eu.weblibre.flutter_mozilla_components.feature.PwaWindowFeature import eu.weblibre.flutter_mozilla_components.widget.CustomTabToolbar import eu.weblibre.flutter_mozilla_components.widget.CustomTabToolbarFeature import mozilla.components.feature.contextmenu.ContextMenuCandidate @@ -39,6 +40,7 @@ import mozilla.components.feature.pwa.feature.WebAppActivityFeature import mozilla.components.feature.pwa.feature.WebAppContentFeature import mozilla.components.feature.pwa.feature.WebAppHideToolbarFeature import mozilla.components.feature.pwa.feature.WebAppSiteControlsFeature +import mozilla.components.support.base.feature.LifecycleAwareFeature import mozilla.components.support.base.feature.UserInteractionHandler import mozilla.components.support.base.feature.ViewBoundFeatureWrapper import mozilla.components.support.base.log.logger.Logger @@ -53,7 +55,7 @@ class ExternalAppBrowserFragment : BaseBrowserFragment(), UserInteractionHandler private val customTabsToolbarFeature = ViewBoundFeatureWrapper() private val contextMenuFeature = ViewBoundFeatureWrapper() private val hideToolbarFeature = ViewBoundFeatureWrapper() - private val windowFeature = ViewBoundFeatureWrapper() + private val windowFeature = ViewBoundFeatureWrapper() private var customTabToolbar: CustomTabToolbar? = null private var activePopup: PopupWindow? = null @@ -368,15 +370,23 @@ class ExternalAppBrowserFragment : BaseBrowserFragment(), UserInteractionHandler components.core.webAppManifestStorage.getManifestCache(url) } ?: customTab.content.webAppManifest + val isPwaOrTwa = customTab.config.externalAppType == ExternalAppType.PROGRESSIVE_WEB_APP || + customTab.config.externalAppType == ExternalAppType.TRUSTED_WEB_ACTIVITY + windowFeature.set( - feature = CustomTabWindowFeature(activity, store, sessionId), + feature = if (isPwaOrTwa) { + PwaWindowFeature( + store = store, + loadUrlUseCase = components.useCases.sessionUseCases.loadUrl, + sessionId = sessionId, + ) + } else { + CustomTabWindowFeature(activity, store, sessionId) + }, owner = this, view = view, ) - val isPwaOrTwa = customTab.config.externalAppType == ExternalAppType.PROGRESSIVE_WEB_APP || - customTab.config.externalAppType == ExternalAppType.TRUSTED_WEB_ACTIVITY - if (isPwaOrTwa) { hideToolbarFeature.set( feature = WebAppHideToolbarFeature( diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/feature/PwaWindowFeature.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/feature/PwaWindowFeature.kt new file mode 100644 index 00000000..8c4b6718 --- /dev/null +++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/feature/PwaWindowFeature.kt @@ -0,0 +1,61 @@ +/* + * 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 https://mozilla.org/MPL/2.0/. + */ + +package eu.weblibre.flutter_mozilla_components.feature + +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +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.findCustomTab +import mozilla.components.browser.state.store.BrowserStore +import mozilla.components.concept.engine.EngineSession +import mozilla.components.concept.engine.window.WindowRequest +import mozilla.components.feature.session.SessionUseCases +import mozilla.components.lib.state.ext.flowScoped +import mozilla.components.support.base.feature.LifecycleAwareFeature + +/** + * Handles popup/window requests inside a standalone PWA session. + */ +class PwaWindowFeature( + private val store: BrowserStore, + private val loadUrlUseCase: SessionUseCases.DefaultLoadUrlUseCase, + private val sessionId: String, + private val mainDispatcher: CoroutineDispatcher = Dispatchers.Main, +) : LifecycleAwareFeature { + + private var scope: CoroutineScope? = null + + override fun start() { + scope = store.flowScoped(dispatcher = mainDispatcher) { flow -> + flow.mapNotNull { state -> state.findCustomTab(sessionId) } + .distinctUntilChangedBy { it.content.windowRequest } + .collect { state -> + val windowRequest = state.content.windowRequest + if (windowRequest?.type == WindowRequest.Type.OPEN) { + if (windowRequest.url.isNotBlank()) { + loadUrlUseCase( + windowRequest.url, + sessionId, + EngineSession.LoadUrlFlags.external(), + ) + } + + store.dispatch(ContentAction.ConsumeWindowRequestAction(sessionId)) + } + } + } + } + + override fun stop() { + scope?.cancel() + scope = null + } +}