Create a Frontend Plugin

Last edit: 2023.07.27

For an introduction to frontend plugins in the TomTom Digital Cockpit platform, see (Frontend Plugins)

Introduction

In this example, we will create a new frontend for managing an account on the device. It will provide a login screen where you can enter a username and a password to login, and if the user is logged in, you have the option to logout again. We will also add a menu item to the main menu that will be associated to the new frontend. The final step will be to let the new frontend replace TomTom Digital Cockpit's user profile frontend.

The source for this example can be found in the following directories in the examples source:

Creating a frontend and the menu item consists of a number of steps:

All the code snippets in this guide can also be found in the TomTom Digital Cockpit example application.

Creating the frontend class

Create a new frontend by deriving the Frontend framework class.

src/main/kotlin/com/example/ivi/example/plugin/frontend/AccountFrontend.kt

1import com.tomtom.ivi.platform.frontend.api.common.frontend.Frontend
2import com.tomtom.ivi.platform.frontend.api.common.frontend.FrontendContext
3
4internal class AccountFrontend(frontendContext: FrontendContext) : Frontend(frontendContext) {
5 // ...
6}

There are no abstract methods in the Frontend class, but some methods, like the lifecycle ones (see the following methods), are good to consider implementing.

Frontend lifecycle methods

  • onCreate - callback when the frontend is created.
  • onDestroy - callback when the frontend is about to get destroyed.

Showing panels on the screen

There are two callbacks when an event is triggered to show a TaskPanel on the screen.

  • createMainTaskPanel - override it to display a single TaskPanel when the UI is shown.
  • openTaskPanels - override it when more control is needed over which panels should be shown.

Note: A frontend class must override only one of these two methods.

Creating the frontend-builder class

Add an AccountFrontendBuilder class, derived from the FrontendBuilder class. Override the build() method in the class and return a new instance of the AccountFrontend class.

src/main/kotlin/com/example/ivi/example/plugin/frontend/AccountFrontendBuilder.kt

1import com.tomtom.ivi.platform.frontend.api.common.frontend.Frontend
2import com.tomtom.ivi.platform.frontend.api.common.frontend.FrontendBuilder
3import com.tomtom.ivi.platform.frontend.api.common.frontend.FrontendContext
4
5class AccountFrontendBuilder: FrontendBuilder() {
6
7 override fun build(frontendContext: FrontendContext): Frontend =
8 AccountFrontend(frontendContext)
9}

The builder class must follow a specific naming convention. It must have a "FrontendBuilder" suffix and must start with an upper case character.

Creating the panel

There are a number of specialized Panel classes that can be used in the platform, see the package com.tomtom.ivi.platform.frontend.api.common.frontend.panels). For this example we will create a Panel class that inherits from the TaskPanel class.

A TaskPanel is typically launched by tapping one of the menu items, like opening Contacts; or some other UI event, like opening the Climate panel. It encapsulates a task that the user may perform, typically away from the map, going back to the map when the task is finished.

Derive from the TaskPanel class, and override the createInitialFragmentInitializer() method, which should return a new instance of the IviFragment class (described further down).

src/main/kotlin/com/example/ivi/example/plugin/frontend/login/AccountLoginPanel.kt

1internal class AccountLoginPanel(frontendContext: FrontendContext) :
2 TaskPanel(frontendContext) {
3
4 override fun createInitialFragmentInitializer() =
5 IviFragment.Initializer(AccountLoginFragment(), this)
6}

Also create a ViewModel class, derived from the FrontendViewModel class. The ViewModel is the ViewModel in the Model-View-ViewModel (MVVM) pattern, whose role is to expose streams of data relevant to the view and streams of events to the model.

src/main/kotlin/com/example/ivi/example/plugin/frontend/login/AccountLoginViewModel.kt

1internal class AccountLoginViewModel(panel: AccountLoginPanel) :
2 FrontendViewModel<AccountLoginPanel>(panel) {
3
4 val username = MutableLiveData("")
5 val password = MutableLiveData("")
6 // ...
7
8 fun onLoginClick() {
9 // ...
10 }
11}

Finally create a Fragment class derived from IviFragment and using the newly created Panel and ViewModel classes, overriding the viewFactory property. The TomTom Digital Cockpit platform is designed to work well with the MVVM pattern, and this is used in the onCreateView callback as a convenience to inflate a data binding layout and use that in the fragment. If an onCreateView custom implementation is preferred, the viewFactory property can be left as null instead.

