Add Support for Web App Launch Trigger

Last edit: 2023.07.27

TomTom Digital Cockpit provides a means for app launching functionality to be triggered from an external source, like an App Store, by providing an implementation for AppLaunchTriggerService. For example, when a third-party app store broadcasts an intent to launch an app, the implementation of this interface could observe such a broadcast and trigger the launch functionality associated with that app type.

In this example, we will continue where we left off after adding an implementation for AppLaunchHandlerService to support launching web apps (see Add support for a custom non-Android app type), as adding app launch trigger for any App type requires having an implementation for AppLaunchHandlerService for the same app type.

In this example, we will be using the Android Debug Bridge (ADB) to fire a broadcast with the required information from the command-line. In production it would be expected that the broadcast would come from an external app.

The following steps describe how to add support for triggering web app launching by receiving a broadcast:

Create web app launch trigger service module

First, add the AppLaunchTriggerService dependency to the /build-logic/libraries.versions.toml file:

iviAppsuiteAppstoreApiServiceApplaunchtrigger = { module = "com.tomtom.ivi.appsuite:appsuite_appstore_api_service_applaunchtrigger", version.ref = "iviPlatform" }

Create a new module (for example, webapplaunchtrigger) under examples/applauncher/services, and add a Gradle build script.

Create a build.gradle.kts file:

1import com.tomtom.ivi.platform.gradle.api.framework.config.ivi
2
3ivi {
4 optInToExperimentalApis = true
5}
6
7dependencies {
8 api(project(":examples_applauncher_common_webapp"))
9 implementation(libraries.iviAppsuiteAppstoreApiServiceApplaunchtrigger)
10}

The app launch trigger module is an Android module, so it must also have an AndroidManifest.xml file.

Create a src/main/AndroidManifest.xml file:

1<?xml version="1.0" encoding="utf-8"?>
2<manifest xmlns:android="http://schemas.android.com/apk/res/android">
3 <permission android:name="com.example.ivi.example.applauncher.APP_LAUNCH_BROADCAST_PERMISSION" />
4</manifest>

Create web app launch trigger broadcast receiver

To trigger the launch of the web app, we need to be able to receive and extract the information sent through the broadcast. Therefore, we need to create a subclass of BroadcastReceiver (for example, WebAppLaunchTriggerBroadcastReceiver) and implement onReceive(Context, Intent), so we can extract the required information for launching a web app.

Let's assume that the web app URL value will be sent with a key name url.

Create src/main/kotlin/com/example/ivi/example/applauncher/services/webapplaunchtrigger/WebAppLaunchTriggerBroadcastReceiver.kt

1import android.content.BroadcastReceiver
2import android.content.Context
3import android.content.Intent
4import androidx.lifecycle.LiveData
5import androidx.lifecycle.MutableLiveData
6import com.example.ivi.example.applauncher.common.webapp.WebApp
7import com.tomtom.tools.android.api.resourceresolution.drawable.ResourceDrawableResolver
8import com.tomtom.tools.android.api.resourceresolution.string.StaticStringResolver
9
10class WebAppLaunchTriggerBroadcastReceiver : BroadcastReceiver() {
11
12 private val mutableTriggeredWebApp = MutableLiveData<WebApp>()
13 val triggeredWebApp: LiveData<WebApp> = mutableTriggeredWebApp
14
15 override fun onReceive(context: Context?, intent: Intent?) {
16 // [WebApp.displayName], [WebApp.summary] and [WebApp.icon] are not necessary for launching
17 // a [WebApp], therefore dummy data is assigned for these values.
18 intent?.apply {
19 mutableTriggeredWebApp.value = WebApp(
20 url = getStringExtra("url") ?: "",
21 displayName = StaticStringResolver("Dummy display name"),
22 summary = StaticStringResolver("Dummy summary"),
23 icon = ResourceDrawableResolver(R.drawable.ttivi_appstore_icon_invalidpackage)
24 )
25 }
26 }
27}

Implement web app launch trigger service

The app launch trigger service can be implemented by defining a class (for example, WebAppLaunchTriggerService) that inherits from the abstract AppLaunchTriggerServiceBase base class, and provides the required implementation for the AppLaunchTriggerService interface.

For an instance of WebAppLaunchTriggerBroadcastReceiver to receive a broadcast, we need to register it with an IntentFilter, which identifies the broadcast action.

Create src/main/kotlin/com/example/ivi/example/applauncher/services/webapplaunchtrigger/WebAppLaunchTriggerService.kt

