Create a Custom VPA Adaptation Service
The VPA Adaptation service provides an abstraction for a VPA engine, with which other TomTom Digital Cockpit components communicate.
The TomTom Digital Cockpit platform includes a stock implementation of the VPA Adaptation Service for the Alexa Auto VPA, and support for the Cerence Assistant VPA is in development. If TomTom Digital Cockpit does not support a specific VPA, or its stock implementation is not suitable for the customer's use case, you can create your own VPA Adaptation Service.
Example VPA app
The TomTom Digital Cockpit SDK comes with an example app showing how to create a custom VPA Adaptation
Service. See the
examples/vpa
directory.
Create a custom VPA Adaptation Service
The following sections describe how to create a custom VPA Adaptation service implementation.
Create a service implementation module
The new module depends on VpaAdaptationService
, declared in the package
com.tomtom.ivi.platform.vpa.api.service.vpaadaptation
.
/build-logic/libraries.versions.toml
iviPlatformVpaApiServiceVpaadaptation = { module = "com.tomtom.ivi.platform:platform_vpa_api_service_vpaadaptation", version.ref = "iviPlatform" }
1dependencies {2 implementation(libraries.iviPlatformVpaApiServiceVpaadaptation)3}
The VpaAdaptationService
API is experimental, and has to be explicitly opted
in.
1ivi {2 optInToExperimentalApis = true3}
Implement the VPA Adaptation Service
The VPA Adaptation service can be implemented by defining a class that inherits from the abstract
VpaAdaptationServiceBase
class and implements the methods defined in the
VpaAdaptationService
interface.
The service implementation requires a number of properties to be set for configuring itself with the
TomTom Digital Cockpit platform. The properties can be set by overriding the onCreate()
method:
src/main/kotlin/ExampleVpaService.kt
1override fun onCreate() {2 super.onCreate()34 vpaProperties = VpaProperties(5 vpaIdentifier = VpaIdentifier(name = "Example VPA"),6 supportedLocales = List<Locale> = listOf(Locale.US),7 supportedCountries = List<String> = listOf("USA")8 )910 vpaAuthenticationStatus = VpaAuthenticationStatus(AuthenticationState.AUTHENTICATED)1112 vpaAvailabilityState = VpaAvailabilityState.AVAILABLE1314 dialogueState = DialogueState.IDLE1516 alerts = emptyList()1718 hasPendingNotifications = false1920 vpaSettings = VpaSettings(21 wakeUpWordEnabled = false,22 activeLocales = emptyList(),23 enabledEarcons = EnumSet.noneOf(VpaEarconType::class.java),24 doNotDisturbModeEnabled = false,25 locationSharingEnabled = false,26 syncVehicleNavigationFavoritesEnabled = false,27 allowedPersonalData = emptyMap()28 )2930 serviceReady = true31}
Depending on the features supported by your VPA, you will need to override other functions of this service so that the VPA can react to certain actions by the user.
VPA Properties
Static information about the VPA, such as its name, supported locales and countries, are stored
in vpaProperties
.
src/main/kotlin/ExampleVpaService.kt
1vpaProperties = VpaProperties(2 vpaIdentifier = VpaIdentifier(name = "Example VPA"),3 supportedLocales = List<Locale> = listOf(Locale.US),4 supportedCountries = List<String> = listOf("USA")5)
Authentication Status
The current authentication state of the VPA is stored in vpaAuthenticationStatus
. If the VPA
supports user authentication, the logIn()
and logOut()
functions should be overridden to allow a
client to authenticate with or log out of the VPA service. These functions should update the
vpaAuthenticationStatus
to reflect the new situation.
src/main/kotlin/ExampleVpaService.kt
1override fun onCreate() {2 vpaAuthenticationStatus = VpaAuthenticationStatus(AuthenticationState.NOT_AUTHENTICATED)3}45override suspend fun logIn() {6 // TODO: Log in to the VPA.78 // If the login is successful - update the status.9 vpaAuthenticationStatus = VpaAuthenticationStatus(AuthenticationState.AUTHENTICATED)10}1112override suspend fun logOut() {13 // TODO: Log out of the VPA.1415 // If the logout is successful - update the status.16 vpaAuthenticationStatus = VpaAuthenticationStatus(AuthenticationState.NOT_AUTHENTICATED)17}
Refer to VpaAuthenticationStatus
for the details.
Availability State
Property vpaAvailabilityState
indicates whether this VPA is ready to be used by clients.
src/main/kotlin/ExampleVpaService.kt
vpaAvailabilityState = VpaAvailabilityState.AVAILABLE
Dialogue State
The state of the conversation with the VPA is reflected by dialogueState
. This helps a UI indicate
whether, for example, the VPA is waiting for the user to reply to a question. It is essential for
the service to monitor the VPA dialogue state and update the property accordingly.
src/main/kotlin/ExampleVpaService.kt
dialogueState = DialogueState.IDLE
Alerts
Property alerts
is used to hold information about the alerts that have been scheduled through
interaction with the VPA.
src/main/kotlin/ExampleVpaService.kt
alerts = emptyList()
Pending Notifications
Some of the VPA messages may not be played at once. Property hasPendingNotifications
informs a
client whether the VPA has voice notifications ready to be played.
src/main/kotlin/ExampleVpaService.kt
hasPendingNotifications = false
Settings
Property vpaSettings
holds a number of settings of the VPA. The VpaAdaptationService
provides
functions to update these settings; please refer to the VpaAdaptationService
API reference documentation for details.
src/main/kotlin/ExampleVpaService.kt
1override fun onCreate() {2 vpaSettings = VpaSettings(3 wakeUpWordEnabled = false,4 activeLocales = emptyList(),5 enabledEarcons = EnumSet.noneOf(VpaEarconType::class.java),6 doNotDisturbModeEnabled = false,7 locationSharingEnabled = false,8 syncVehicleNavigationFavoritesEnabled = false,9 allowedPersonalData = emptyMap()10 )11}
Push-To-Talk
To define how the VPA reacts to a push-to-talk button press, override the startListening()
function. In this function you would typically inform the VPA engine that the user is ready to talk
to the VPA.
src/main/kotlin/ExampleVpaService.kt
1override suspend fun startListening() {2 // TODO: Handle push-to-talk activation.3}
Privacy Mode
The privacy mode is controlled by the enablePrivacyMode()
function.
src/main/kotlin/ExampleVpaService.kt
1override suspend fun enablePrivacyMode(enable: Boolean) {2 // TODO: Enable or disable the privacy mode.3}
Please refer to the VpaAdaptationService
API reference documentation for
detailed information on its properties and functions.
Create a 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.
-
An
ExampleVpaServiceHost
class:
src/main/kotlin/ExampleVpaServiceHost.kt
1import com.tomtom.ivi.platform.framework.api.ipc.iviservice.IviDiscoverableServiceIdProvider2import com.tomtom.ivi.platform.framework.api.ipc.iviservice.IviServiceHostBase3import com.tomtom.ivi.platform.framework.api.ipc.iviservice.IviServiceHostContext45/**6 * An [ExampleVpaServiceHost] host server.7 */8internal class ExampleVpaServiceHost(9 iviServiceHostContext: IviServiceHostContext,10 iviDiscoverableServiceIdProvider: IviDiscoverableServiceIdProvider11) :12 IviServiceHostBase(iviServiceHostContext) {1314 override val iviServices = setOf(15 ExampleVpaService(iviServiceHostContext, iviDiscoverableServiceIdProvider)16 )17} -
An
ExampleVpaServiceHostBuilder
class:
src/main/kotlin/ExampleVpaServiceHostBuilder.kt
1/**2 * An [ExampleVpaServiceHost] builder used to build an [ExampleVpaService] host.3 */4class ExampleVpaServiceHostBuilder : IviServiceHostBuilder() {56 override fun build(iviServiceHostContext: IviServiceHostContext): IviServiceHostBase =7 ExampleVpaServiceHostBuilder(iviServiceHostContext) {8 getDiscoverableServiceId(it)9 }1011 companion object12}
Please ensure that the ExampleVpaServiceHostBuilder
class is added to the root of your module
hierarchy.
Configure the service host deployment
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.
/examples/vpa/iviservicehosts.gradle.kts
1import com.tomtom.ivi.buildsrc.dependencies.ExampleModuleReference2import com.tomtom.ivi.platform.gradle.api.common.dependencies.IviPlatformModuleReference3import com.tomtom.ivi.platform.gradle.api.common.iviapplication.config.IviServiceHostConfig4import com.tomtom.ivi.platform.gradle.api.common.iviapplication.config.IviServiceInterfaceConfig56/**7 * Defines a configuration for the Example VPA service.8 *9 * The configuration specifies the service host implementation and the list of interfaces10 * implemented by this service host.11 */12val exampleVpaServiceHost by extra {13 IviServiceHostConfig(14 serviceHostBuilderName = "ExampleVpaServiceHostBuilder",15 implementationModule = ExampleModuleReference("examples_vpa_service"),16 interfaces = listOf(17 IviServiceInterfaceConfig(18 serviceName = "VpaAdaptationService",19 serviceId = "com.example.ivi.example.vpa",20 serviceApiModule = IviPlatformModuleReference(21 "platform_vpa_api_service_vpaadaptation"22 )23 )24 )25 )26}
Register the service host build configuration in the main application's build script.
examples/vpa/app/build.gradle.kts
1import com.tomtom.ivi.platform.gradle.api.common.iviapplication.config.IviServiceHostConfig2import com.tomtom.ivi.platform.gradle.api.framework.config.ivi34// Define the service host configs as defined in the top-level `iviservicehosts.gradle.kts` file.5apply(from = rootProject.file("examples/vpa/iviservicehosts.gradle.kts"))67// Use Gradle's extra extensions to obtain the `exampleVpaServiceHost` config as defined in8// the top-level `iviservicehosts.gradle.kts` file.9val exampleVpaServiceHost: IviServiceHostConfig by project.extra1011ivi {12 application {13 enabled = true14 services {15 // Add the Example VPA service host to the application.16 addHost(exampleVpaServiceHost)17 }18 }19}2021// The rest of the build script, dependencies, etc.