Mempelajari dasar-dasar Library Aplikasi Mobil

1. Sebelum memulai

Dalam codelab ini, Anda akan mempelajari cara membuat aplikasi distraksi dioptimalkan untuk Android Auto dan Android Automotive OS menggunakan Library Aplikasi Android untuk Mobil. Pertama-tama, Anda akan menambahkan dukungan untuk Android Auto, lalu dengan sedikit upaya tambahan, Anda akan membuat varian aplikasi yang dapat dijalankan di Android Automotive OS. Setelah aplikasi dapat dijalankan di kedua platform, Anda akan membuat layar tambahan dan interaktivitas dasar.

Yang akan Anda butuhkan

Yang akan Anda buat

Android Auto

Android Automotive OS

Rekaman layar yang menunjukkan aplikasi yang dijalankan di Android Auto menggunakan Desktop Head Unit.

Rekaman layar yang menunjukkan aplikasi yang dijalankan di emulator Android Automotive OS.

Yang akan Anda pelajari

  • Cara kerja arsitektur host klien Library Aplikasi Mobil.
  • Cara menulis class CarAppService, Session, dan Screen Anda sendiri.
  • Cara menggunakan kembali implementasi Anda di Android Auto maupun Android Automotive OS.
  • Cara menjalankan Android Auto di mesin pengembangan menggunakan Desktop Head Unit.
  • Cara menjalankan emulator Android Automotive OS.

2. Memulai persiapan

Mendapatkan kode

  1. Kode untuk codelab ini dapat ditemukan di direktori car-app-library-fundamentals dalam repositori GitHub car-codelabs. Untuk membuat clone kode ini, jalankan perintah berikut:
git clone https://github.com/android/car-codelabs.git
  1. Atau, Anda dapat mendownload repositori sebagai file ZIP:

Membuka project

  • Setelah memulai Android Studio, impor project dengan memilih direktori car-app-library-fundamentals/start saja. Direktori car-app-library-fundamentals/end berisi kode solusi yang dapat Anda rujuk kapan saja jika Anda mengalami kebuntuan atau hanya ingin melihat project lengkap.

Memahami kode

  • Setelah membuka project di Android Studio, luangkan waktu untuk memeriksa kode awal.

Perhatikan bahwa kode awal untuk aplikasi dibagi menjadi dua modul, yakni :app dan :common:data.

Modul :app bergantung pada modul :common:data.

Modul :app berisi UI dan logika aplikasi seluler, sedangkan modul :common:data berisi class data dan repositori model Place yang digunakan untuk membaca model Place. Agar lebih sederhana, repositori membaca data dari daftar yang di-hardcode, tetapi juga dapat secara mudah membaca data dari database atau server backend di aplikasi sebenarnya.

Modul :app memiliki dependensi pada modul :common:data sehingga dapat membaca dan menampilkan daftar model Place.

3. Mempelajari Library Aplikasi Android untuk Mobil

Library Aplikasi Android untuk Mobil adalah serangkaian library Jetpack yang memungkinkan developer membuat aplikasi untuk kendaraan. Library ini memberikan template framework berisi antarmuka pengguna yang dioptimalkan untuk mengemudi sekaligus memungkinkan adaptasi dengan berbagai konfigurasi hardware yang ada di mobil (misalnya, metode input, ukuran layar, dan rasio aspek). Semua ini akan memudahkan Anda sebagai developer dalam membuat aplikasi yang tentunya akan berperforma baik di berbagai kendaraan yang dilengkapi Android Auto dan Android Automotive OS.

Mempelajari cara kerjanya

Aplikasi yang dibangun menggunakan Library Aplikasi Mobil tidak dijalankan langsung di Android Auto atau Android Automotive OS. Namun, aplikasi ini mengandalkan aplikasi host yang berkomunikasi dengan aplikasi klien (aplikasi Anda) dan merender antarmuka pengguna klien untuk aplikasi tersebut. Android Auto sendiri merupakan host dan Google Automotive App Host adalah host yang digunakan di kendaraan Android Automotive OS yang dilengkapi Google.

Berikut class kunci Library Aplikasi Mobil yang harus Anda perluas saat membuat aplikasi:

CarAppService

CarAppService adalah subclass dari class Service Android dan berfungsi sebagai titik entri bagi aplikasi host untuk berkomunikasi dengan aplikasi klien (seperti aplikasi yang akan Anda buat dalam codelab ini). Tujuan utamanya adalah membuat instance Session yang akan berinteraksi dengan aplikasi host.

Session

Session dapat dianggap sebagai instance aplikasi klien yang dijalankan di layar kendaraan. Seperti komponen Android lainnya, class ini memiliki siklus prosesnya sendiri yang dapat digunakan untuk menginisialisasi dan mengakhiri resource yang digunakan selama masa aktif instance Session. CarAppService dan Session memiliki hubungan one-to-many. Misalnya, CarAppService mungkin saja memiliki dua instance Session, satu untuk layar utama, dan satu lagi untuk layar cluster aplikasi navigasi yang mendukung layar cluster.

Screen

Instance Screen berfungsi menghasilkan antarmuka pengguna yang dirender oleh aplikasi host. Antarmuka pengguna ini direpresentasikan oleh class Template sehingga setiap model memiliki jenis tata letak tertentu, seperti petak atau daftar. Setiap Session mengelola stack instance Screen yang menangani alur penggunaan melalui berbagai bagian aplikasi Anda. Sama halnya dengan Session, Screen juga memiliki siklus prosesnya sendiri yang dapat Anda gunakan.

Diagram yang menunjukkan cara kerja Library Aplikasi Mobil. Di sisi kiri, terdapat dua kotak Display. Di bagian tengah, terdapat satu kotak Host. Di sisi kanan, terdapat satu kotak CarAppService. Dalam kotak CarAppService, terdapat dua kotak Session. Dalam Session pertama, terdapat tiga kotak Screen yang bersusun. Dalam Session kedua, terdapat dua kotak Screen yang bersusun. Terdapat tanda panah antara setiap Display dan host serta antara host dan Session. Panah ini menunjukkan cara host mengelola komunikasi antara semua komponen yang berbeda.