1import android.content.IntentFilter
2import com.tomtom.ivi.appsuite.appstore.api.service.applaunchtrigger.AppLaunchTriggerServiceBase
3import com.tomtom.ivi.platform.framework.api.ipc.iviservice.IviDiscoverableServiceIdProvider
4import com.tomtom.ivi.platform.framework.api.ipc.iviservice.IviServiceHostContext
5
6class WebAppLaunchTriggerService(
7 iviServiceHostContext: IviServiceHostContext,
8 serviceIdProvider: IviDiscoverableServiceIdProvider
9) : AppLaunchTriggerServiceBase(iviServiceHostContext, serviceIdProvider) {
10
11 override fun onCreate() {
12 super.onCreate()
13
14 val webAppLaunchTriggerBroadcastReceiver = WebAppLaunchTriggerBroadcastReceiver()
15 val filter = IntentFilter(WEB_APP_LAUNCH_TRIGGER_ACTION_BROADCAST)
16
17 // Register the receiver with a permission to prevent it being triggered insecurely.
18 // Contact the 3rd-party app store provider to add the defined permission when sending the
19 // intent.
20 context.registerReceiver(
21 webAppLaunchTriggerBroadcastReceiver,
22 filter,
23 WEB_APP_BROADCAST_LAUNCH_TRIGGER_PERMISSION,
24 null)
25
26 webAppLaunchTriggerBroadcastReceiver.triggeredWebApp.observe(this) {
27 appLaunchTriggerEventListener.onAppLaunchTriggered(it)
28 }
29
30 serviceReady = true
31 }
32
33 private companion object {
34 const val WEB_APP_LAUNCH_TRIGGER_ACTION_BROADCAST =
35 "com.example.ivi.example.applauncher.WEB_APP_LAUNCH_ACTION"
36
37 const val WEB_APP_BROADCAST_LAUNCH_TRIGGER_PERMISSION =
38 "com.example.ivi.example.applauncher.APP_LAUNCH_BROADCAST_PERMISSION"
39 }
40}

Create web app launch trigger service host

Your module will also need to define a service host where the service will be running, as well as provide a service host builder. This can be achieved by creating two classes.

  • A WebAppLaunchTriggerServiceHost class:

    src/main/kotlin/com/example/ivi/example/applauncher/services/webapplaunchtrigger/WebAppLaunchTriggerServiceHost.kt

    1import com.tomtom.ivi.platform.framework.api.ipc.iviservice.IviDiscoverableServiceIdProvider
    2import com.tomtom.ivi.platform.framework.api.ipc.iviservice.IviServiceHostBase
    3import com.tomtom.ivi.platform.framework.api.ipc.iviservice.IviServiceHostContext
    4
    5class WebAppLaunchTriggerServiceHost(
    6iviServiceHostContext: IviServiceHostContext,
    7iviDiscoverableServiceIdProvider: IviDiscoverableServiceIdProvider
    8) : IviServiceHostBase(iviServiceHostContext) {
    9
    10 override val iviServices = setOf(
    11 WebAppLaunchTriggerService(iviServiceHostContext, iviDiscoverableServiceIdProvider)
    12 )
    13}
  • A WebAppLaunchTriggerServiceHostBuilder class:

    src/main/kotlin/com/example/ivi/example/applauncher/services/webapplaunchtrigger/WebAppLaunchTriggerServiceHostBuilder.kt

    1import com.tomtom.ivi.platform.framework.api.ipc.iviservice.IviServiceHostBase
    2import com.tomtom.ivi.platform.framework.api.ipc.iviservice.IviServiceHostBuilder
    3import com.tomtom.ivi.platform.framework.api.ipc.iviservice.IviServiceHostContext
    4
    5class WebAppLaunchTriggerServiceHostBuilder : IviServiceHostBuilder() {
    6
    7 override fun build(iviServiceHostContext: IviServiceHostContext): IviServiceHostBase =
    8 WebAppLaunchTriggerServiceHost(iviServiceHostContext) {
    9 getDiscoverableServiceId(it)
    10 }
    11
    12 companion object
    13}

Configure web app launch trigger service host deployment

Note: Every service host needs to be configured and registered in your application. This is necessary to know which service should be started with which implementation, when a client requires the access to a service API.

Define an IVI service host implementation in your gradle file. This can also be defined in a top-level gradle file (for example, iviservicehosts.gradle.kts) so it can be used in a multi-project build, including the tests.

Modify the examples/applauncher/iviservicehosts.gradle.kts file:

1/**
2 * Defines a configuration for the web app source provider service.
3 *
4 * The configuration specifies the service host implementation and the list of interfaces
5 * implemented by this service host.
6 */
7val webAppLaunchTriggerServiceHost by extra {
8 IviServiceHostConfig(
9 serviceHostBuilderName = "WebAppLaunchTriggerServiceHostBuilder",
10 implementationModule = ExampleModuleReference("examples_applauncher_services_webapplaunchtrigger"),
11 interfaces = listOf(
12 IviServiceInterfaceConfig(
13 serviceName = "AppLaunchTriggerService",
14 serviceId = "com.example.ivi.example.applauncher.services.webapplaunchtrigger",
15 serviceApiModule = IviAppsuiteModuleReference("appsuite_appstore_api_service_applaunchtrigger"),
16 multipleInstances = true
17 )
18 )
19 )
20}

Register the service host build configuration in the main application's build script.

Modify the examples/applauncher/app/build.gradle.kts file:

1apply(from = rootProject.file("examples/applauncher/iviservicehosts.gradle.kts"))
2
3val webAppLaunchTriggerServiceHost: IviServiceHostConfig by project.extra
4
5ivi {
6 application {
7 enabled = true
8 services {
9 // Add the web app launch trigger service host to the application.
10 addHost(webAppLaunchTriggerServiceHost)
11 }
12 }
13}

Send web app launch trigger broadcast

After setting up everything we need to test the implementation by sending a broadcast from our development machine. To do this we can use the adb command-line tool which provides a command for sending a broadcast.

Note: Make sure that the action sent through the broadcast is identical to the action defined in the IntentFilter that was previously registered for the broadcast. Also, you will need to temporarily comment out the broadcastPermission parameter when calling registerReceiver() in order to verify it with adb.

adb shell "am broadcast -a com.example.ivi.example.applauncher.WEB_APP_LAUNCH_ACTION --es url 'https://www.tomtom.com/'"