src/main/kotlin/com/example/ivi/example/plugin/frontend/login/AccountLoginFragment.kt

1internal class AccountLoginFragment :
2 IviFragment<AccountLoginPanel, AccountLoginViewModel>(AccountLoginViewModel::class) {
3
4 override val viewFactory = ViewFactory(TtiviAccountLoginFragmentBinding::inflate)
5}

See this expressions page for more information on how data-binding works in Android and how the ViewModel class binds to the XML layout. It also explains how the TtiviAccountLoginFragmentBinding class that you pass to the ViewFactory is auto-generated.

Creating a menu item

In this tutorial a menu item is added to the main menu that will open the main task panel of the AccountFrontend. To add the menu item to the main menu we need a MenuItem instance.

Create an AccountMenuItem.kt file, add a property in the file, and assign it a MenuItem instance. The name of the property must follow a specific naming convention. It must have a "MenuItem" suffix and start with a lowercase character.

src/main/kotlin/com/example/ivi/example/plugin/frontend/AccountMenuItem.kt

1val accountMenuItem = MenuItem(
2 AccountFrontend::class.qualifiedName!!,
3 R.drawable.ttivi_account_menuitem,
4 R.string.ttivi_account_menuitem_name
5)

The MenuItem constructor takes a unique ID, a DrawableResolver, and a StringResolver. The latter two resolve the icon and the name of the menu item. In the above example the resolvers are defined as Android resources.

Defining the frontend and menu item build config

Create the frontend and menu item build configurations. These configurations will be used to register the frontend and the menu item to the framework at build time.

Define a frontend implementation and a menu item implementation. These can also be defined in a top-level Gradle file (for example frontends-and-menuitems.gradle.kts) so it can be used in a multi-project build, including the tests.

Create an examples/plugin/app/build.gradle.kts file:

1import com.tomtom.ivi.buildsrc.dependencies.ExampleModuleReference
2import com.tomtom.ivi.platform.gradle.api.common.iviapplication.config.FrontendCreationPolicy
3import com.tomtom.ivi.platform.gradle.api.common.iviapplication.config.FrontendConfig
4
5/**
6 * Defines the implementation and the configuration of the account frontend.
7 */
8val accountFrontend = FrontendConfig(
9 // Needs to match with the name of the builder class.
10 frontendBuilderName = "AccountFrontendBuilder",
11 // The module containing the frontend implementation.
12 implementationModule = ExampleModuleReference("examples_plugin_frontend"),
13 // Create the frontend on demand. It will be created when the menu item is selected.
14 creationPolicy = FrontendCreationPolicy.CREATE_ON_DEMAND
15)
16
17// We can use `FrontendConfig.toMenuItem()` as the menu item is defined in the same module as
18// the frontend implementation. The argument given needs to match with the property that
19// was created earlier in the tutorial.
20val accountMenuItem = accountFrontend.toMenuItem("accountMenuItem")

The above build configurations use the ExampleModuleReference to resolve a module name into the full-qualified package. It is defined once and used for all configurations. See Integrate TomTom Digital Cockpit into a Gradle Project for details.

Registering the frontend and menu item build config

The last step is to register the frontend and the menu item to build configurations in the main application's build script.

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