Anda akan mempelajari seluk-beluk class ini saat menulis CarAppService, Session, dan Screen di bagian Menulis CarAppService dalam codelab ini.

4. Menyiapkan konfigurasi awal

Untuk memulai, siapkan modul yang berisi CarAppService, lalu deklarasikan dependensinya.

Membuat modul car-app-service

  1. Setelah memilih modul :common di jendela Project, klik kanan dan pilih opsi New > Module.
  2. Dalam wizard modul yang terbuka, pilih template Android Library (agar modul ini dapat digunakan sebagai dependensi oleh modul lainnya) dalam daftar di sebelah kiri, lalu gunakan nilai berikut:
  • Nama modul: :common:car-app-service
  • Nama paket: com.example.places.carappservice
  • SDK minimum: API 23: Android 6.0 (Marshmallow)

Buka wizard New Module dengan menetapkan nilai sesuai penjelasan dalam langkah ini.

Menyiapkan dependensi

  1. Dalam file libs.version.toml, tambahkan entri untuk artefak androidx.car.app:app.

libs.version.toml

[versions]
...
carApp = "1.7.0-rc01"

[libraries]
...
androidx-car-app = { group = "androidx.car.app", name = "app", version.ref = "carApp" }
  1. Selanjutnya, tambahkan dua dependensi ke file build.gradle.kts modul :common:car-app-service.
  • androidx.car.app:app. Komponen ini adalah artefak utama dari Library Aplikasi Mobil dan berisi semua class inti yang digunakan saat membuat aplikasi. Library ini terdiri dari tiga artefak lainnya, androidx.car.app:app-projected untuk fungsi spesifik Android Auto, androidx.car.app:app-automotive untuk kode fungsi Android Automotive OS, dan androidx.car.app:app-testing untuk komponen pendukung yang berguna untuk pengujian unit. Nantinya Anda juga akan menggunakan app-projected dan app-automotive dalam codelab ini.
  • :common:data. Komponen ini adalah modul data yang sama yang digunakan oleh aplikasi seluler yang sudah ada dan memungkinkan penggunaan sumber data yang sama untuk setiap versi pengalaman aplikasi.

build.gradle.kts (Modul :common:car-app-service)

dependencies {
    ...
    implementation(libs.androidx.car.app)
    implementation(project(":common:data"))
    ...
}

Dengan perubahan ini, grafik dependensi untuk modul aplikasi akan terlihat seperti berikut:

Modul :app dan :common:car-app-service sama-sama bergantung pada modul :common:data.

Setelah dependensinya siap, kini saatnya kita menulis CarAppService.

5. Menulis CarAppService

  1. Mulailah dengan membuat file bernama PlacesCarAppService.kt di paket carappservice dalam modul :common:car-app-service.
  2. Dalam file ini, buat class bernama PlacesCarAppService, yang memperluas CarAppService.

PlacesCarAppService.kt

import androidx.car.app.CarAppService
import androidx.car.app.Session
import androidx.car.app.SessionInfo
import androidx.car.app.validation.HostValidator

...

class PlacesCarAppService : CarAppService() {

    override fun createHostValidator(): HostValidator {
        return HostValidator.ALLOW_ALL_HOSTS_VALIDATOR
    }

   override fun onCreateSession(sessionInfo: SessionInfo): Session {
        // PlacesSession will be an unresolved reference until the next step
        return PlacesSession()
    }
}

Class abstrak CarAppService mengimplementasikan metode Service seperti onBind dan onUnbind untuk Anda dan mencegah penggantian lebih lanjut dari metode tersebut guna memastikan interoperabilitas yang sesuai dengan aplikasi host. Anda hanya perlu mengimplementasikan createHostValidator dan onCreateSession.

HostValidator yang Anda tampilkan dari createHostValidator akan dirujuk saat CarAppService Anda diikat guna memastikan bahwa host dapat dipercaya, dan pengikatan tersebut akan gagal jika host tidak sesuai dengan parameter yang Anda tetapkan. Untuk tujuan codelab ini (dan pengujian secara umum), sebaiknya gunakan ALLOW_ALL_HOSTS_VALIDATOR agar dapat dengan mudah memastikan bahwa aplikasi Anda sudah terhubung, tetapi jangan gunakan ini dalam produksi. Lihat dokumentasi terkait createHostValidator guna mengetahui info selengkapnya tentang cara mengonfigurasi komponen ini untuk aplikasi produksi.

Untuk aplikasi yang sederhana seperti ini, onCreateSession dapat dengan mudah menampilkan instance Session. Dalam aplikasi yang lebih kompleks, sebaiknya lakukan inisialisasi terhadap resource jangka panjang seperti metrik dan klien logging yang digunakan selama aplikasi Anda dijalankan di kendaraan.

  1. Terakhir, Anda harus menambahkan elemen <service> yang sesuai dengan PlacesCarAppService dalam file AndroidManifest.xml modul :common:car-app-service untuk memberi tahu keberadaannya pada sistem operasi (dan lebih luas lagi, pada aplikasi lainnya seperti host).

AndroidManifest.xml (:common:car-app-service)

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <!--
        This AndroidManifest.xml will contain all of the elements that should be shared across the
        Android Auto and Automotive OS versions of the app, such as the CarAppService <service> element
    -->

    <application>
        <service
            android:name="com.example.places.carappservice.PlacesCarAppService"
            android:exported="true">
            <intent-filter>
                <action android:name="androidx.car.app.CarAppService" />
                <category android:name="androidx.car.app.category.POI" />
            </intent-filter>
        </service>
    </application>
</manifest>

