Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
<modelVersion>4.0.0</modelVersion>
<groupId>io.ionic.libs</groupId>
<artifactId>ioninappbrowser-android</artifactId>
<version>1.6.1</version>
<version>1.7.0</version>
</project>
1 change: 1 addition & 0 deletions src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<activity
android:name=".views.OSIABWebViewActivity"
android:exported="false"
android:process=":OSInAppBrowser"
Comment thread
OS-pedrogustavobilro marked this conversation as resolved.
android:configChanges="orientation|screenSize|uiMode"
android:label="OSIABWebViewActivity"
android:theme="@style/AppTheme.WebView"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
package com.outsystems.plugins.inappbrowser.osinappbrowserlib

import android.annotation.SuppressLint
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Build
import com.outsystems.plugins.inappbrowser.osinappbrowserlib.views.OSIABWebViewActivity
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import java.io.Serializable

sealed class OSIABEvents {
@RequiresOptIn(
level = RequiresOptIn.Level.WARNING,
message = "This API requires a prior call to OSIABEvents.registerReceiver(context) to work correctly with process isolation on Android 9+."
)
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
annotation class RequiresEventBridgeRegistration

sealed class OSIABEvents : Serializable {
Comment thread
OS-pedrogustavobilro marked this conversation as resolved.
abstract val browserId: String

data class BrowserPageLoaded(override val browserId: String) : OSIABEvents()
Expand All @@ -19,18 +33,59 @@ sealed class OSIABEvents {
) : OSIABEvents()
data class OSIABWebViewEvent(
override val browserId: String,
val activity: OSIABWebViewActivity
val activity: OSIABWebViewActivity? = null
) : OSIABEvents()

companion object {
const val EXTRA_BROWSER_ID = "com.outsystems.plugins.inappbrowser.osinappbrowserlib.EXTRA_BROWSER_ID"
const val ACTION_IAB_EVENT = "com.outsystems.plugins.inappbrowser.osinappbrowserlib.ACTION_IAB_EVENT"
const val EXTRA_EVENT_DATA = "com.outsystems.plugins.inappbrowser.osinappbrowserlib.EXTRA_EVENT_DATA"

private val _events = MutableSharedFlow<OSIABEvents>()
private val _events = MutableSharedFlow<OSIABEvents>(extraBufferCapacity = 64)
Comment thread
ItsChaceD marked this conversation as resolved.
val events = _events.asSharedFlow()

private var receiver: BroadcastReceiver? = null

@SuppressLint("UnspecifiedRegisterReceiverFlag")
fun registerReceiver(context: Context) {
Comment thread
ItsChaceD marked this conversation as resolved.
Comment thread
ItsChaceD marked this conversation as resolved.
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
}
Comment thread
ItsChaceD marked this conversation as resolved.
Outdated
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)
}
Comment thread
ItsChaceD marked this conversation as resolved.
Outdated
}

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)
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -35,6 +36,7 @@ class OSIABCustomTabsSessionHelper: OSIABCustomTabsSessionHelperInterface {
flowHelper: OSIABFlowHelperInterface,
customTabsSessionCallback: (CustomTabsSession?) -> Unit
) {
OSIABEvents.registerReceiver(context)
Comment thread
ItsChaceD marked this conversation as resolved.
Outdated
CustomTabsClient.bindCustomTabsService(
context,
packageName,
Expand All @@ -54,6 +56,7 @@ class OSIABCustomTabsSessionHelper: OSIABCustomTabsSessionHelperInterface {
)
}

@OptIn(RequiresEventBridgeRegistration::class)
private inner class CustomTabsCallbackImpl(
private val browserId: String,
private val lifecycleScope: CoroutineScope,
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 ->
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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}")
}
}
Comment thread
OS-pedrogustavobilro marked this conversation as resolved.
}
}

override fun onCreate(savedInstanceState: Bundle?) {
Expand All @@ -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()

Expand Down Expand Up @@ -917,6 +926,7 @@ class OSIABWebViewActivity : AppCompatActivity() {
private fun sendWebViewEvent(event: OSIABEvents) {
lifecycleScope.launch {
OSIABEvents.postEvent(event)
OSIABEvents.broadcastEvent(this@OSIABWebViewActivity, event)
Comment thread
ItsChaceD marked this conversation as resolved.
}
}

Expand Down
Loading