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.

Tidak termasuk dalam cakupan

  • Panduan tentang cara membuat aplikasi media (audio) untuk Android Auto dan Android Automotive OS. Lihat Membuat aplikasi media untuk mobil guna mengetahui detail tentang cara membuat aplikasi tersebut.
  • Panduan tentang cara membuat aplikasi pesan untuk Android Auto. Lihat Membuat aplikasi pesan untuk Android Auto guna mengetahui detail tentang cara membuat aplikasi tersebut.

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 dibuat 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 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 menulis CarAppService, Session, dan Screen di bagian Menulis CarAppService dalam codelab ini, jadi tidak perlu khawatir jika merasa belum benar-benar memahaminya.

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 build.gradle level project, tambahkan deklarasi variabel untuk Library Aplikasi Mobil sebagai berikut. Dengan begitu, Anda dapat secara mudah menggunakan versi yang sama di tiap-tiap modul dalam aplikasi.

build.gradle (Project: Places)

buildscript {
    ext {
        // All versions can be found at https://developer.android.com/jetpack/androidx/releases/car-app
        car_app_library_version = '1.3.0-rc01'
        ...
    }
}
  1. Selanjutnya, tambahkan dua dependensi ke file build.gradle 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 (Module :common:car-app-service)

dependencies {
    ...
    implementation "androidx.car.app:app:$car_app_library_version"
    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

class PlacesCarAppService : CarAppService() {

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

    override fun onCreateSession(): 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, yang akan menentukan kriteria kualitas aplikasi yang harus dipenuhi (hal ini akan dibahas lebih lanjut nanti). Nilai lainnya yang mungkin digunakan adalah androidx.car.app.category.NAVIGATION dan androidx.car.app.category.IOT.

Membuat class PlacesSession

  • Buat file PlacesCarAppService.kt, lalu tambahkan kode berikut:

PlacesCarAppService.kt

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

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)
            .setHeaderAction(Action.APP_ICON)
            .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 modul :app, tambahkan komponen berikut:

build.gradle (Modul :app)

dependencies {
    ...
    implementation project(path: ':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 (Modul :app)

dependencies {
    ...
    implementation "androidx.car.app:app:$car_app_library_version"
    ...
}
  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

@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

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 mengaktifkan ikon debug di pojok kanan atas (hanya terlihat jika menjalankan aplikasi di DHU) atau memeriksa Logcat di Android Studio.

Layar error yang sama seperti gambar sebelumnya, tetapi kini dengan tombol debug yang diaktifkan. Stack trace ditampilkan di layar.

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)—seperti yang dijelaskan sebelumnya saat membuat modul :common:car-app-service, 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. Untuk menambahkan dependensi, buka file build.gradle, lalu sisipkan kode berikut:

build.gradle (Modul :automotive)

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

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.

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 mendeklarasikan bahwa beberapa fitur tidak diwajibkan.

Hal ini bertujuan memastikan bahwa aplikasi Anda kompatibel dengan rentang hardware yang tersedia di mobil yang dilengkapi Google. Misalnya, jika aplikasi Anda mewajibkan fitur android.hardware.screen.portrait, aplikasi tersebut tidak akan kompatibel dengan kendaraaan yang memiliki layar lanskap karena orientasi layar di sebagian besar kendaraan bersifat tetap. Oleh karena itu, atribut android:required ditetapkan ke false untuk fitur ini.

AndroidManifest.xml (:automotive)

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    ...
    <uses-feature
        android:name="android.hardware.wifi"
        android:required="false" />
    <uses-feature
        android:name="android.hardware.screen.portrait"
        android:required="false" />
    <uses-feature
        android:name="android.hardware.screen.landscape"
        android:required="false" />
    <uses-feature
        android:name="android.hardware.camera"
        android:required="false" />
    ...
</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. Perlu diketahui bahwa 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

Menginstal Image Sistem Automotive dengan Play Store

  1. Pertama, buka SDK Manager di Android Studio, lalu pilih tab SDK Platforms jika belum dipilih. Di pojok kanan bawah jendela SDK Manager, pastikan kotak Show package details dicentang.
  2. Instal satu atau beberapa image emulator berikut. Image hanya dapat berjalan di komputer dengan arsitektur yang sama (x86/ARM) dengan image itu sendiri.
  • Image Sistem Atom_64 Intel x86 Android 12L > Automotive dengan Play Store
  • Image Sistem ARM 64 v8a Android 12L > Automotive dengan Play Store
  • Image Sistem Atom_64 Intel x86 Android 11 > Automotive dengan Play Store
  • Image Sistem Atom_64 Intel x86 Android 10 > Automotive dengan Play Store

Membuat Perangkat Virtual Android untuk Android Automotive OS

  1. Setelah membuka Pengelola Perangkat, pilih Automotive di kolom Category di sisi kiri jendela. Setelah itu, pilih definisi perangkat Automotive (1024p landscape) dari daftar, lalu klik Next.

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

  1. Pada halaman berikutnya, pilih image sistem dari langkah sebelumnya (jika Anda memilih image Android 11/API 30, opsi ini mungkin ada di tab x86 Images, bukan tab Recommended default). Klik Next, lalu pilih opsi lanjutan yang Anda inginkan sebelum akhirnya membuat AVD dengan mengklik Finish.

Menjalankan aplikasi

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

Konfigurasi

Saat pertama kali menjalankan aplikasi, Anda mungkin akan melihat layar seperti berikut:

Aplikasi menampilkan layar bertuliskan &#39;System update required&#39; dengan tombol yang bertuliskan &#39;Check for updates&#39; di bawahnya.

Jika ini terjadi, klik tombol Check for updates, yang akan mengarahkan Anda ke halaman Play Store untuk aplikasi Google Automotive App Host, lalu klik tombol Install. Jika Anda tidak login saat mengklik tombol Check for updates, Anda akan diarahkan ke alur login. Setelah login, Anda dapat membuka aplikasi lagi untuk mengklik tombol dan kembali ke halaman Play Store.

Halaman Play Store Google Automotive App Host - terdapat tombol &#39;Install&#39; di bagian pojok kanan atas.

  1. Setelah host diinstal, buka aplikasi dari peluncur (ikon petak sembilan titik di baris bawah) lagi, dan Anda akan melihat tampilan berikut:

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

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

Stack trace lain ditampilkan karena terjadi error

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

class DetailScreen(carContext: CarContext, private val placeId: Int) : Screen(carContext) {

    override fun onGetTemplate(): Template {
        val place = PlacesRepository().getPlace(placeId)
            ?: return MessageTemplate.Builder("Place not found")
                .setHeaderAction(Action.BACK)
                .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()
        )
            .setTitle(place.name)
            .setHeaderAction(Action.BACK)
            .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 beralih antara opsi favoritkan dan batal favoritkan tempat dari DetailScreen.

  1. Pertama, tambahkan variabel lokal, isFavorite, 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 isFavorite = false
    ...
}
  1. Selanjutnya, 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
  • Nama: baseline_favorite_24
  • Gambar klip: favorite
  • Ukuran: 24dp x 24dp
  • Warna: #000000
  • Opasitas: 100%

Wizard Asset Studio dengan tampilan input yang disebutkan dalam langkah ini

  1. Kemudian, di DetailsScreen.kt, buat ActionStrip untuk PaneTemplate.

Komponen UI ActionStrip ditempatkan di baris header di sebelah judul dan sangat cocok untuk tindakan sekunder maupun tersier. Karena navigasi adalah tindakan utama yang akan dilakukan di DetailScreen, menempatkan Action untuk tindakan favoritkan dan batalkan favorit di ActionStrip adalah cara yang bagus untuk menyusun struktur layar.

DetailScreen.kt

val navigateAction = ...

val actionStrip = ActionStrip.Builder()
    .addAction(
        Action.Builder()
            .setIcon(
                CarIcon.Builder(
                    IconCompat.createWithResource(
                        carContext,
                        R.drawable.baseline_favorite_24
                    )
                ).setTint(
                    if (isFavorite) CarColor.RED else CarColor.createCustom(
                        Color.LTGRAY,
                        Color.DKGRAY
                    )
                ).build()
            )
            .setOnClickListener {
                isFavorite = !isFavorite
            }.build()
    )
    .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 favorit.
  1. Jangan lupa untuk memanggil setActionStrip di PaneTemplate.Builder jika ingin menggunakannya.

DetailScreen.kt

return PaneTemplate.Builder(...)
    ...
    .setActionStrip(actionStrip)
    .build()
  1. Sekarang, jalankan aplikasi dan lihat apa yang terjadi:

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

Ini menarik... kliknya sudah terjadi tetapi UI-nya 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 seperti berikut:

DetailScreen.kt

.setOnClickListener {
    isFavorite = !isFavorite
    // Request that `onGetTemplate` be called again so that updates to the
    // screen's state can be picked up
    invalidate()
}
  1. Jalankan aplikasi lagi, dan kali ini akan terlihat bahwa warna ikon hati akan berubah di setiap kliknya.

DetailScreen akan ditampilkan. Pengguna mengetuk ikon favorit dan kini warnanya berubah seperti yang diharapkan.

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 adalah aplikasi navigasi, aplikasi lokasi menarik (POI) (seperti yang Anda kerjakan dalam codelab ini), atau aplikasi internet of things (IOT), Anda dapat mulai membuat aplikasi sekarang juga dan merilisnya hingga fase produksi di kedua platform.

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 (misalnya emulator Polestar 2) 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.
  • Lihat Etalase aplikasi contoh yang menunjukkan fungsi lengkap Library Aplikasi Mobil.

Bacaan lebih lanjut

Dokumen referensi