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
- Android Studio terbaru.
- Pengalaman dengan Kotlin dasar.
- Pengetahuan dasar tentang Layanan Android.
- Rasakan pengalaman membuat Perangkat Virtual Android dan menjalankannya di Android Emulator.
- Pengetahuan dasar tentang Modularisasi aplikasi Android.
- Pengetahuan dasar tentang Pola desain builder.
Yang akan Anda buat
Android Auto | Android Automotive OS |
Yang akan Anda pelajari
- Cara kerja arsitektur host klien Library Aplikasi Mobil.
- Cara menulis class
CarAppService
,Session
, danScreen
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
- Kode untuk codelab ini dapat ditemukan di direktori
car-app-library-fundamentals
dalam repositori GitHubcar-codelabs
. Untuk membuat clone kode ini, jalankan perintah berikut:
git clone https://github.com/android/car-codelabs.git
- 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. Direktoricar-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
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.
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
- Setelah memilih modul
:common
di jendela Project, klik kanan dan pilih opsi New > Module. - 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)
Menyiapkan dependensi
- Dalam file
libs.version.toml
, tambahkan entri untuk artefakandroidx.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" }
- 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, danandroidx.car.app:app-testing
untuk komponen pendukung yang berguna untuk pengujian unit. Nantinya Anda juga akan menggunakanapp-projected
danapp-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:
Setelah dependensinya siap, kini saatnya kita menulis CarAppService
.
5. Menulis CarAppService
- Mulailah dengan membuat file bernama
PlacesCarAppService.kt
di paketcarappservice
dalam modul:common:car-app-service
. - Dalam file ini, buat class bernama
PlacesCarAppService
, yang memperluasCarAppService
.
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.
- Terakhir, Anda harus menambahkan elemen
<service>
yang sesuai denganPlacesCarAppService
dalam fileAndroidManifest.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.
- Klik kanan paket
com.example.places.carappservice
, lalu pilih New > Package (nama lengkap paketnya adalahcom.example.places.carappservice.screen
). Di sinilah Anda akan meletakkan semua subclassScreen
untuk aplikasi. - Dalam paket
screen
, buat file bernamaMainScreen.kt
untuk meletakkan classMainScreen
, yang akan memperluasScreen
. Untuk sekarang, pesan yang ditampilkannya menggunakanPaneTemplate
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:
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
- 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
- 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>
- Dalam file
AndroidManifest.xml
modul:app
, tambahkan elemen<meta-data>
berikut, yang merujuk fileautomotive_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.
- Untuk menggunakan
CarConnection
API, Anda harus menambahkan dependensi terlebih dahulu dalam modul:app
di artefakandroidx.car.app:app
.
build.gradle.kts (Modul :app)
dependencies {
...
implementation(libs.androidx.car.app)
...
}
- 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
)
}
- 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())
}
}
}
}
- Jika dijalankan, aplikasinya akan menampilkan pesan Not projecting.
7. Melakukan pengujian menggunakan Desktop Head Unit (DHU)
Setelah mengimplementasikan CarAppService
dan menyiapkan konfigurasi Android Auto, kini saatnya menjalankan aplikasi dan melihat tampilannya.
- 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).
Ups, aplikasinya error.
- Untuk mengetahui penyebab error aplikasi, Anda dapat memeriksa Logcat di Android Studio (Anda mungkin perlu menghapus filter Logcat default untuk
package:mine
dan menggantinya denganis: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.
- Untuk menetapkan level API minimum yang didukung, tambahkan elemen
<meta-data>
berikut dalam fileAndroidManfiest.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>
- Instal aplikasi lagi dan luncurkan di DHU, setelah itu Anda akan melihat tampilan berikut:
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:
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
- 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.
- Klik Next, lalu pilih No Activity di layar berikutnya sebelum mengklik Finish.
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.
- Pertama, tambahkan entri untuk artefak
androidx.car.app:app-automotive
dilibs.versions.toml
.
libs.version.toml
[libraries]
...
androidx-car-app-automotive = { group = "androidx.car.app", name = "app-automotive", version.ref = "carApp"}
- 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:
Menyiapkan manifes
- Pertama, Anda harus mendeklarasikan dua fitur, yakni
android.hardware.type.automotive
danandroid.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>
- 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>
- Terakhir, Anda harus menambahkan elemen
<activity>
untukCarAppActivity
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 classCarAppActivity
dari paketapp-automotive
.android:exported
ditetapkan ketrue
karenaActivity
ini harus bisa diluncurkan oleh aplikasi selain aplikasi itu sendiri (peluncur).android:launchMode
ditetapkan kesingleTask
sehingga hanya boleh ada satu instanceCarAppActivity
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
- 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.
- Di halaman berikutnya, pilih image sistem API 34 (image akan didownload jika belum) dan buat AVD dengan mengklik Finish.
Menjalankan aplikasi
Jalankan aplikasi di emulator yang baru saja Anda buat menggunakan konfigurasi run automotive
.
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
- Untuk memulai, ganti kode di metode
onGetTemplate
classMainScreen
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
.
- Jalankan aplikasi lagi (di salah satu atau kedua platform) untuk mengetahui hasilnya.
Android Auto | Android Automotive OS |
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) ...
- 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 |
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.
- 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:
24
dp x24
dp - Warna:
#000000
- Opasitas:
100%
- Setelah itu, di paket
screen
buat file bernamaDetailScreen.kt
(di samping fileMainScreen.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.
Navigasi antar-layar
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.
- Untuk melakukan navigasi dari salah satu item daftar di
MainScreen
keDetailScreen
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.
- 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
.
- 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
...
}
- 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:
24
dp x24
dp - Warna:
#000000
- Opasitas:
100%
- Kemudian, di
DetailsScreen.kt
, buatAction
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.
- Jangan lupa untuk memanggil
addEndHeaderAction
diPaneTemplate.Builder
agar dapat menggunakanbookmarkAction
.
DetailScreen.kt
Header.Builder()
...
.addEndHeaderAction(bookmarkAction)
.build()
- Sekarang, jalankan aplikasi dan lihat apa yang terjadi:
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
.
- Untuk memperbaiki masalah ini, perbarui
OnClickListener
untukbookmarkAction
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()
}
- Jalankan kembali aplikasi untuk melihat bahwa ikon kini diperbarui saat diklik.
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
- Menggunakan Library Aplikasi Android untuk Mobil membahas konten dalam codelab ini dan banyak konten lainnya.
- Panduan Desain Library Aplikasi Android untuk Mobil memberikan penjelasan mendetail terkait semua template yang berbeda beserta praktik terbaik yang sebaiknya diikuti saat membangun aplikasi Anda.
- Halaman Kualitas aplikasi Android untuk mobil menjelaskan kriteria yang harus dipenuhi aplikasi Anda untuk menciptakan pengalaman pengguna yang optimal dan lulus peninjauan Play Store.