Ada dua hal penting yang harus dicatat di sini:

  • Elemen <action> memungkinkan aplikasi host (dan peluncur) menemukan aplikasi.
  • Elemen <category> mendeklarasikan kategori aplikasi (dalam hal ini lokasi menarik), yang akan menentukan kriteria kualitas aplikasi yang harus dipenuhi (hal ini akan dibahas lebih lanjut nanti). Nilai lainnya yang mungkin digunakan dijelaskan dalam Kategori aplikasi yang didukung.

Membuat class PlacesSession

  • Dalam PlacesCarAppService.kt, tambahkan kode berikut:

PlacesCarAppService.kt

import android.content.Intent
import androidx.car.app.Screen

...

class PlacesSession : Session() {
    override fun onCreateScreen(intent: Intent): Screen {
        // MainScreen will be an unresolved reference until the next step
        return MainScreen(carContext)
    }
}

Untuk aplikasi sederhana seperti ini, Anda hanya perlu menampilkan layar utama di onCreateScreen. Namun, karena metode ini memerlukan Intent sebagai parameter, aplikasi dengan fitur yang lebih lengkap mungkin juga akan membaca datanya dan mengisi data sebelumnya dari layar atau menggunakan logika kondisional lainnya.

Membuat class MainScreen

Selanjutnya, buat paket baru bernama screen.

  1. Klik kanan paket com.example.places.carappservice, lalu pilih New > Package (nama lengkap paketnya adalah com.example.places.carappservice.screen). Di sinilah Anda akan meletakkan semua subclass Screen untuk aplikasi.
  2. Dalam paket screen, buat file bernama MainScreen.kt untuk meletakkan class MainScreen, yang akan memperluas Screen. Untuk sekarang, pesan yang ditampilkannya menggunakan PaneTemplate mungkin akan bersifat sederhana seperti Hello, world!.

MainScreen.kt

import androidx.car.app.CarContext
import androidx.car.app.Screen
import androidx.car.app.model.Action
import androidx.car.app.model.Header
import androidx.car.app.model.Pane
import androidx.car.app.model.PaneTemplate
import androidx.car.app.model.Row
import androidx.car.app.model.Template

...

class MainScreen(carContext: CarContext) : Screen(carContext) {

    override fun onGetTemplate(): Template {
        val row = Row.Builder()
            .setTitle("Hello, world!")
            .build()

        val pane = Pane.Builder()
            .addRow(row)
            .build()

        return PaneTemplate.Builder(pane)
            .setHeader(
                Header.Builder()
                    .setStartHeaderAction(Action.APP_ICON)
                    .build()
            ).build()
    }
}

6. Menambahkan dukungan untuk Android Auto

Meski kini Anda telah mengimplementasikan semua logika yang diperlukan untuk mengaktifkan dan menjalankan aplikasi, ada dua konfigurasi lainnya yang harus disiapkan sebelum Anda dapat menjalankannya di Android Auto.

Menambahkan dependensi pada modul car-app-service

Dalam file build.gradle.kts modul :app, tambahkan komponen berikut:

build.gradle.kts (Modul :app)

dependencies {
    ...
    implementation(project(":common:car-app-service"))
    ...
}

Dengan perubahan ini, grafik dependensi untuk modul aplikasi akan terlihat seperti berikut:

Modul :app dan :common:car-app-service sama-sama bergantung pada modul :common:data. Modul :app juga bergantung pada modul :common:car-app-service.

Dengan ini, kode yang baru saja Anda tulis dalam modul :common:car-app-service akan dipaketkan bersama dengan komponen lainnya yang disertakan dalam Library Aplikasi Mobil, seperti aktivitas pemberian izin yang disediakan.

Mendeklarasikan meta-data com.google.android.gms.car.application

  1. Klik kanan modul :common:car-app-service, pilih opsi New > Android Resource File, lalu masukkan nilai berikut:
  • Nama file: automotive_app_desc.xml
  • Jenis resource: XML
  • Elemen root: automotiveApp

Wizard New Resource File dengan nilai sesuai penjelasan dalam langkah ini.

  1. Dalam file tersebut, tambahkan elemen <uses> berikut untuk mendeklarasikan bahwa aplikasi Anda menggunakan template yang disediakan oleh Library Aplikasi Mobil.

automotive_app_desc.xml

<?xml version="1.0" encoding="utf-8"?>
<automotiveApp>
    <uses name="template"/>
</automotiveApp>
  1. Dalam file AndroidManifest.xml modul :app, tambahkan elemen <meta-data> berikut, yang merujuk file automotive_app_desc.xml yang baru saja Anda buat.

AndroidManifest.xml (:app)

<application ...>

    <meta-data
        android:name="com.google.android.gms.car.application"
        android:resource="@xml/automotive_app_desc" />

    ...

</application>

File ini dibaca oleh Android Auto dan memberi tahu kemampuan apa yang didukung aplikasi Anda–dalam kasus ini, file memberi tahu Android Auto bahwa aplikasi menggunakan sistem template Library Aplikasi Mobil. Informasi ini kemudian digunakan untuk menangani perilaku seperti menambahkan aplikasi ke peluncur Android Auto dan membukanya dari notifikasi.

Opsional: Memproses perubahan proyeksi

Terkadang, Anda perlu tahu apakah perangkat pengguna sudah terhubung ke mobil atau tidak. Anda dapat mengetahuinya menggunakan CarConnection API, yang memberikan LiveData sehingga Anda dapat memantau status koneksi.

  1. Untuk menggunakan CarConnection API, Anda harus menambahkan dependensi terlebih dahulu dalam modul :app di artefak androidx.car.app:app.

build.gradle.kts (Modul :app)

dependencies {
    ...
    implementation(libs.androidx.car.app)
    ...
}
  1. Untuk tujuan demonstrasi, Anda dapat membuat Composable sederhana seperti berikut, yang menampilkan status koneksi saat ini. Dalam aplikasi sebenarnya, status ini mungkin dicatat dalam logging tertentu, yang digunakan untuk menonaktifkan fungsi tertentu di layar ponsel saat diproyeksikan, atau digunakan untuk tujuan lainnya.

