Add Support for a Custom Non-Android App Type

Last edit: 2023.07.27

For a custom, non-Android, app type, a means to access the list of available apps to display in the App Launcher and also a means to launch these apps will be required. To do this the AppSourceProviderService and AppLaunchHandlerService will need to be implemented.

The following steps describe how to create source provider and launch handler service implementations, using the example of a web app:

Create the web app type

The new app type will need to be visible from a number of other modules, so create a new module for it under examples/applauncher/common. Name the module webapp, 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 // The App base class is currently flagged as experimental, and has to be explicitly opted in.
5 optInToExperimentalApis = true
6}
7
8dependencies {
9 implementation(libraries.iviAppsuiteAppstoreApiCommonModel)
10}

Declare an app store dependency in the /build-logic/libraries.versions.toml file:

iviAppsuiteAppstoreApiCommonModel = { module = "com.tomtom.ivi.appsuite:appsuite_appstore_api_common_model", version.ref = "iviPlatform" }

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

Create a src/main/AndroidManifest.xml file:

<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.example.ivi.example.applauncher.common.webapp" />

Declare the custom app type.

Create src/main/kotlin/com/example/ivi/example/applauncher/common/webapp/WebApp.kt

1import com.tomtom.ivi.appsuite.appstore.api.common.model.App
2import com.tomtom.tools.android.api.resourceresolution.drawable.DrawableResolver
3import com.tomtom.tools.android.api.resourceresolution.string.StringResolver
4import kotlinx.parcelize.Parcelize
5
6@Parcelize
7class WebApp(
8 val url: String,
9 override val displayName: StringResolver,
10 override val summary: StringResolver,
11 override val icon: DrawableResolver,
12) : App {
13
14 override val id: String = "${this::class.java.simpleName}.$url"
15
16 override fun equals(other: Any?): Boolean {
17 if (this === other) return true
18 if (javaClass != other?.javaClass) return false
19
20 other as WebApp
21
22 if (url != other.url) return false
23
24 return true
25 }
26
27 override fun hashCode(): Int = url.hashCode()
28}

Create the web app source provider

The following steps describe how to create a custom app source provider service implementation:

Create the web app source provider service module

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

iviAppsuiteAppstoreApiServiceAppsourceprovider = { module = "com.tomtom.ivi.appsuite:appsuite_appstore_api_service_appsourceprovider", version.ref = "iviPlatform" }

Create a module for the AppSourceProviderService implementation under examples/applauncher/services (for example examples/applauncher/services/webappsourceprovider) 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 // The AppSourceProviderService API is currently an experimental feature, and has to be
5 // explicitly opted in.
6 optInToExperimentalApis = true
7}
8dependencies {
9 api(project(":examples_applauncher_common_webapp"))
10 implementation(libraries.iviAppsuiteAppstoreApiServiceAppsourceprovider)
11}

The app source provider service module is an Android module, so it must also have an AndroidManifest.xml file.

Create a src/main/AndroidManifest.xml file:

<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.example.ivi.example.applauncher.services.webappsourceprovider" />

Implement the web app source provider service

The app source provider service can be implemented by defining a class (for example, WebAppSourceProviderService) that inherits from the abstract AppSourceProviderServiceBase base class, and implements the methods defined in the AppSourceProviderService interface.

The service implementation needs to set a number of properties for configuring itself with the TomTom Digital Cockpit platform. Please refer to the AppSourceProviderService API reference documentation for detailed information on these properties.

The properties can be set by overriding the onCreate() method.

Create src/main/kotlin/com/example/ivi/example/applauncher/services/webappsourceprovider/WebAppSourceProviderService.kt

1import com.tomtom.ivi.appsuite.appstore.api.common.model.ParcelableAppClass
2import com.tomtom.ivi.appsuite.appstore.api.service.appsourceprovider.AppSourceProviderServiceBase
3import com.tomtom.ivi.example.common.webapp.WebApp
4import com.tomtom.ivi.platform.framework.api.ipc.iviservice.IviDiscoverableServiceIdProvider
5import com.tomtom.ivi.platform.framework.api.ipc.iviservice.IviServiceHostContext
6import com.tomtom.tools.android.api.resourceresolution.drawable.ResourceDrawableResolver
7import com.tomtom.tools.android.api.resourceresolution.string.StaticStringResolver
8
9class WebAppSourceProviderService(
10 iviServiceHostContext: IviServiceHostContext,
11 serviceIdProvider: IviDiscoverableServiceIdProvider
12) : AppSourceProviderServiceBase(iviServiceHostContext, serviceIdProvider) {
13
14 override fun onCreate() {
15 super.onCreate()
16
17 appStore = null
18
19 // An IVI service interface can use only [Parcelable] types, so the [supportedAppClass]
20 // must be returned inside a [Parcelable] wrapper class.
21 supportedAppClass = ParcelableAppClass(WebApp::class.java)
22
23 installedApps = listOf(
24 WebApp(
25 url = "https://www.tomtom.com/",
26 displayName = StaticStringResolver("TomTom"),
27 summary = StaticStringResolver("TomTom Web"),
28 icon = ResourceDrawableResolver(R.drawable.ttivi_appstore_icon_invalidpackage)
29 )
30 )
31 }
32
33 override fun onRequiredPropertiesInitialized() {
34 serviceReady = true
35 }
36}

