Kotlin (Android) Installation
Integrate the Asurion Widget into your native Android application using Android WebView. This approach loads the web widget inside a native WebView component, providing a seamless experience on Android devices.
Prerequisites
- Android 5.0 (API level 21) or higher
- Android Studio Arctic Fox or higher
- Kotlin 1.5 or higher
Setup
Add internet permission to your AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
Basic Usage
Create an Activity that renders the widget inside a WebView:
import android.annotation.SuppressLint
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.webkit.WebChromeClient
import android.webkit.WebSettings
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.appcompat.app.AppCompatActivity
class AsurionWidgetActivity : AppCompatActivity() {
private lateinit var webView: WebView
private val widgetId = "<WIDGET_ID>"
private val context = mapOf("userId" to "user-123", "platform" to "android")
private val trackingParameters = mapOf("utm_source" to "mobile-app", "utm_medium" to "webview")
@SuppressLint("SetJavaScriptEnabled")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
webView = WebView(this).apply {
layoutParams = android.view.ViewGroup.LayoutParams(
android.view.ViewGroup.LayoutParams.MATCH_PARENT,
android.view.ViewGroup.LayoutParams.MATCH_PARENT
)
}
setContentView(webView)
setupWebView()
loadWidget()
}
@SuppressLint("SetJavaScriptEnabled")
private fun setupWebView() {
webView.settings.apply {
javaScriptEnabled = true
domStorageEnabled = true
mediaPlaybackRequiresUserGesture = false
loadWithOverviewMode = true
useWideViewPort = true
cacheMode = WebSettings.LOAD_DEFAULT
javaScriptCanOpenWindowsAutomatically = true
setSupportMultipleWindows(true)
}
webView.webViewClient = WebViewClient()
webView.setBackgroundColor(android.graphics.Color.TRANSPARENT)
// Handle window.open() calls from JavaScript
webView.webChromeClient = object : WebChromeClient() {
override fun onCreateWindow(
view: WebView?,
isDialog: Boolean,
isUserGesture: Boolean,
resultMsg: android.os.Message?
): Boolean {
val result = view?.hitTestResult
val url = result?.extra
if (url != null) {
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
}
return false
}
}
}
private fun loadWidget() {
val html = generateWidgetHTML()
webView.loadDataWithBaseURL(null, html, "text/html", "UTF-8", null)
}
private fun generateWidgetHTML(): String {
return """
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<style>
* { margin: 0; padding: 0; }
html, body { height: 100%; width: 100%; overflow: hidden; }
</style>
<script>
(function (w, d, s, o, f, a, js, fjs) {
w[o] = function () {
const [action, ...params] = arguments;
(w[o].q = w[o].q || []).push([action, ...params]);
};
(js = d.createElement(s)), (fjs = d.getElementsByTagName(s)[0]);
js.id = o;
js.src = f;
js.async = 1;
js.setAttribute('data-widget-id', a.id);
js.setAttribute('data-variant', a.variant);
w[o + '_context'] = a.context;
w[o + '_trackingParameters'] = a.trackingParameters;
fjs.parentNode.insertBefore(js, fjs);
// Forward widget events to native
w[o]('subscribe', 'state', function (state) {
AndroidBridge.onWidgetEvent(JSON.stringify({ type: 'state', payload: state }));
});
w[o]('subscribe', 'options', function (options) {
AndroidBridge.onWidgetEvent(JSON.stringify({ type: 'options', payload: options }));
});
})(
window,
document,
'script',
'_asurion_widget',
'https://app.widget-loader.service-initiation.asurion.com/widget-loader.js',
{
id: '$widgetId',
variant: 'embedded',
context: ${org.json.JSONObject(context)},
trackingParameters: ${org.json.JSONObject(trackingParameters)},
}
);
</script>
</head>
<body></body>
</html>
""".trimIndent()
}
override fun onBackPressed() {
if (webView.canGoBack()) {
webView.goBack()
} else {
super.onBackPressed()
}
}
}
Jetpack Compose Integration
For Jetpack Compose applications, create a composable wrapper:
import android.annotation.SuppressLint
import android.content.Intent
import android.graphics.Color
import android.net.Uri
import android.webkit.WebChromeClient
import android.webkit.WebSettings
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.viewinterop.AndroidView
import org.json.JSONObject
@SuppressLint("SetJavaScriptEnabled")
@Composable
fun AsurionWidget(
widgetId: String,
modifier: Modifier = Modifier,
context: Map<String, Any>? = null,
trackingParameters: Map<String, String>? = null
) {
val localContext = LocalContext.current
AndroidView(
modifier = modifier,
factory = { androidContext ->
WebView(androidContext).apply {
settings.apply {
javaScriptEnabled = true
domStorageEnabled = true
mediaPlaybackRequiresUserGesture = false
loadWithOverviewMode = true
useWideViewPort = true
cacheMode = WebSettings.LOAD_DEFAULT
javaScriptCanOpenWindowsAutomatically = true
setSupportMultipleWindows(true)
}
webViewClient = WebViewClient()
setBackgroundColor(Color.TRANSPARENT)
webChromeClient = object : WebChromeClient() {
override fun onCreateWindow(
view: WebView?,
isDialog: Boolean,
isUserGesture: Boolean,
resultMsg: android.os.Message?
): Boolean {
val url = view?.hitTestResult?.extra
if (url != null) {
localContext.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
}
return false
}
}
}
},
update = { webView ->
val html = generateWidgetHTML(widgetId, context, trackingParameters)
webView.loadDataWithBaseURL(null, html, "text/html", "UTF-8", null)
}
)
}
private fun generateWidgetHTML(
widgetId: String,
context: Map<String, Any>?,
trackingParameters: Map<String, String>?
): String {
val contextJson = context?.let { JSONObject(it).toString() } ?: "{}"
val trackingJson = trackingParameters?.let { JSONObject(it.toMap()).toString() } ?: "{}"
return """
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<style>
* { margin: 0; padding: 0; }
html, body { height: 100%; width: 100%; overflow: hidden; }
</style>
<script>
(function (w, d, s, o, f, a, js, fjs) {
w[o] = function () {
const [action, ...params] = arguments;
(w[o].q = w[o].q || []).push([action, ...params]);
};
(js = d.createElement(s)), (fjs = d.getElementsByTagName(s)[0]);
js.id = o;
js.src = f;
js.async = 1;
js.setAttribute('data-widget-id', a.id);
js.setAttribute('data-variant', a.variant);
w[o + '_context'] = a.context;
w[o + '_trackingParameters'] = a.trackingParameters;
fjs.parentNode.insertBefore(js, fjs);
// Forward widget events to native
w[o]('subscribe', 'state', function (state) {
AndroidBridge.onWidgetEvent(JSON.stringify({ type: 'state', payload: state }));
});
w[o]('subscribe', 'options', function (options) {
AndroidBridge.onWidgetEvent(JSON.stringify({ type: 'options', payload: options }));
});
})(
window,
document,
'script',
'_asurion_widget',
'https://app.widget-loader.service-initiation.asurion.com/widget-loader.js',
{
id: '$widgetId',
variant: 'embedded',
context: $contextJson,
trackingParameters: $trackingJson,
}
);
</script>
</head>
<body></body>
</html>
""".trimIndent()
}
// Usage in Compose
@Composable
fun WidgetScreen() {
AsurionWidget(
widgetId = "<WIDGET_ID>",
modifier = Modifier.fillMaxSize(),
context = mapOf("userId" to "user-123", "platform" to "android")
)
}
Configuration Options
| Option | Type | Required | Description |
|---|---|---|---|
id | string | true | Your unique widget identifier provided by Asurion |
variant | string | true | Widget variant type. For Android WebView, only 'embedded' is supported |
context | object | false | Additional context data to pass to the widget. Can contain any key-value pairs |
trackingParameters | object | false | Tracking parameters that will be appended to external links within the widget. Must be string key-value pairs |
Communicating with the Widget
You can send commands to the widget using the evaluateJavascript method:
class AsurionWidgetActivity : AppCompatActivity() {
private lateinit var webView: WebView
// ... onCreate, setupWebView, loadWidget, generateWidgetHTML ...
fun extendContext(data: Map<String, Any>) {
val jsonString = org.json.JSONObject(data).toString()
webView.evaluateJavascript("window._asurion_widget('extendContext', $jsonString);", null)
}
fun extendTrackingParameters(params: Map<String, String>) {
val jsonString = org.json.JSONObject(params).toString()
webView.evaluateJavascript("window._asurion_widget('extendTrackingParameters', $jsonString);", null)
}
}
WebView Fragment
For more flexible usage, you can create a Fragment:
import android.annotation.SuppressLint
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.webkit.WebSettings
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.fragment.app.Fragment
class AsurionWidgetFragment : Fragment() {
private var webView: WebView? = null
private var widgetId: String = ""
private var context: Map<String, Any>? = null
private var trackingParameters: Map<String, String>? = null
companion object {
private const val ARG_WIDGET_ID = "widget_id"
fun newInstance(widgetId: String): AsurionWidgetFragment {
return AsurionWidgetFragment().apply {
arguments = Bundle().apply {
putString(ARG_WIDGET_ID, widgetId)
}
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
widgetId = arguments?.getString(ARG_WIDGET_ID) ?: ""
}
@SuppressLint("SetJavaScriptEnabled")
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
webView = WebView(requireContext()).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
settings.apply {
javaScriptEnabled = true
domStorageEnabled = true
mediaPlaybackRequiresUserGesture = false
loadWithOverviewMode = true
useWideViewPort = true
cacheMode = WebSettings.LOAD_DEFAULT
}
webViewClient = WebViewClient()
setBackgroundColor(android.graphics.Color.TRANSPARENT)
}
loadWidget()
return webView!!
}
private fun loadWidget() {
val html = generateWidgetHTML()
webView?.loadDataWithBaseURL(null, html, "text/html", "UTF-8", null)
}
private fun generateWidgetHTML(): String {
// Same HTML generation as in Activity example
return """
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<style>
* { margin: 0; padding: 0; }
html, body { height: 100%; width: 100%; overflow: hidden; }
</style>
<script>
(function (w, d, s, o, f, a, js, fjs) {
w[o] = function () {
const [action, ...params] = arguments;
(w[o].q = w[o].q || []).push([action, ...params]);
};
(js = d.createElement(s)), (fjs = d.getElementsByTagName(s)[0]);
js.id = o;
js.src = f;
js.async = 1;
js.setAttribute('data-widget-id', a.id);
js.setAttribute('data-variant', a.variant);
w[o + '_context'] = a.context;
w[o + '_trackingParameters'] = a.trackingParameters;
fjs.parentNode.insertBefore(js, fjs);
// Forward widget events to native
w[o]('subscribe', 'state', function (state) {
AndroidBridge.onWidgetEvent(JSON.stringify({ type: 'state', payload: state }));
});
w[o]('subscribe', 'options', function (options) {
AndroidBridge.onWidgetEvent(JSON.stringify({ type: 'options', payload: options }));
});
})(
window,
document,
'script',
'_asurion_widget',
'https://app.widget-loader.service-initiation.asurion.com/widget-loader.js',
{
id: '$widgetId',
variant: 'embedded',
}
);
</script>
</head>
<body></body>
</html>
""".trimIndent()
}
fun extendContext(data: Map<String, Any>) {
val jsonString = org.json.JSONObject(data).toString()
webView?.evaluateJavascript(
"window._asurion_widget('extendContext', $jsonString);",
null
)
}
override fun onDestroyView() {
webView?.destroy()
webView = null
super.onDestroyView()
}
}
Subscribing to Widget Events
The widget forwards events to native code using a JavaScript interface (configured in Basic Usage above). Handle these events by adding a JavaScript interface to your WebView:
class AsurionWidgetActivity : AppCompatActivity() {
private lateinit var webView: WebView
// ...
@SuppressLint("SetJavaScriptEnabled")
private fun setupWebView() {
webView.settings.apply {
javaScriptEnabled = true
// ... other settings ...
}
webView.addJavascriptInterface(WidgetEventBridge(), "AndroidBridge")
// ... other setup ...
}
inner class WidgetEventBridge {
@android.webkit.JavascriptInterface
fun onWidgetEvent(eventJson: String) {
runOnUiThread {
val event = org.json.JSONObject(eventJson)
val type = event.getString("type")
when (type) {
"state" -> {
val payload = event.getString("payload")
println("Widget state: $payload")
}
"options" -> {
val payload = event.getJSONObject("payload")
println("Widget options: $payload")
}
}
}
}
}
}