MainActivity.kt

import androidx.car.app.connection.CarConnection

...

@Composable
fun ProjectionState(carConnectionType: Int, modifier: Modifier = Modifier) {
    val text = when (carConnectionType) {
        CarConnection.CONNECTION_TYPE_NOT_CONNECTED -> "Not projecting"
        CarConnection.CONNECTION_TYPE_NATIVE -> "Running on Android Automotive OS"
        CarConnection.CONNECTION_TYPE_PROJECTION -> "Projecting"
        else -> "Unknown connection type"
    }

    Text(
        text = text,
        style = MaterialTheme.typography.bodyMedium,
        modifier = modifier
    )
}
  1. Setelah menemukan cara untuk menampilkan data, kini saatnya membaca dan meneruskannya menjadi Composable, sebagaimana ditunjukkan dalam cuplikan berikut.

MainActivity.kt

import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState

...

setContent {
    val carConnectionType by CarConnection(this).type.observeAsState(initial = -1)
    PlacesTheme {
        // A surface container using the 'background' color from the theme
        Surface(
            modifier = Modifier.fillMaxSize(),
            color = MaterialTheme.colorScheme.background
        ) {
            Column {
                Text(
                    text = "Places",
                    style = MaterialTheme.typography.displayLarge,
                    modifier = Modifier.padding(8.dp)
                )
                ProjectionState(
                    carConnectionType = carConnectionType,
                    modifier = Modifier.padding(8.dp)
                )
                PlaceList(places = PlacesRepository().getPlaces())
            }
        }
    }
}
  1. Jika dijalankan, aplikasinya akan menampilkan pesan Not projecting.

Kini terdapat baris teks tambahan di layar untuk status proyeksi yang bertuliskan &#39;Not projecting&#39;

7. Melakukan pengujian menggunakan Desktop Head Unit (DHU)

Setelah mengimplementasikan CarAppService dan menyiapkan konfigurasi Android Auto, kini saatnya menjalankan aplikasi dan melihat tampilannya.

  1. Instal aplikasi di ponsel Anda, lalu ikuti petunjuk tentang cara menginstal dan menjalankan DHU.

Setelah DHU aktif dan dijalankan, Anda akan melihat ikon aplikasi di peluncur (jika tidak, periksa kembali apakah Anda sudah mengikuti semua langkah di bagian sebelumnya, lalu keluar dan mulai ulang DHU dari terminal).

  1. Membuka aplikasi dari peluncur

Peluncur Android Auto yang menampilkan petak aplikasi, termasuk aplikasi Places.

Ups, aplikasinya error.

Terdapat layar error dengan pesan bertuliskan &#39;Android Auto has encountered an unexpected error&#39;. Terdapat tombol debug di pojok kanan atas layar.

  1. Untuk mengetahui penyebab error aplikasi, Anda dapat memeriksa Logcat di Android Studio (Anda mungkin perlu menghapus filter Logcat default untuk package:mine dan menggantinya dengan is:error).
Error: [type: null, cause: null, debug msg: java.lang.IllegalArgumentException: Min API level not declared in manifest (androidx.car.app.minCarApiLevel)
        at androidx.car.app.AppInfo.retrieveMinCarAppApiLevel(AppInfo.java:143)
        at androidx.car.app.AppInfo.create(AppInfo.java:91)
        at androidx.car.app.CarAppService.getAppInfo(CarAppService.java:380)
        at androidx.car.app.CarAppBinder.getAppInfo(CarAppBinder.java:255)
        at androidx.car.app.ICarApp$Stub.onTransact(ICarApp.java:182)
        at android.os.Binder.execTransactInternal(Binder.java:1285)
        at android.os.Binder.execTransact(Binder.java:1244)
]

Dari log, Anda dapat melihat bahwa ada deklarasi yang belum disertakan dalam manifes untuk level API minimum yang didukung aplikasi. Sebelum menambahkan entri tersebut, sebaiknya pahami terlebih dahulu mengapa kita perlu melakukannya.

Seperti Android itu sendiri, Library Aplikasi Mobil juga memiliki konsep level API karena harus ada perjanjian antara aplikasi host dan klien agar keduanya dapat berkomunikasi. Aplikasi host mendukung level API tertentu beserta fiturnya yang terkait (dan untuk kompatibilitas mundur, hal ini juga berlaku untuk level yang lebih rendah). Misalnya, SignInTemplate dapat digunakan pada host yang menjalankan level API 2 atau lebih tinggi. Namun, jika Anda mencoba menggunakannya pada host yang hanya mendukung level API 1, host tersebut tidak akan mengetahui jenis template dan tidak akan bisa melakukan tindakan penting dengan hal tersebut.

Selama proses binding host ke klien, harus ada semacam titik temu dalam hal level API yang didukung agar binding berhasil. Misalnya, jika host hanya mendukung level API 1, tetapi aplikasi klien tidak dapat dijalankan tanpa fitur dari level API 2 (sebagaimana disebutkan dalam deklarasi manifes ini), aplikasi tidak akan terhubung karena klien tidak akan berhasil dijalankan di host. Oleh karena itu, persyaratan level API minimum harus dideklarasikan oleh klien dalam manifesnya guna memastikan bahwa hanya host yang dapat mendukungnya yang akan terikat dengannya.

  1. Untuk menetapkan level API minimum yang didukung, tambahkan elemen <meta-data> berikut dalam file AndroidManfiest.xml modul :common:car-app-service:

AndroidManifest.xml (:common:car-app-service)

<application>
    <meta-data
        android:name="androidx.car.app.minCarApiLevel"
        android:value="1" />
    <service android:name="com.example.places.carappservice.PlacesCarAppService" ...>
        ...
    </service>
</application>
  1. Instal aplikasi lagi dan luncurkan di DHU, setelah itu Anda akan melihat tampilan berikut:

Aplikasi menampilkan layar dasar bertuliskan &#39;Hello, world&#39;

