Android WebView Integration
Introduction
TrustX is not only supported in browser flows but also allows for integration through WebView
objects such that users can implement the TrustX flow directly through their native applications.
This document will cover integration requirements for Android devices.
WebView Configuration
Android WebView
is a standard Android framework component located in android.webkit
package. The WebView
component has several configuration options that need to be set in order for it to correctly load the TrustX flow. Mandatory configuration options that need to be set are listed below:
- webChromeClient
- settings
- JavaScript interface
WebChromeClient Configuration
A WebChromeClient
is another class in Android used in conjunction with a WebView
component to provide additional customization and control over the behavior of web content displayed within an Android application. While
the WebViewClient
focuses on the loading and navigation aspects of the WebView
, the WebChromeClient
deals with interactions and events related to the Chrome browser
itself, such as JavaScript dialogs, permission requests, and progress
updates.
WebChromeClient
configuration for Daon TrustX flow includes overriding two callbacks:
onPermissionRequest(request: PermissionRequest?)
- This callback is triggered when certain permissions are requested from theWebView
. In case of TrustX flow, this is in regards toCAMERA
permissions. Therequest
parameter has all of the necessary information and options to handle permission request response actions, includinggrant()
anddeny()
calls. Origin URI (request?.origin
) should be checked every time this callback is triggered to make sure that the provided request came from the genuine WebApp.onConsoleMessage(consoleMessage: ConsoleMessage?)
- Is used to capture and handle messages logged to the JavaScript console. This override is optional and serves the purpose of giving more information about the flow that happens inside of theWebView
component.onShowFileChooser(webView: WebView?, filePathCallback: ValueCallback<Array<Uri>>?, fileChooserParams: FileChooserParams?): Boolean
- Is used to handle the file chooser event that is triggered by the user on the WebView component. Overriding is mandatory for the additional documents upload feature as the additional document images and/or files need to be uploaded from the file system of the device. Options for file choosing can differ between devices, but nevertheless should include camera capture and file manager options for the user to choose from.
A code sample of the component configuration is shown below (given in Kotlin syntax):
class CustomWebChromeClient(private val listener: WebChromeClientListener) : WebChromeClient() {
private lateinit var permissionRequest: PermissionRequest
override fun onPermissionRequest(request: PermissionRequest?) {
if (request != null) {
permissionRequest = request
listener.onRequestPermission(request.resources[0])
}
}
override fun onConsoleMessage(consoleMessage: ConsoleMessage?): Boolean {
...
return super.onConsoleMessage(consoleMessage)
}
fun onPermissionGranted() {
permissionRequest.grant(permissionRequest.resources)
}
override fun onShowFileChooser(
webView: WebView?,
filePathCallback: ValueCallback<Array<Uri>>?,
fileChooserParams: FileChooserParams?
): Boolean {
listener.onShowFilePicker(filePathCallback, fileChooserParams)
return true
}
}
Camera Permissions
WebView
handling of camera permissions requires that users override the onPermissionRequest as detailed in the section above.
In this example, permissions are granted for the WebView first by overriding the onPermissionRequest() method and sending a permissions request to the device.
Example:
//This section is requesting permissions for WebView.
class CustomWebChromeClient(private val listener: WebChromeClientListener) : WebChromeClient() {
private lateinit var permissionRequest: PermissionRequest
override fun onPermissionRequest(request: PermissionRequest?) {
if (request != null) {
permissionRequest = request
listener.onRequestPermission(request.resources[0])
}
}
fun onPermissionGranted() {
permissionRequest.grant(permissionRequest.resources)
}
}
Once the onPermissionRequest callback has been triggered, the camera permission request is sent via the listener to Activity implementation. The example below demonstrates how permissions are requested from the user and, if granted, the permission information is returned to the Chrome client using the onPermissionGranted() call.
Example:
// Handling application layer camera permissions
private val requestCameraPermissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean ->
if (!isGranted) {
Log.d(TAG, "isNotGranted: ")
} else {
Log.d(TAG, "isGranted: ")
customWebChromeClient.onPermissionGranted()
}
}
Settings Configuration
WebView
settings refer to a set of configuration options and parameters that control the behavior and functionality of a WebView
component. These settings allow developers to customize how web content is displayed and interacted with inside the WebView
. You can access and configure these settings using the WebSettings
class, which is typically obtained from the WebView
via the getSettings()
method.
WebView
settings configuration for Daon TrustX flow includes:
- useWideViewPort set to
true
- When set totrue
, it allows the web page's "viewport" to be scaled to fit the width of theWebView
control. - loadWithOverviewMode set to
true
- When set totrue
, theWebView
will load the web page and display it as if it were zoomed out to fit the width of theWebView
's viewport. - javaScriptEnabled set to
true
- When set totrue
, it allows JavaScript code embedded in the web pages displayed in theWebView
to execute. - mediaPlaybackRequiresUserGesture set to
false
- When set tofalse
, media elements on web pages will automatically start running when the page is loaded. This option is mandatory in order for camera capture screens to work without requiring any user interaction. - domStorageEnabled set to
true
- controls whether web page scripts are allowed to use the web storage feature of the Document Object Model (DOM). For TrustX use cases, it is mandatory that this value is set totrue
so that web pages can uselocalStorage
andsessionStorage
.
A code sample of the settings configuration is shown below (given in Kotlin syntax, but very similar in Java):
settings.apply {
useWideViewPort = true
loadWithOverviewMode = true
javaScriptEnabled = true
mediaPlaybackRequiresUserGesture = false
domStorageEnabled = true
}
JavaScript Interface Configuration
A JavaScript interface of the WebView
component is a mechanism that allows JavaScript code to run in a web page displayed within a WebView
. This allows the Javascript code to communicate with and invoke methods in the Android application's Java code. It enables bi-directional communication between JavaScript running in the WebView
and native Java code, making it possible to exchange data and trigger actions between the web page and the Android app.
Integration of JavaScript interface is fairly simple, and requires only adding it to the WebView
component using the addJavaScriptInterface
standard function. This call requires one additional parameter which is the name of the JavaScript interface that is being set. This name is used on the web side to indicate which interface is being set and used for this particular WebView
component.
A code sample of the Daon TrustX JavaScript interface setup is shown below (given in Kotlin syntax, but very similar in Java):
//This name is required to be set in order for WebView to work with Daon TrustX flow
private const val MOBILE_FORM_HANDLER = "MobileFormHandler"
...
val webView = findViewById(R.id.trustXWebView)
webView.addJavascriptInterface(trustXJavaScriptHelper, MOBILE_FORM_HANDLER)
...
And the implementation of JavaScript interface class TrustXJavaScriptHelper
is shown below:
class TrustXJavaScriptHelper {
fun webLoaded() {
/*
* Used to notify the Android app that the web page was successfully loaded.
* There are other mechanisms for this, but in Daon TrustX use case, we're
* ensuring that our own web page is being loaded because after this callback
* is received, Daon TrustX web app waits for the same callback from Android
* app to be triggered.
*/
}
fun webCompleted() {
/*
* Used to notify the Android app that the Daon TrustX flow has been completed.
*/
}
fun webTimeout() {
/*
* Indicates that an activity has expired during the TrustX flow.
*/
}
fun openCameraSettings() {
/*
* Used to notify the Android app that the WebApp is requesting opening of the application settings.
*/
}
fun error(message: String) {
/*
* Used to notify the Android app that the WebApp has encountered an error.
*/
}
}
The following table details all String messages that will be received from TrustX.
Event Name | Description |
---|---|
webLoaded | A safety feature to ensure that only the TrustX web app can be loaded. |
webCompleted | indicates the completion of the TrustX flow. |
webTimeout | Indicates that an activity has expired during the TrustX flow. |
openCameraSettings | Used to notify the Android app that the WebApp is requesting to open the application settings. |
error | Indicates an error has occurred. |
Example of full WebView component configuration
Below is the code example showing how to initialise, configure and load URL into WebView component using the above implementations:
class MainActivity : AppCompatActivity(), WebChromeClientListener {
private val MOBILE_FORM_HANDLER = "MobileFormHandler"
private lateinit var webView: WebView
private var filePathCallback: ValueCallback<Array<Uri>>? = null
private var fileChooserParams: WebChromeClient.FileChooserParams? = null
private lateinit var customWebChromeClient: CustomWebChromeClient
private val fileChooserLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { }
private val requestCameraPermissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { }
private val settingsLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { }
override fun onCreate(savedInstanceState: Bundle?) { }
override fun onBackPressed() { }
override fun onShowFilePicker(
filePathCallback: ValueCallback<Array<Uri>>?,
fileChooserParams: WebChromeClient.FileChooserParams?
) { }
override fun onRequestPermission(permission: String) { }
override fun onWebLoaded() { }
override fun onWebCompleted() { }
override fun onWebTimeout() { }
override fun openCameraSettings() { }
}
interface WebChromeClientListener { }
Note: The URL string that required for the webView.loadUrl()
should be the Process Definition Token URL. More information on Process Definitions could be found in the Process Definition guide.
Handling Android Back Action
This section will describe the configuration parameters, that are not tightly connected to the WebView
component integration, but are mandatory for the use cases in TrustX flow.
Activities and Fragments in Android framework are notified about the keyboard back action events through the onBackPressed
callback. In the Daon TrustX use case, this callback need to be overridden in the component that started the WebView
and the back action should be delegated to WebView
in the way that is shown below, to ensure that the navigation in the flow is correctly executed:
override fun onBackPressed() {
if (webView.canGoBack()) {
webView.goBack()
} else {
super.onBackPressed()
}
}
Android Manifest
The application should implement support for two Android permissions in order for the TrustX flow to work correctly:
- INTERNET permission - allows the application to access the internet (used in this flow to communicate with the WebApp).
- CAMERA permission - allows the application to access the camera (used in this flow to capture face and/ or documents).
Example:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
Known Issues and Limitations
One of the main issues that Android WebView integration with TrustX flow has shown is the very slow and often interrupted camera flow. It is best described as a “lagging” issue that happens when the camera is actively trying to capture and analyse frames. This can happen during the Face and Document capture steps. To ensure that this issue happens as rarely as possible, Daon have added several implementation insurances that the WebView
component is overloaded as little as possible memory-wise.
Some solutions included:
- Starting the whole
WebView
flow in the new process and enablinghardware acceleration
feature.
Code Sample:
<activity
android:name="com.example.DaonTrustXActivity"
android:exported="true"
android:process=":Onboarding"
android:hardwareAccelerated="true"
/>
- Clearing all of the cashed memory of the
WebView
after the completed session and before the start of the new session.
Code Sample:
webView.apply {
clearCache(true)
clearFormData()
clearHistory()
clearSslPreferences()
}
CookieManager.getInstance().apply {
removeAllCookies(null)
flush()
}
WebStorage.getInstance().deleteAllData()
webView.removeAllViews()
webView.destroy()
These options help to mitigate the occurrences of this particular issue, but intermittent issues may occur in some integrations.