Create the web app source provider 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 WebAppSourceProviderServiceHost class:

    src/main/kotlin/com/example/ivi/example/applauncher/services/webappsourceprovider/WebAppSourceProviderServiceHost.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 WebAppSourceProviderServiceHost(
    6 iviServiceHostContext: IviServiceHostContext,
    7 iviDiscoverableServiceIdProvider: IviDiscoverableServiceIdProvider
    8) : IviServiceHostBase(iviServiceHostContext) {
    9
    10 override val iviServices = setOf(
    11 WebAppSourceProviderService(iviServiceHostContext, iviDiscoverableServiceIdProvider)
    12 )
    13}
  • A WebAppSourceProviderServiceHostBuilder class:

    src/main/kotlin/com/example/ivi/example/applauncher/services/webappsourceprovider/WebAppSourceProviderServiceHostBuilder.kt

    1/**
    2 * A [WebAppSourceProviderServiceHost] builder used to build a [WebAppSourceProviderService]
    3 * host.
    4 */
    5import com.tomtom.ivi.platform.framework.api.ipc.iviservice.IviServiceHostBase
    6import com.tomtom.ivi.platform.framework.api.ipc.iviservice.IviServiceHostBuilder
    7import com.tomtom.ivi.platform.framework.api.ipc.iviservice.IviServiceHostContext
    8
    9class WebAppSourceProviderServiceHostBuilder : IviServiceHostBuilder() {
    10
    11 override fun build(iviServiceHostContext: IviServiceHostContext): IviServiceHostBase =
    12 WebAppSourceProviderServiceHost(iviServiceHostContext) {
    13 getDiscoverableServiceId(it)
    14 }
    15
    16 companion object
    17}

Configure the web app source provider 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 */
7
8val webAppSourceProviderServiceHost by extra {
9 IviServiceHostConfig(
10 serviceHostBuilderName = "WebAppSourceProviderServiceHostBuilder",
11 implementationModule = ExampleModuleReference("services_webappsourceprovider"),
12 interfaces = listOf(
13 IviServiceInterfaceConfig(
14 serviceName = "AppSourceProviderService",
15 serviceId = "com.tomtom.ivi.example.service.webappsourceprovider",
16 serviceApiModule = IviAppsuiteModuleReference("appsuite_appstore_api_service_appsourceprovider"),
17 multipleInstances = true
18 )
19 )
20 )
21}

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 webAppSourceProviderServiceHost: IviServiceHostConfig by project.extra
4
5ivi {
6 application {
7 enabled = true
8 services {
9 // Add the web app source provider service host to the application.
10 addHost(webAppSourceProviderServiceHost)
11 }
12 }
13}

Create the web app launch handler

The following steps describe how to create a web app launch handler service implementation:

Create the web app launch handler service module

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

iviAppsuiteAppstoreApiServiceApplaunchhandler = { module = "com.tomtom.ivi.appsuite:appsuite_appstore_api_service_applaunchhandler", version.ref = "iviPlatform" }

Create a module for the AppLaunchHandlerService implementation under examples/applauncher/services (for example examples/applauncher/services/webapplaunchhandler) 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 // The AppLaunchHandlerService API is currently an experimental feature, and has to be
5 // explicitly opted in.
6 optInToExperimentalApis = true
7}
8dependencies {
9 api(project(":examples_applauncher_common_webapp"))
10 implementation(libraries.iviAppsuiteAppstoreApiServiceApplaunchhandler)
11}

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

Create a src/main/AndroidManifest.xml file:

<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.example.ivi.example.applauncher.services.webapplaunchhandler" />

Implement the web app launch handler service

The app launch handler service can be implemented by defining a class (for example, WebAppLaunchHandlerService) that inherits from the abstract AppLaunchHandlerServiceBase base class, and implements the methods defined in the AppLaunchHandlerService interface.