Agar lebih lengkap, Anda juga dapat mencoba menetapkan minCarApiLevel ke nilai yang besar (misalnya 100) untuk mengetahui apa yang terjadi saat Anda mencoba memulai aplikasi jika host dan klien tidak kompatibel (petunjuk: aplikasi akan mengalami error, sama seperti jika tidak ada nilai yang ditetapkan).

Penting juga untuk diketahui bahwa, sama seperti Android itu sendiri, Anda dapat menggunakan berbagai fitur dari API yang lebih tinggi dari level minimum yang dideklarasikan jika Anda memverifikasi di runtime bahwa host mendukung level yang diwajibkan.

Opsional: Memproses perubahan proyeksi

  • Jika Anda menambahkan pemroses CarConnection pada langkah sebelumnya, Anda akan melihat perubahan status di ponsel Anda saat DHU dijalankan, seperti yang terlihat di bawah:

Baris teks yang menampilkan status proyeksi kini bertuliskan &#39;Projecting&#39; karena ponsel sudah terhubung ke DHU.

8. Menambahkan dukungan untuk Android Automotive OS

Setelah Android Auto aktif dan dijalankan, kini saatnya melakukan upaya ekstra untuk mendukung Android Automotive OS juga.

Membuat modul :automotive

  1. Guna membuat modul yang berisi kode khusus untuk versi Android Automotive OS aplikasi, buka File > New > New Module... di Android Studio, pilih opsi Automotive dari daftar jenis template di sebelah kiri, lalu gunakan nilai berikut:
  • Nama Aplikasi/Library: Places (sama seperti aplikasi utama, tetapi Anda juga dapat memilih nama lain sesuai keinginan)
  • Nama modul: automotive
  • Nama paket: com.example.places.automotive
  • Bahasa: Kotlin
  • SDK minimum: API 29: Android 10.0 (Q)– semua kendaraan Android Automotive OS yang mendukung aplikasi Library Aplikasi Mobil memiliki level API minimum 29.

Wizard Create New Module untuk modul Android Automotive OS yang menampilkan nilai yang tercantum dalam langkah ini.

  1. Klik Next, lalu pilih No Activity di layar berikutnya sebelum mengklik Finish.

Halaman kedua wizard Create New Module. Ada tiga opsi yang ditampilkan, &#39;No Activity&#39;, &#39;Media Service&#39;, dan &#39;Messaging Service&#39;. Opsi yang dipilih adalah &#39;No Activity&#39;.

Menambahkan dependensi

Sama seperti Android Auto, Anda harus mendeklarasikan dependensi di modul :common:car-app-service. Dengan begitu, Anda dapat menggunakan implementasi Anda di kedua platform.

Selain itu, Anda perlu menambahkan dependensi di artefak androidx.car.app:app-automotive. Berbeda dengan artefak androidx.car.app:app-projected yang sifatnya opsional bagi Android Auto, dependensi ini diwajibkan di Android Automotive OS karena berisi CarAppActivity yang digunakan untuk menjalankan aplikasi Anda.

  1. Pertama, tambahkan entri untuk artefak androidx.car.app:app-automotive di libs.versions.toml.

libs.version.toml

[libraries]
...
androidx-car-app-automotive = { group = "androidx.car.app", name = "app-automotive", version.ref = "carApp"}
  1. Untuk menambahkan dependensi, buka file build.gradle.kts, lalu sisipkan kode berikut:

build.gradle.kts (Modul :automotive)

dependencies {
    ...
    implementation(project(":common:car-app-service"))
    implementation(libs.androidx.car.app.automotive)
    ...
}

Dengan perubahan ini, grafik dependensi untuk modul aplikasi akan terlihat seperti berikut:

Modul :app dan :common:car-app-service sama-sama bergantung pada modul :common:data. Modul :app dan :automotive bergantung pada modul :common:car-app-service.

Menyiapkan manifes

  1. Pertama, Anda harus mendeklarasikan dua fitur, yakni android.hardware.type.automotive dan android.software.car.templates_host, sebagaimana diwajibkan.

android.hardware.type.automotive merupakan fitur sistem yang mengindikasikan bahwa perangkat itu sendiri adalah kendaraan (lihat FEATURE_AUTOMOTIVE untuk mengetahui detail selengkapnya). Hanya aplikasi yang mewajibkan fitur ini yang dapat dikirim ke jalur Automotive OS di Konsol Play (dan aplikasi yang dikirim ke jalur lain tidak dapat mewajibkan fitur ini). android.software.car.templates_host adalah fitur sistem yang hanya tersedia di kendaraan yang memiliki host template yang diwajibkan untuk menjalankan aplikasi template. Untuk codelab ini, perubahan ini sudah memadai. Saat membangun aplikasi Anda sendiri, pastikan untuk memeriksa apakah aplikasi Anda memenuhi semua persyaratan fitur Google Play untuk Android Automotive OS.

AndroidManifest.xml (:automotive)

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-feature
        android:name="android.hardware.type.automotive"
        android:required="true" />
    <uses-feature
        android:name="android.software.car.templates_host"
        android:required="true" />
    ...
</manifest>
  1. Selanjutnya, Anda harus menambahkan referensi ke file automotive_app_desc.xml seperti yang Anda lakukan untuk Android Auto.

Perlu diketahui bahwa kali ini atribut android:name tidak sama seperti sebelumnya–jika sebelumnya nilainya com.google.android.gms.car.application, sekarang nilainya adalah com.android.automotive. Sama seperti sebelumnya, ini akan merujuk file automotive_app_desc.xml dalam modul :common:car-app-service, sehingga resource yang sama akan digunakan di Android Auto dan Android Automotive OS. Selain itu, elemen <meta-data> berada di dalam elemen <application> (jadi Anda harus mengubah tag application agar tidak tertutup).

AndroidManifest.xml (:automotive)

<application>
    ...
    <meta-data android:name="com.android.automotive"
        android:resource="@xml/automotive_app_desc"/>
    ...
