improved pwa lifecycle
This commit is contained in:
+3
-1
@@ -163,7 +163,9 @@ object GlobalComponents {
|
||||
|
||||
val previousComponents = _components
|
||||
val previousMode = currentMode
|
||||
val previousCustomTabs = if (previousMode == ComponentsMode.EXTERNAL) {
|
||||
val isSameProfile = previousComponents?.profileApplicationContext?.relativePath ==
|
||||
applicationContext.relativePath
|
||||
val previousCustomTabs = if (isSameProfile) {
|
||||
previousComponents?.core?.store?.state?.customTabs.orEmpty()
|
||||
} else {
|
||||
emptyList()
|
||||
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import mozilla.components.browser.state.action.CustomTabListAction
|
||||
import mozilla.components.browser.state.state.CustomTabConfig
|
||||
import mozilla.components.browser.state.state.ExternalAppType
|
||||
import mozilla.components.browser.state.state.SessionState
|
||||
import mozilla.components.browser.state.state.createCustomTab
|
||||
import mozilla.components.concept.engine.EngineSession
|
||||
|
||||
object PwaSessionCreator {
|
||||
suspend fun create(url: String, contextId: String?): String {
|
||||
val components = GlobalComponents.components
|
||||
?: throw IllegalStateException("Components not initialized")
|
||||
|
||||
val manifest = withContext(Dispatchers.IO) {
|
||||
components.core.webAppManifestStorage.loadManifest(url)
|
||||
}
|
||||
|
||||
return withContext(Dispatchers.Main) {
|
||||
val customTabConfig = CustomTabConfig(
|
||||
externalAppType = ExternalAppType.PROGRESSIVE_WEB_APP,
|
||||
)
|
||||
|
||||
val tab = createCustomTab(
|
||||
url = url,
|
||||
contextId = contextId,
|
||||
config = customTabConfig,
|
||||
webAppManifest = manifest,
|
||||
source = SessionState.Source.Internal.CustomTab,
|
||||
private = false,
|
||||
)
|
||||
|
||||
components.core.store.dispatch(
|
||||
CustomTabListAction.AddCustomTabAction(tab),
|
||||
)
|
||||
|
||||
val loadUrlFlags = EngineSession.LoadUrlFlags.external()
|
||||
components.useCases.sessionUseCases.loadUrl(url, tab.id, loadUrlFlags)
|
||||
|
||||
tab.id
|
||||
}
|
||||
}
|
||||
}
|
||||
+63
-6
@@ -16,6 +16,7 @@ import androidx.core.view.WindowCompat
|
||||
import eu.weblibre.flutter_mozilla_components.ExternalAppBrowserFragment
|
||||
import eu.weblibre.flutter_mozilla_components.GlobalComponents
|
||||
import eu.weblibre.flutter_mozilla_components.PwaConstants
|
||||
import eu.weblibre.flutter_mozilla_components.PwaSessionCreator
|
||||
import eu.weblibre.flutter_mozilla_components.R
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -66,6 +67,7 @@ open class ExternalAppBrowserActivity : AppCompatActivity() {
|
||||
|
||||
private val logger = Logger("ExternalAppBrowserActivity")
|
||||
private val coroutineScope = CoroutineScope(Dispatchers.Main + SupervisorJob())
|
||||
private var isRecoveringPwaSession = false
|
||||
|
||||
private val customTabSessionId: String?
|
||||
get() = intent?.getStringExtra(EXTRA_CUSTOM_TAB_SESSION_ID)
|
||||
@@ -85,17 +87,19 @@ open class ExternalAppBrowserActivity : AppCompatActivity() {
|
||||
finishAndRemoveTask()
|
||||
}
|
||||
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
setContentView(R.layout.activity_external_app_browser)
|
||||
|
||||
val sessionId = customTabSessionId
|
||||
if (sessionId == null) {
|
||||
Log.e(TAG, "No custom tab session ID provided")
|
||||
logger.error("No custom tab session ID provided, finishing.")
|
||||
fallbackToMainActivity()
|
||||
if (!recoverPwaSession(null, "missing session ID")) {
|
||||
fallbackToMainActivity()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
setContentView(R.layout.activity_external_app_browser)
|
||||
|
||||
val components = GlobalComponents.components
|
||||
if (components == null) {
|
||||
if (GlobalComponents.ensureExternalComponents(applicationContext)) {
|
||||
@@ -145,7 +149,9 @@ open class ExternalAppBrowserActivity : AppCompatActivity() {
|
||||
if (components.core.store.state.findCustomTab(sessionId) == null) {
|
||||
Log.e(TAG, "Custom tab session $sessionId not found in store")
|
||||
logger.error("Custom tab session $sessionId not found in store, finishing.")
|
||||
fallbackToMainActivity()
|
||||
if (!recoverPwaSession(sessionId, "session not found in store")) {
|
||||
fallbackToMainActivity()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -168,10 +174,61 @@ open class ExternalAppBrowserActivity : AppCompatActivity() {
|
||||
if (components.core.store.state.findCustomTab(sessionId) == null) {
|
||||
Log.w(TAG, "Custom tab session $sessionId gone on resume")
|
||||
logger.debug("Custom tab session $sessionId gone, finishing activity.")
|
||||
fallbackToMainActivity()
|
||||
if (!recoverPwaSession(sessionId, "session gone on resume")) {
|
||||
fallbackToMainActivity()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun recoverPwaSession(missingSessionId: String?, reason: String): Boolean {
|
||||
if (isRecoveringPwaSession) {
|
||||
return true
|
||||
}
|
||||
|
||||
val launchUrl = webAppManifestUrl
|
||||
?: intent?.getStringExtra(PwaConstants.EXTRA_PWA_INSTALL_START_URL)
|
||||
?: return false
|
||||
|
||||
val isPwaTask = webAppManifestUrl != null ||
|
||||
intent?.hasExtra(PwaConstants.EXTRA_PWA_PROFILE_UUID) == true
|
||||
if (!isPwaTask) {
|
||||
return false
|
||||
}
|
||||
|
||||
val contextId = intent?.getStringExtra(PwaConstants.EXTRA_PWA_CONTEXT_ID)
|
||||
isRecoveringPwaSession = true
|
||||
Log.w(TAG, "Recovering PWA session for $launchUrl: $reason")
|
||||
|
||||
coroutineScope.launch {
|
||||
try {
|
||||
val sessionId = PwaSessionCreator.create(launchUrl, contextId)
|
||||
Log.d(
|
||||
TAG,
|
||||
"Recovered PWA session: old=$missingSessionId, new=$sessionId, url=$launchUrl",
|
||||
)
|
||||
|
||||
intent.putExtra(EXTRA_CUSTOM_TAB_SESSION_ID, sessionId)
|
||||
if (webAppManifestUrl == null) {
|
||||
intent.putExtra(EXTRA_WEB_APP_MANIFEST_URL, launchUrl)
|
||||
}
|
||||
|
||||
if (!isFinishing && !isDestroyed) {
|
||||
showFragment(sessionId)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to recover PWA session", e)
|
||||
logger.error("Failed to recover PWA session, removing stale task.", e)
|
||||
if (!isFinishing && !isDestroyed) {
|
||||
finishAndRemoveTask()
|
||||
}
|
||||
} finally {
|
||||
isRecoveringPwaSession = false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun fallbackToMainActivity() {
|
||||
val mainIntent = Intent().apply {
|
||||
setClassName(this@ExternalAppBrowserActivity, "eu.weblibre.gecko.MainActivity")
|
||||
|
||||
+7
-47
@@ -19,6 +19,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import eu.weblibre.flutter_mozilla_components.Components
|
||||
import eu.weblibre.flutter_mozilla_components.GlobalComponents
|
||||
import eu.weblibre.flutter_mozilla_components.PwaConstants
|
||||
import eu.weblibre.flutter_mozilla_components.PwaSessionCreator
|
||||
import eu.weblibre.flutter_mozilla_components.gatekeeper.IntentBlockNotifier
|
||||
import eu.weblibre.flutter_mozilla_components.gatekeeper.IntentGatekeeperPreferences
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@@ -26,7 +27,6 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import mozilla.components.browser.state.selector.findCustomTab
|
||||
import mozilla.components.feature.customtabs.CustomTabIntentProcessor
|
||||
import mozilla.components.feature.intent.ext.getSessionId
|
||||
@@ -475,23 +475,17 @@ class IntentReceiverActivity : Activity() {
|
||||
return
|
||||
}
|
||||
|
||||
val components = GlobalComponents.components
|
||||
?: run {
|
||||
Log.e(TAG, "Components not available for PWA launch")
|
||||
handleRegularIntent(intent)
|
||||
return
|
||||
}
|
||||
if (GlobalComponents.components == null) {
|
||||
Log.e(TAG, "Components not available for PWA launch")
|
||||
handleRegularIntent(intent)
|
||||
return
|
||||
}
|
||||
|
||||
coroutineScope.launch {
|
||||
try {
|
||||
val manifest = withContext(Dispatchers.IO) {
|
||||
components.core.webAppManifestStorage.loadManifest(url)
|
||||
}
|
||||
|
||||
val sessionId = createPwaSession(
|
||||
val sessionId = PwaSessionCreator.create(
|
||||
url = url,
|
||||
contextId = contextId,
|
||||
manifest = manifest
|
||||
)
|
||||
|
||||
Log.d(TAG, "Created PWA session: contextId=$contextId, sessionId=$sessionId")
|
||||
@@ -597,40 +591,6 @@ class IntentReceiverActivity : Activity() {
|
||||
(launchUrl == requestedInstallStartUrl || launchUrl == url)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a custom tab session for a PWA with the specified context ID.
|
||||
*/
|
||||
private fun createPwaSession(
|
||||
url: String,
|
||||
contextId: String?,
|
||||
manifest: mozilla.components.concept.engine.manifest.WebAppManifest?
|
||||
): String {
|
||||
val components = GlobalComponents.components
|
||||
?: throw IllegalStateException("Components not initialized")
|
||||
|
||||
val customTabConfig = mozilla.components.browser.state.state.CustomTabConfig(
|
||||
externalAppType = mozilla.components.browser.state.state.ExternalAppType.PROGRESSIVE_WEB_APP
|
||||
)
|
||||
|
||||
val tab = mozilla.components.browser.state.state.createCustomTab(
|
||||
url = url,
|
||||
contextId = contextId,
|
||||
config = customTabConfig,
|
||||
webAppManifest = manifest,
|
||||
source = mozilla.components.browser.state.state.SessionState.Source.Internal.CustomTab,
|
||||
private = false
|
||||
)
|
||||
|
||||
components.core.store.dispatch(
|
||||
mozilla.components.browser.state.action.CustomTabListAction.AddCustomTabAction(tab)
|
||||
)
|
||||
|
||||
val loadUrlFlags = mozilla.components.concept.engine.EngineSession.LoadUrlFlags.external()
|
||||
components.useCases.sessionUseCases.loadUrl(url, tab.id, loadUrlFlags)
|
||||
|
||||
return tab.id
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts a URL from a SEND intent.
|
||||
* First checks EXTRA_TEXT for a URL, then EXTRA_STREAM.
|
||||
|
||||
Reference in New Issue
Block a user