Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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).

## [2.0.3]

### Fixes

- Keep the main app process active while the WebView is in the foreground, by binding it to the WebView's process. Without this, Android can freeze the main process during a browser session, so browser events stop being processed and the app never reacts to the page finishing ([RMET-5394](https://outsystemsrd.atlassian.net/browse/RMET-5394)).

## [2.0.2]

### 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>2.0.2</version>
<version>2.0.3</version>
</project>
3 changes: 3 additions & 0 deletions src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@
android:resource="@xml/file_paths" />
</provider>

<service
android:name=".OSIABKeepAliveService"
android:exported="false" />
</application>

<queries>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.outsystems.plugins.inappbrowser.osinappbrowserlib

import android.app.Service
import android.content.Intent
import android.os.Binder
import android.os.IBinder

/**
* Runs in the main app process. While the isolated WebView activity is bound to it,
* the main process is not eligible for OS app freezing, so it keeps processing
* browser events while the WebView is in the foreground.
*/
class OSIABKeepAliveService : Service() {
override fun onBind(intent: Intent?): IBinder = Binder()
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@ import android.Manifest
import android.app.Application
import android.app.Activity
import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.ServiceConnection
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.IBinder
import android.provider.MediaStore
import android.util.Log
import android.view.Gravity
Expand Down Expand Up @@ -42,6 +45,7 @@ import androidx.core.content.FileProvider
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import com.outsystems.plugins.inappbrowser.osinappbrowserlib.OSIABEvents
import com.outsystems.plugins.inappbrowser.osinappbrowserlib.OSIABKeepAliveService
import com.outsystems.plugins.inappbrowser.osinappbrowserlib.R
import com.outsystems.plugins.inappbrowser.osinappbrowserlib.helpers.OSIABPdfHelper
import com.outsystems.plugins.inappbrowser.osinappbrowserlib.models.OSIABToolbarPosition
Expand Down Expand Up @@ -73,6 +77,8 @@ open class OSIABWebViewActivity : AppCompatActivity() {

private var closeReceiver: BroadcastReceiver? = null

private var keepAliveConnection: ServiceConnection? = null

// for the browserPageLoaded event, which we only want to trigger on the first URL loaded in the WebView
private var isFirstLoad = true

Expand Down Expand Up @@ -139,6 +145,11 @@ open class OSIABWebViewActivity : AppCompatActivity() {
const val REQUEST_LOCATION_PERMISSION = 623
const val REQUEST_CAMERA_PERMISSION = 624
const val LOG_TAG = "OSIABWebViewActivity"
const val ISOLATED_PROCESS_SUFFIX = ":OSInAppBrowser"

private fun isIsolatedProcess(): Boolean =
Build.VERSION.SDK_INT >= Build.VERSION_CODES.P &&
Application.getProcessName().endsWith(ISOLATED_PROCESS_SUFFIX)
val errorsToHandle = listOf(
WebViewClient.ERROR_HOST_LOOKUP,
WebViewClient.ERROR_UNSUPPORTED_SCHEME,
Expand All @@ -153,15 +164,12 @@ open class OSIABWebViewActivity : AppCompatActivity() {
}

init {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
try {
val processName = Application.getProcessName()
if (processName.endsWith(":OSInAppBrowser")) {
WebView.setDataDirectorySuffix("OSInAppBrowser")
}
} catch (e: Exception) {
Log.d(LOG_TAG, "Suffix already set or error: ${e.message}")
try {
if (isIsolatedProcess()) {
WebView.setDataDirectorySuffix("OSInAppBrowser")
}
} catch (e: Exception) {
Log.d(LOG_TAG, "Suffix already set or error: ${e.message}")
}
}
}
Expand All @@ -180,6 +188,17 @@ open class OSIABWebViewActivity : AppCompatActivity() {

browserId = intent.getStringExtra(OSIABEvents.EXTRA_BROWSER_ID) ?: ""

// keep the main process out of the freezable state while the browser is in front,
// otherwise events queue and the app's close flow stalls until it unfreezes
if (isIsolatedProcess()) {
val connection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {}
override fun onServiceDisconnected(name: ComponentName?) {}
}
bindService(Intent(this, OSIABKeepAliveService::class.java), connection, Context.BIND_AUTO_CREATE)
keepAliveConnection = connection
Comment on lines +198 to +199

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: In accordance to the docs https://developer.android.com/reference/android/content/Context#bindService(android.content.Intent,%20android.content.ServiceConnection,%20int), the method can throw SecurityException, and we should call unbindService in that situation (and I guess if we do we don't need to assign keepAliveConnection. Can we add that here perhaps?

I don't think we need to fail the activity on that exception, as the webview would still function as is right?

}

// Register receiver for close commands from main process
closeReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
Expand Down Expand Up @@ -280,6 +299,14 @@ open class OSIABWebViewActivity : AppCompatActivity() {
}
closeReceiver = null
}
keepAliveConnection?.let {
try {
unbindService(it)
} catch (e: Exception) {
// Service may not be bound, ignore
}
keepAliveConnection = null
}
webView.destroy()
super.onDestroy()
}
Expand Down
Loading