</application>
  1. Terakhir, Anda harus menambahkan elemen <activity> untuk CarAppActivity yang disertakan dalam library.

AndroidManifest.xml (:automotive)

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    ...
    <application ...>
        ...
        <activity
            android:name="androidx.car.app.activity.CarAppActivity"
            android:exported="true"
            android:launchMode="singleTask"
            android:theme="@android:style/Theme.DeviceDefault.NoActionBar">

            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>

            <meta-data
                android:name="distractionOptimized"
                android:value="true" />
        </activity>
    </application>
</manifest>

Semua itu akan memberikan hasil sebagai berikut:

  • android:name membuat daftar nama class yang sepenuhnya memenuhi syarat pada class CarAppActivity dari paket app-automotive.
  • android:exported ditetapkan ke true karena Activity ini harus bisa diluncurkan oleh aplikasi selain aplikasi itu sendiri (peluncur).
  • android:launchMode ditetapkan ke singleTask sehingga hanya boleh ada satu instance CarAppActivity pada satu waktu.
  • android:theme ditetapkan ke @android:style/Theme.DeviceDefault.NoActionBar sehingga aplikasi menggunakan ruang layar penuh yang tersedia untuknya.
  • Filter intent menunjukkan bahwa ini adalah Activity peluncur untuk aplikasi.
  • Terdapat elemen <meta-data> yang menunjukkan ke sistem bahwa aplikasi dapat digunakan saat pembatasan UX diterapkan, seperti saat kendaraan sedang bergerak.

Opsional: menyalin ikon peluncur dari modul :app

Karena Anda baru saja membuat modul :automotive, ikon logo Android hijau akan menjadi ikon default.

  • Jika mau, Anda dapat menyalin dan menempel direktori resource mipmap dari modul :app ke modul :automotive agar menggunakan ikon peluncur yang sama seperti aplikasi seluler.

9. Melakukan pengujian menggunakan emulator Android Automotive OS

Membuat Perangkat Virtual Android untuk Android Automotive OS

  1. Setelah membuka Pengelola Perangkat, pilih Automotive di kolom Category di sisi kiri jendela. Kemudian, pilih definisi perangkat Automotive (1408p landscape) with Google Play dari daftar, lalu klik Next.

Wizard Virtual Device Configuration yang menampilkan opsi profil hardware &#39;Automotive (1024p landscape)&#39; yang dipilih.

  1. Di halaman berikutnya, pilih image sistem API 34 (image akan didownload jika belum) dan buat AVD dengan mengklik Finish.

c9631de84fc6e835.png

Menjalankan aplikasi

Jalankan aplikasi di emulator yang baru saja Anda buat menggunakan konfigurasi run automotive.

Konfigurasi

Aplikasi menampilkan layar dasar bertuliskan &#39;Hello, world&#39;

Pada langkah berikutnya, Anda akan membuat perubahan pada modul :common:car-app-service untuk menampilkan daftar tempat dan memungkinkan pengguna memulai navigasi ke lokasi yang dipilih di aplikasi lain.

10. Menambahkan peta dan layar detail

Menambahkan peta ke layar utama

  1. Untuk memulai, ganti kode di metode onGetTemplate class MainScreen dengan kode berikut:

MainScreen.kt

import androidx.car.app.model.CarLocation
import androidx.car.app.model.Distance
import androidx.car.app.model.DistanceSpan
import androidx.car.app.model.ItemList
import androidx.car.app.model.Metadata
import androidx.car.app.model.Place
import androidx.car.app.model.PlaceListMapTemplate
import androidx.car.app.model.PlaceMarker
import com.example.places.data.PlacesRepository

...

override fun onGetTemplate(): Template {
    val placesRepository = PlacesRepository()
    val itemListBuilder = ItemList.Builder()
        .setNoItemsMessage("No places to show")

    placesRepository.getPlaces()
        .forEach {
            itemListBuilder.addItem(
                Row.Builder()
                    .setTitle(it.name)
                    // Each item in the list *must* have a DistanceSpan applied to either the title
                    // or one of the its lines of text (to help drivers make decisions)
                    .addText(SpannableString(" ").apply {
                        setSpan(
                            DistanceSpan.create(
                                Distance.create(Math.random() * 100, Distance.UNIT_KILOMETERS)
                            ), 0, 1, Spannable.SPAN_INCLUSIVE_INCLUSIVE
                        )
                    })
                    .setOnClickListener { TODO() }
                    // Setting Metadata is optional, but is required to automatically show the
                    // item's location on the provided map
                    .setMetadata(
                        Metadata.Builder()
                            .setPlace(Place.Builder(CarLocation.create(it.latitude, it.longitude))
                                // Using the default PlaceMarker indicates that the host should
                                // decide how to style the pins it shows on the map/in the list
                                .setMarker(PlaceMarker.Builder().build())
                                .build())
                            .build()
                    ).build()
            )
        }

    return PlaceListMapTemplate.Builder()
        .setTitle("Places")
        .setItemList(itemListBuilder.build())
        .build()
}

Kode ini membaca daftar instance Place dari PlacesRepository, lalu mengonversi masing-masing instance menjadi Row untuk ditambahkan ke ItemList yang ditampilkan oleh PlaceListMapTemplate.

  1. Jalankan aplikasi lagi (di salah satu atau kedua platform) untuk mengetahui hasilnya.

Android Auto

Android Automotive OS

Aplikasi mengalami error dan pengguna diarahkan kembali ke peluncur setelah membukanya.

Ups, terjadi error lagi–sepertinya ada izin yang belum disertakan.

java.lang.SecurityException: The car app does not have a required permission: androidx.car.app.MAP_TEMPLATES
        at android.os.Parcel.createExceptionOrNull(Parcel.java:2373)
        at android.os.Parcel.createException(Parcel.java:2357)
        at android.os.Parcel.readException(Parcel.java:2340)
        at android.os.Parcel.readException(Parcel.java:2282)
        ...
  1. Untuk memperbaiki error, tambahkan elemen <uses-permission> berikut dalam manifes modul :common:car-app-service.

