-
Notifications
You must be signed in to change notification settings - Fork 4
fix: separate local storage between WebView processes #54
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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 { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Feels confusing for this class to be Can you explain why you implemented it that way? |
||
| abstract val browserId: String | ||
|
|
||
| data class BrowserPageLoaded(override val browserId: String) : OSIABEvents() | ||
|
|
@@ -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) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why was this added? |
||
| val events = _events.asSharedFlow() | ||
|
|
||
| private var receiver: BroadcastReceiver? = null | ||
|
|
||
| @SuppressLint("UnspecifiedRegisterReceiverFlag") | ||
| fun registerReceiver(context: Context) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So this is necessary because the WebView activity now runs in a separate process? Wouldn't this be necessary for CustomTabs as well before-hand?
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wondering if we should have a way to unregister the receiver, e.g. after Close events are submitted, can we get rid of the receiver, rather than it living throughout the entire app's lifecycle? |
||
| 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 on lines
+58
to
+62
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor: We can probably replace this with a one-liner for IntentCompat#getSerializableExtra (assuming the library already has an AndroidX dependency with the method available) |
||
| 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 on lines
+71
to
+75
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor: Can maybe replace this with a one-liner ContextCompat#registerReceiver (assuming the library already has an AndroidX dependency that comes with this) |
||
| } | ||
|
|
||
| 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 |
|---|---|---|
|
|
@@ -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) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is being called here but Same for |
||
| 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, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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) | ||
|
Comment on lines
928
to
+929
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we be calling both methods here? |
||
| } | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should make sure plugins (OutSystems especially) document this change, wouldn't be surprised if it ends up breaking existing Android apps in some unexpected way, due to the many different ways people use InAppBrowser.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, doesn't this mean that existing apps that have cookies or local storage data for a certain webpage in InAppBrowser, will no longer have that data when they update the app to this version where the data directory changes?