diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4fcac34..61b6e47 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [1.7.0]
+
+### Fixes
+
+- Fix issue with local storage isolation between WebView and main app on Android 28+ [RMET-4918](https://outsystemsrd.atlassian.net/browse/RMET-4918)
+
## [1.6.1]
### Fixes
diff --git a/pom.xml b/pom.xml
index 3ab5e59..c704fb1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,5 +6,5 @@
4.0.0
io.ionic.libs
ioninappbrowser-android
- 1.6.1
+ 1.7.0
diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index 4def0b9..bf36993 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -17,6 +17,7 @@
()
+ private val _events = MutableSharedFlow(extraBufferCapacity = 64)
val events = _events.asSharedFlow()
+ private var receiver: BroadcastReceiver? = null
+
+ @SuppressLint("UnspecifiedRegisterReceiverFlag")
+ fun registerReceiver(context: Context) {
+ if (receiver != null) return
+
+ val appContext = context.applicationContext
+ receiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ if (intent?.action == ACTION_IAB_EVENT) {
+ @Suppress("DEPRECATION")
+ val event = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ intent.getSerializableExtra(EXTRA_EVENT_DATA, OSIABEvents::class.java)
+ } else {
+ intent.getSerializableExtra(EXTRA_EVENT_DATA) as? OSIABEvents
+ }
+ event?.let {
+ _events.tryEmit(it)
+ }
+ }
+ }
+ }
+
+ val filter = IntentFilter(ACTION_IAB_EVENT)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ appContext.registerReceiver(receiver, filter, Context.RECEIVER_NOT_EXPORTED)
+ } else {
+ appContext.registerReceiver(receiver, filter)
+ }
+ }
+
suspend fun postEvent(event: OSIABEvents) {
_events.emit(event)
}
+
+ fun broadcastEvent(context: Context, event: OSIABEvents) {
+ val intent = Intent(ACTION_IAB_EVENT).apply {
+ setPackage(context.packageName)
+ putExtra(EXTRA_EVENT_DATA, event)
+ }
+ context.sendBroadcast(intent)
+ }
}
}
diff --git a/src/main/java/com.outsystems.plugins.inappbrowser/osinappbrowserlib/helpers/OSIABCustomTabsSessionHelper.kt b/src/main/java/com.outsystems.plugins.inappbrowser/osinappbrowserlib/helpers/OSIABCustomTabsSessionHelper.kt
index a26ca53..a5e5862 100644
--- a/src/main/java/com.outsystems.plugins.inappbrowser/osinappbrowserlib/helpers/OSIABCustomTabsSessionHelper.kt
+++ b/src/main/java/com.outsystems.plugins.inappbrowser/osinappbrowserlib/helpers/OSIABCustomTabsSessionHelper.kt
@@ -11,6 +11,7 @@ import androidx.browser.customtabs.CustomTabsClient
import androidx.browser.customtabs.CustomTabsServiceConnection
import androidx.browser.customtabs.CustomTabsSession
import com.outsystems.plugins.inappbrowser.osinappbrowserlib.OSIABEvents
+import com.outsystems.plugins.inappbrowser.osinappbrowserlib.RequiresEventBridgeRegistration
import com.outsystems.plugins.inappbrowser.osinappbrowserlib.views.OSIABCustomTabsControllerActivity
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
@@ -35,6 +36,7 @@ class OSIABCustomTabsSessionHelper: OSIABCustomTabsSessionHelperInterface {
flowHelper: OSIABFlowHelperInterface,
customTabsSessionCallback: (CustomTabsSession?) -> Unit
) {
+ OSIABEvents.registerReceiver(context)
CustomTabsClient.bindCustomTabsService(
context,
packageName,
@@ -54,6 +56,7 @@ class OSIABCustomTabsSessionHelper: OSIABCustomTabsSessionHelperInterface {
)
}
+ @OptIn(RequiresEventBridgeRegistration::class)
private inner class CustomTabsCallbackImpl(
private val browserId: String,
private val lifecycleScope: CoroutineScope,
diff --git a/src/main/java/com.outsystems.plugins.inappbrowser/osinappbrowserlib/helpers/OSIABFlowHelper.kt b/src/main/java/com.outsystems.plugins.inappbrowser/osinappbrowserlib/helpers/OSIABFlowHelper.kt
index 3ae711b..f312132 100644
--- a/src/main/java/com.outsystems.plugins.inappbrowser/osinappbrowserlib/helpers/OSIABFlowHelper.kt
+++ b/src/main/java/com.outsystems.plugins.inappbrowser/osinappbrowserlib/helpers/OSIABFlowHelper.kt
@@ -1,6 +1,7 @@
package com.outsystems.plugins.inappbrowser.osinappbrowserlib.helpers
import com.outsystems.plugins.inappbrowser.osinappbrowserlib.OSIABEvents
+import com.outsystems.plugins.inappbrowser.osinappbrowserlib.RequiresEventBridgeRegistration
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.transformWhile
@@ -14,7 +15,11 @@ class OSIABFlowHelper: OSIABFlowHelperInterface {
* @param browserId Identifier for the browser instance to emit events to
* @param scope CoroutineScope to launch
* @param onEventReceived callback to send the collected event in
+ *
+ * @note For Android API 28+, you must call [OSIABEvents.registerReceiver] once during your application
+ * or activity lifecycle to ensure events from the isolated browser process are correctly received and bridged.
*/
+ @RequiresEventBridgeRegistration
override fun listenToEvents(
browserId: String,
scope: CoroutineScope,
diff --git a/src/main/java/com.outsystems.plugins.inappbrowser/osinappbrowserlib/helpers/OSIABFlowHelperInterface.kt b/src/main/java/com.outsystems.plugins.inappbrowser/osinappbrowserlib/helpers/OSIABFlowHelperInterface.kt
index 7c0c329..0ff2bbc 100644
--- a/src/main/java/com.outsystems.plugins.inappbrowser/osinappbrowserlib/helpers/OSIABFlowHelperInterface.kt
+++ b/src/main/java/com.outsystems.plugins.inappbrowser/osinappbrowserlib/helpers/OSIABFlowHelperInterface.kt
@@ -1,6 +1,7 @@
package com.outsystems.plugins.inappbrowser.osinappbrowserlib.helpers
import com.outsystems.plugins.inappbrowser.osinappbrowserlib.OSIABEvents
+import com.outsystems.plugins.inappbrowser.osinappbrowserlib.RequiresEventBridgeRegistration
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
@@ -12,7 +13,11 @@ interface OSIABFlowHelperInterface {
* @param browserId Identifier for the browser instance to emit events to
* @param scope CoroutineScope to launch
* @param onEventReceived callback to send the collected event in
+ *
+ * @note For Android API 28+, you must call [OSIABEvents.registerReceiver] once during your application
+ * or activity lifecycle to ensure events from the isolated browser process are correctly received and bridged.
*/
+ @RequiresEventBridgeRegistration
fun listenToEvents(
browserId: String,
scope: CoroutineScope,
diff --git a/src/main/java/com.outsystems.plugins.inappbrowser/osinappbrowserlib/routeradapters/OSIABCustomTabsRouterAdapter.kt b/src/main/java/com.outsystems.plugins.inappbrowser/osinappbrowserlib/routeradapters/OSIABCustomTabsRouterAdapter.kt
index 0c4e69a..08df9d1 100644
--- a/src/main/java/com.outsystems.plugins.inappbrowser/osinappbrowserlib/routeradapters/OSIABCustomTabsRouterAdapter.kt
+++ b/src/main/java/com.outsystems.plugins.inappbrowser/osinappbrowserlib/routeradapters/OSIABCustomTabsRouterAdapter.kt
@@ -6,6 +6,7 @@ import android.net.Uri
import androidx.browser.customtabs.CustomTabsIntent
import androidx.browser.customtabs.CustomTabsSession
import com.outsystems.plugins.inappbrowser.osinappbrowserlib.OSIABEvents
+import com.outsystems.plugins.inappbrowser.osinappbrowserlib.RequiresEventBridgeRegistration
import com.outsystems.plugins.inappbrowser.osinappbrowserlib.helpers.OSIABCustomTabsSessionHelper
import com.outsystems.plugins.inappbrowser.osinappbrowserlib.helpers.OSIABCustomTabsSessionHelperInterface
import com.outsystems.plugins.inappbrowser.osinappbrowserlib.helpers.OSIABFlowHelperInterface
@@ -40,7 +41,9 @@ class OSIABCustomTabsRouterAdapter(
// for the browserPageLoaded event, which we only want to trigger on the first URL loaded in the CustomTabs instance
private var isFirstLoad = true
+ @OptIn(RequiresEventBridgeRegistration::class)
override fun close(completionHandler: (Boolean) -> Unit) {
+ OSIABEvents.registerReceiver(context)
var closeEventJob: Job? = null
closeEventJob = flowHelper.listenToEvents(browserId, lifecycleScope) { event ->
@@ -141,6 +144,7 @@ class OSIABCustomTabsRouterAdapter(
}
override fun handleOpen(url: String, completionHandler: (Boolean) -> Unit) {
+ OSIABEvents.registerReceiver(context)
lifecycleScope.launch {
try {
val uri = Uri.parse(url)
@@ -165,6 +169,7 @@ class OSIABCustomTabsRouterAdapter(
}
}
+ @OptIn(RequiresEventBridgeRegistration::class)
private fun openCustomTabsIntent(session: CustomTabsSession, uri: Uri, completionHandler: (Boolean) -> Unit) {
val customTabsIntent = buildCustomTabsIntent(session)
var eventsJob: Job? = null
diff --git a/src/main/java/com.outsystems.plugins.inappbrowser/osinappbrowserlib/routeradapters/OSIABWebViewRouterAdapter.kt b/src/main/java/com.outsystems.plugins.inappbrowser/osinappbrowserlib/routeradapters/OSIABWebViewRouterAdapter.kt
index 6f5b741..951ffc5 100644
--- a/src/main/java/com.outsystems.plugins.inappbrowser/osinappbrowserlib/routeradapters/OSIABWebViewRouterAdapter.kt
+++ b/src/main/java/com.outsystems.plugins.inappbrowser/osinappbrowserlib/routeradapters/OSIABWebViewRouterAdapter.kt
@@ -4,6 +4,7 @@ import android.content.Context
import android.content.Intent
import android.os.Bundle
import com.outsystems.plugins.inappbrowser.osinappbrowserlib.OSIABEvents
+import com.outsystems.plugins.inappbrowser.osinappbrowserlib.RequiresEventBridgeRegistration
import com.outsystems.plugins.inappbrowser.osinappbrowserlib.helpers.OSIABFlowHelperInterface
import com.outsystems.plugins.inappbrowser.osinappbrowserlib.models.OSIABWebViewOptions
import com.outsystems.plugins.inappbrowser.osinappbrowserlib.views.OSIABWebViewActivity
@@ -71,10 +72,12 @@ class OSIABWebViewRouterAdapter(
* @param url URL to be opened.
* @param completionHandler The callback with the result of opening the url.
*/
+ @OptIn(RequiresEventBridgeRegistration::class)
override fun handleOpen(url: String, completionHandler: (Boolean) -> Unit) {
lifecycleScope.launch {
try {
// Collect the browser events
+ OSIABEvents.registerReceiver(context)
var eventsJob: Job? = null
eventsJob = flowHelper.listenToEvents(browserId, lifecycleScope) { event ->
when (event) {
diff --git a/src/main/java/com.outsystems.plugins.inappbrowser/osinappbrowserlib/views/OSIABWebViewActivity.kt b/src/main/java/com.outsystems.plugins.inappbrowser/osinappbrowserlib/views/OSIABWebViewActivity.kt
index 59ec090..0b42254 100644
--- a/src/main/java/com.outsystems.plugins.inappbrowser/osinappbrowserlib/views/OSIABWebViewActivity.kt
+++ b/src/main/java/com.outsystems.plugins.inappbrowser/osinappbrowserlib/views/OSIABWebViewActivity.kt
@@ -148,6 +148,15 @@ class OSIABWebViewActivity : AppCompatActivity() {
return File.createTempFile("${prefix}${timeStamp}_", suffix, storageDir)
}
+ init {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ try {
+ WebView.setDataDirectorySuffix("OSInAppBrowser")
+ } catch (e: Exception) {
+ Log.d(LOG_TAG, "Suffix already set or error: ${e.message}")
+ }
+ }
+ }
}
override fun onCreate(savedInstanceState: Bundle?) {
@@ -164,7 +173,7 @@ class OSIABWebViewActivity : AppCompatActivity() {
browserId = intent.getStringExtra(OSIABEvents.EXTRA_BROWSER_ID) ?: ""
- sendWebViewEvent(OSIABWebViewEvent(browserId, this@OSIABWebViewActivity))
+ sendWebViewEvent(OSIABEvents.OSIABWebViewEvent(browserId))
appName = applicationInfo.loadLabel(packageManager).toString()
@@ -917,6 +926,7 @@ class OSIABWebViewActivity : AppCompatActivity() {
private fun sendWebViewEvent(event: OSIABEvents) {
lifecycleScope.launch {
OSIABEvents.postEvent(event)
+ OSIABEvents.broadcastEvent(this@OSIABWebViewActivity, event)
}
}