Izin ini harus dideklarasikan oleh setiap aplikasi yang menggunakan PlaceListMapTemplate. Jika tidak, aplikasi akan mengalami error seperti yang ditunjukkan. Perlu diketahui bahwa hanya aplikasi yang mendeklarasikan kategorinya sebagai androidx.car.app.category.POI yang dapat menggunakan template ini, dan kemudian dapat menggunakan izin ini.

AndroidManifest.xml (:common:car-app-service)

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-permission android:name="androidx.car.app.MAP_TEMPLATES" />
    ...
</manifest>

Jika Anda menjalankan aplikasi setelah menambahkan izin ini, tampilannya akan terlihat seperti berikut di setiap platform:

Android Auto

Android Automotive OS

Daftar lokasi ditampilkan di sisi kiri layar dan peta dengan pin yang sesuai dengan lokasi tersebut ditampilkan di baliknya, mengisi sisa ruang di layar.

Daftar lokasi ditampilkan di sisi kiri layar dan peta dengan pin yang sesuai dengan lokasi tersebut ditampilkan di baliknya, mengisi sisa ruang di layar.

Anda tidak perlu mengkhawatirkan proses rendering peta karena hal ini ditangani oleh host aplikasi saat Anda memberikan Metadata yang diperlukan.

Menambahkan layar detail

Selanjutnya, kita akan menambahkan layar detail agar pengguna dapat melihat informasi lebih lanjut terkait suatu lokasi tertentu, lalu pengguna dapat melakukan navigasi ke lokasi tersebut menggunakan aplikasi navigasi pilihan mereka atau kembali ke daftar tempat lainnya. Hal ini dapat dilakukan menggunakan PaneTemplate, yang memungkinkan Anda menampilkan hingga empat baris informasi di samping tombol tindakan opsional.

  1. Pertama, klik kanan direktori res di modul :common:car-app-service, klik New > Vector Asset, lalu buat ikon navigasi menggunakan konfigurasi berikut:
  • Jenis aset: Clip art
  • Gambar klip: navigation
  • Nama: baseline_navigation_24
  • Ukuran: 24dp x 24dp
  • Warna: #000000
  • Opasitas: 100%

Wizard Asset Studio dengan tampilan input yang disebutkan dalam langkah ini

  1. Setelah itu, di paket screen buat file bernama DetailScreen.kt (di samping file MainScreen.kt yang sudah ada), lalu tambahkan kode berikut:

DetailScreen.kt

import android.graphics.Color
import androidx.car.app.CarContext
import androidx.car.app.Screen
import androidx.car.app.model.Action
import androidx.car.app.model.CarColor
import androidx.car.app.model.CarIcon
import androidx.car.app.model.Header
import androidx.car.app.model.MessageTemplate
import androidx.car.app.model.Pane
import androidx.car.app.model.PaneTemplate
import androidx.car.app.model.Row
import androidx.car.app.model.Template
import androidx.core.graphics.drawable.IconCompat
import com.example.android.cars.carappservice.R
import com.example.places.data.PlacesRepository
import com.example.places.data.model.toIntent

class DetailScreen(carContext: CarContext, private val placeId: Int) : Screen(carContext) {
    private var isBookmarked = false

    override fun onGetTemplate(): Template {
        val place = PlacesRepository().getPlace(placeId)
            ?: return MessageTemplate.Builder("Place not found")
                .setHeader(
                    Header.Builder()
                        .setStartHeaderAction(Action.BACK)
                        .build()
                )
                .build()

        val navigateAction = Action.Builder()
            .setTitle("Navigate")
            .setIcon(
                CarIcon.Builder(
                    IconCompat.createWithResource(
                        carContext,
                        R.drawable.baseline_navigation_24
                    )
                ).build()
            )
            // Only certain Intent actions are supported by `startCarApp`. Check its documentation
            // for all of the details. To open another app that can handle navigating to a location
            // you must use the CarContext.ACTION_NAVIGATE action and not Intent.ACTION_VIEW like
            // you might on a phone.
            .setOnClickListener { carContext.startCarApp(place.toIntent(CarContext.ACTION_NAVIGATE)) }
            .build()

        return PaneTemplate.Builder(
            Pane.Builder()
                .addAction(navigateAction)
                .addRow(
                    Row.Builder()
                        .setTitle("Coordinates")
                        .addText("${place.latitude}, ${place.longitude}")
                        .build()
                ).addRow(
                    Row.Builder()
                        .setTitle("Description")
                        .addText(place.description)
                        .build()
                ).build()
        ).setHeader(
            Header.Builder()
                .setStartHeaderAction(Action.BACK)
                .setTitle(place.name)
                .build()
        ).build()
    }
}

Perhatikan baik-baik cara navigateAction dibuat—panggilan ke startCarApp dalam OnClickListener miliknya adalah kunci untuk berinteraksi dengan aplikasi lain di Android Auto dan Android Automotive OS.

Dengan dua jenis layar yang sudah siap, kini saatnya menambahkan navigasi antar-keduanya. Navigasi dalam Library Aplikasi Mobil menggunakan model stack push dan pop-up yang sangat cocok untuk alur tugas sederhana yang dapat diselesaikan saat mengemudi.

Diagram yang menggambarkan cara kerja navigasi dalam aplikasi dengan Library Aplikasi Mobil. Di sisi kiri, terdapat stack yang hanya berisi MainScreen. Di antara stack ini dan stack tengah, terdapat tanda panah berlabel &#39;Push DetailScreen&#39;. Stack tengah memiliki DetailScreen di atas MainScreen yang ada. Di antara stack tengah dan stack kanan, terdapat tanda panah berlabel &#39;Pop&#39;. Stack kanan tidak berbeda dengan stack kiri yang hanya berisi MainScreen.

  1. Untuk melakukan navigasi dari salah satu item daftar di MainScreen ke DetailScreen untuk item tersebut, tambahkan kode berikut:

MainScreen.kt

Row.Builder()
    ...
    .setOnClickListener { screenManager.push(DetailScreen(carContext, it.id)) }
    ...

Anda tidak perlu mengkhawatirkan navigasi kembali dari DetailScreen ke MainScreen. Hal ini sudah ditangani karena setHeaderAction(Action.BACK) dipanggil saat membuat PaneTemplate yang ditampilkan di DetailScreen. Saat tindakan header diklik oleh pengguna, host akan menangani penutupan layar saat ini dari stack untuk Anda, tetapi perilaku ini dapat diganti oleh aplikasi Anda jika diinginkan.

  1. Sekarang, jalankan aplikasi untuk melihat cara kerja DetailScreen dan navigasi dalam aplikasi.

11. Memperbarui konten di layar

Anda sering kali ingin agar pengguna dapat berinteraksi dengan layar dan mengubah status elemen layar tersebut. Agar tahu cara melakukan ini, Anda akan membuat fungsi agar pengguna dapat menambahkan atau menghapus bookmark untuk suatu tempat dari DetailScreen.

  1. Pertama, tambahkan variabel lokal, isBookmarked, yang berisi status. Di aplikasi yang sebenarnya, ini akan disimpan sebagai bagian dari lapisan data, tetapi variabel lokal sudah cukup untuk tujuan demonstrasi.

DetailScreen.kt

class DetailScreen(carContext: CarContext, private val placeId: Int) : Screen(carContext) {
    private var isBookmarked = false
    ...
}
  1. Selanjutnya, klik kanan direktori res di modul :common:car-app-service, klik New > Vector Asset, lalu buat dua ikon bookmark menggunakan konfigurasi berikut:
  • Jenis aset: Clip art
  • Nama: outline_bookmark_add_24, outline_bookmark_added_24
  • Gambar klip: bookmark, bookmark_added (dari set sumber Simbol Material dengan Garis Batas)
  • Ukuran: 24dp x 24dp
  • Warna: #000000
  • Opasitas: 100%
  1. Kemudian, di DetailsScreen.kt, buat Action untuk fungsi bookmark.

DetailScreen.kt

val navigateAction = ...

val bookmarkAction = Action.Builder()
    .setIcon(
        CarIcon.Builder(
            IconCompat.createWithResource(
                carContext,
                if (isBookmarked) R.drawable.outline_bookmark_added_24 else R.drawable.outline_bookmark_add_24
            )
        ).build()
    )
    .setOnClickListener {
        isBookmarked = !isBookmarked
    }.build()

...

Ada dua poin menarik di sini:

  • CarIcon akan diberi tint bergantung pada status item.
  • setOnClickListener digunakan untuk memberikan reaksi terhadap input dari pengguna dan mengaktifkan/menonaktifkan status bookmark.
  1. Jangan lupa untuk memanggil addEndHeaderAction di PaneTemplate.Builder agar dapat menggunakan bookmarkAction.

DetailScreen.kt

Header.Builder()
    ...
    .addEndHeaderAction(bookmarkAction)
    .build()
  1. Sekarang, jalankan aplikasi dan lihat apa yang terjadi:

DetailScreen akan ditampilkan. Pengguna mengetuk ikon favorit, tetapi warnanya tidak berubah seperti yang diharapkan.

Klik diterima, tetapi ikon tidak berubah.

Hal ini karena Library Aplikasi Mobil memiliki konsep refresh. Untuk membatasi gangguan bagi pengemudi, refresh konten di layar memiliki batasan tertentu (yang mungkin bervariasi menurut template yang ditampilkan), dan setiap refresh harus diminta secara eksplisit oleh kode Anda sendiri dengan memanggil metode invalidate class Screen. UI-nya tidak akan berubah jika Anda hanya memperbarui status tertentu yang dirujuk di onGetTemplate.

  1. Untuk memperbaiki masalah ini, perbarui OnClickListener untuk bookmarkAction sebagai berikut:

DetailScreen.kt

.setOnClickListener {
    isBookmarked = !isBookmarked
    // Request that `onGetTemplate` be called again so that updates to the
    // screen's state can be picked up
    invalidate()
}
  1. Jalankan kembali aplikasi untuk melihat bahwa ikon kini diperbarui saat diklik.

96da7958632706bb.gif

Begitulah langkah-langkahnya, kini Anda memiliki aplikasi yang terintegrasi secara optimal dengan Android Auto dan Android Automotive OS.

12. Selamat

Anda berhasil membuat aplikasi Library Aplikasi Mobil. Kini saatnya menerapkan apa yang telah Anda pelajari pada aplikasi Anda sendiri.

Seperti yang dijelaskan sebelumnya, saat ini hanya kategori tertentu yang dibuat menggunakan aplikasi Library Aplikasi Mobil yang dapat dikirim ke Play Store. Jika aplikasi Anda berada dalam salah satu kategori yang didukung, Anda dapat mulai membangun aplikasi sekarang.

Berbagai kategori aplikasi baru akan ditambahkan setiap tahunnya, sehingga meski Anda tidak dapat langsung menerapkan apa yang telah Anda pelajari, pantau terus informasinya, bisa jadi akan tiba saat yang tepat untuk memperluas aplikasi Anda ke perangkat mobil.

Untuk dicoba

  • Instal emulator OEM dan lihat bagaimana penyesuaian OEM dapat mengubah tampilan dan nuansa aplikasi Library Aplikasi Mobil di Android Automotive OS. Perlu diketahui bahwa tidak semua emulator OEM mendukung aplikasi Library Aplikasi Mobil.
  • Gunakan konfigurasi DHU dan profil hardware emulator Android Automotive OS yang berbeda untuk melihat cara aplikasi host menangani adaptasi aplikasi untuk berbagai ukuran layar.
  • Lihat Etalase aplikasi contoh yang menunjukkan fungsi lengkap Library Aplikasi Mobil.

Bacaan lebih lanjut

Dokumen referensi