1import com.tomtom.ivi.buildsrc.dependencies.ExampleModuleReference
2import com.tomtom.ivi.platform.gradle.api.common.iviapplication.config.FrontendConfig
3import com.tomtom.ivi.platform.gradle.api.common.iviapplication.config.FrontendCreationPolicy
4import com.tomtom.ivi.platform.gradle.api.common.iviapplication.config.IviInstanceIdentifier
5import com.tomtom.ivi.platform.gradle.api.common.iviapplication.config.MenuItemConfig
6import com.tomtom.ivi.platform.gradle.api.framework.config.ivi
7
8plugins {
9 // Apply the plugin to use default frontends and services from the TomTom Digital Cockpit
10 // Platform and from all TomTom Digital Cockpit Applications (from appsuite).
11 id("com.tomtom.ivi.product.defaults.core")
12}
13
14// Create `accountFrontend` and `accountMenuItem`
15val accountFrontend = FrontendConfig(
16 // Needs to match with the name of the builder class.
17 frontendBuilderName = "AccountFrontendBuilder",
18 // The module containing the frontend implementation.
19 implementationModule = ExampleModuleReference("examples_plugin_frontend"),
20 // Create the frontend on demand. It will be created when the menu item is selected.
21 creationPolicy = FrontendCreationPolicy.CREATE_ON_DEMAND
22)
23
24// We can use `FrontendConfig.toMenuItem()` as the menu item is defined in the same module as
25// the frontend implementation. The argument given needs to match with the property that
26// was created earlier in the tutorial.
27val accountMenuItem = accountFrontend.toMenuItem("accountMenuItem")
28
29ivi {
30 application {
31 enabled = true
32 iviInstances {
33 create(IviInstanceIdentifier.default) {
34 // Use the default frontends and menu items as defined by the plugin applied above:
35 // `com.tomtom.ivi.product.defaults.core`.
36 applyGroups {
37 includeDefaultPlatformGroups()
38 includeDefaultAppsuiteGroups()
39 }
40 frontends {
41 // Register the `accountFrontend`.
42 add(accountFrontend)
43 }
44 menuItems {
45 // Register the `accountMenuItem` and associate it with the `accountFrontend`.
46 addLast(accountMenuItem to accountFrontend)
47 }
48 }
49 }
50 }
51}
52
53// The rest of the build script, dependencies, etc.

The above example adds the accountFrontend and the accountMenuItem to the default IVI instance. A vehicle may have multiple infotainment screens. Each infotainment screen is an IVI instance. See Configure the Runtime Deployment of the IVI System for more details about IVI instance configurations.

The final step is to let the new frontend replace TomTom Digital Cockpit's user profile frontend. For this we have to use replace instead of add. The same applies for the user profile menu item.

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

1import com.tomtom.ivi.platform.gradle.api.common.iviapplication.config.FrontendConfig
2import com.tomtom.ivi.platform.gradle.api.common.iviapplication.config.IviInstanceIdentifier
3import com.tomtom.ivi.platform.gradle.api.common.iviapplication.config.MenuItemConfig
4import com.tomtom.ivi.platform.gradle.api.plugin.defaultsplatform.userProfileFrontend
5import com.tomtom.ivi.platform.gradle.api.plugin.defaultsplatform.userProfileMenuItem
6import com.tomtom.ivi.platform.gradle.api.framework.config.ivi
7
8plugins {
9 // Apply the Gradle plugin to define the default frontends, menu items and services from
10 // the TomTom Digital Cockpit Platform and from all TomTom Digital Cockpit Applications (from
11 // the appsuite). The default frontends, menu items, and services are defined in groups. The
12 // groups are applied to the following IVI application configuration.
13 id("com.tomtom.ivi.product.defaults.core")
14}
15
16// Create `accountFrontend` and `accountMenuItem`.
17val accountFrontend = FrontendConfig(
18 frontendBuilderName = "AccountFrontendBuilder",
19 implementationModule = ExampleModuleReference("examples_plugin_frontend"),
20 creationPolicy = FrontendCreationPolicy.CREATE_ON_DEMAND
21)
22val accountMenuItem = accountFrontend.toMenuItem("accountMenuItem")
23
24ivi {
25 application {
26 enabled = true
27 iviInstances {
28 create(IviInstanceIdentifier.default) {
29 // Configure all frontends and menu items from all groups that do not require an
30 // explicit opt-in. The groups are defined by the
31 // `com.tomtom.ivi.platform.defaults.core` Gradle plugin.
32 applyGroups {
33 includeDefaultPlatformGroups()
34 includeDefaultAppsuiteGroups()
35 }
36
37 // Replace TomTom Digital Cockpit's user profile frontend with the `accountFrontend`.
38 frontends {
39 replace(userProfileFrontend, accountFrontend)
40 }
41
42 // Replace TomTom Digital Cockpit's user profile menu item with the `accountMenuItem`
43 // and associate it with the `accountFrontend`.
44 menuItems {
45 replace(userProfileMenuItem, accountMenuItem to accountFrontend)
46 }
47 }
48 }
49 }
50}
51
52// The rest of the build script, dependencies, etc.

The above example replaces the userProfileFrontend with the accountFrontend and replaces the userProfileMenuItem with the accountMenuItem.

More information

For information on how to call the @IviServiceApi members from Frontend Plugins refer to the section Calling service methods.