The service implementation needs to set a number of properties for configuring itself with the TomTom Digital Cockpit platform. Please refer to the AppLaunchHandlerService API reference documentation for detailed information on these properties.

In this example we will launch the web app in the default browser app, whereas in production, it would be expected to launch the web app within a WebView within a TomTom Digital Cockpit panel.

The properties can be set by overriding the onCreate() method.

Create src/main/kotlin/com/example/ivi/example/applauncher/services/webapplaunchhandler/WebAppLaunchHandlerService.kt

1import android.content.Intent
2import android.net.Uri
3import com.tomtom.ivi.appsuite.appstore.api.common.model.App
4import com.tomtom.ivi.appsuite.appstore.api.common.model.ParcelableAppClass
5import com.tomtom.ivi.appsuite.appstore.api.service.applaunchhandler.AppLaunchHandlerServiceBase
6import com.tomtom.ivi.example.common.webapp.WebApp
7import com.tomtom.ivi.platform.framework.api.ipc.iviservice.IviDiscoverableServiceIdProvider
8import com.tomtom.ivi.platform.framework.api.ipc.iviservice.IviServiceHostContext
9
10class WebAppLaunchHandlerService(
11 private val iviServiceHostContext: IviServiceHostContext,
12 serviceIdProvider: IviDiscoverableServiceIdProvider
13) : AppLaunchHandlerServiceBase(iviServiceHostContext, serviceIdProvider) {
14
15 override fun onCreate() {
16 super.onCreate()
17
18
19 // An IVI service interface can use only [Parcelable] types, so the [supportedAppClass]
20 // must be returned inside a [Parcelable] wrapper class.
21 supportedAppClass = ParcelableAppClass(WebApp::class.java)
22 }
23
24 override fun onRequiredPropertiesInitialized() {
25 serviceReady = true
26 }
27
28 override suspend fun launchApp(app: App) {
29 require(app is WebApp)
30 launchWebAppInBrowser(app)
31 }
32
33 private fun launchWebAppInBrowser(app: WebApp) {
34 val webIntent = Intent(Intent.ACTION_VIEW, Uri.parse(app.url))
35 iviServiceHostContext.context.startActivity(webIntent)
36 }
37}

Create the web app launch handler 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 WebAppLaunchHandlerServiceHost class:

    src/main/kotlin/com/example/ivi/example/applauncher/services/webapplaunchhandler/WebAppLaunchHandlerServiceHost.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 WebAppLaunchHandlerServiceHost(
    6 iviServiceHostContext: IviServiceHostContext,
    7 iviDiscoverableServiceIdProvider: IviDiscoverableServiceIdProvider
    8) : IviServiceHostBase(iviServiceHostContext) {
    9
    10 override val iviServices = setOf(
    11 WebAppLaunchHandlerService(iviServiceHostContext, iviDiscoverableServiceIdProvider)
    12 )
    13}
  • A WebAppLaunchHandlerServiceHostBuilder class:

    src/main/kotlin/com/example/ivi/example/applauncher/services/webapplaunchhandler/WebAppLaunchHandlerServiceHostBuilder.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 WebAppLaunchHandlerServiceHostBuilder : IviServiceHostBuilder() {
    6
    7 override fun build(iviServiceHostContext: IviServiceHostContext): IviServiceHostBase =
    8 WebAppLaunchHandlerServiceHost(iviServiceHostContext) {
    9 getDiscoverableServiceId(it)
    10 }
    11
    12 companion object
    13}

Configure the web app launch handler 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 launch handler service.
3 *
4 * The configuration specifies the service host implementation and the list of interfaces
5 * implemented by this service host.
6 */
7
8val webAppLaunchHandlerServiceHost by extra {
9 IviServiceHostConfig(
10 serviceHostBuilderName = "WebAppLaunchHandlerServiceHostBuilder",
11 implementationModule = ExampleModuleReference("services_webapplaunchhandler"),
12 interfaces = listOf(
13 IviServiceInterfaceConfig(
14 serviceName = "AppLaunchHandlerService",
15 serviceId = "com.tomtom.ivi.example.service.webapplaunchhandler",
16 serviceApiModule = IviAppsuiteModuleReference("appsuite_appstore_api_service_applaunchhandler"),
17 multipleInstances = true
18 )
19 )
20 )
21}

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 webAppLaunchHandlerServiceHost: IviServiceHostConfig by project.extra
4
5ivi {
6 application {
7 enabled = true
8 services {
9 // Add the web app launch handler service host to the application.
10 addHost(webAppLaunchHandlerServiceHost)
11 }
12 }
13}