Gunakan plugin library UI Mobil untuk membuat implementasi komponen yang lengkap penyesuaian di library UI Mobil, bukan menggunakan overlay resource runtime (Rro). RRO memungkinkan Anda mengubah hanya resource XML library UI Mobil yang membatasi sejauh mana hal yang dapat Anda sesuaikan.
Membuat plugin
Plugin library UI Mobil adalah APK yang berisi class yang mengimplementasikan serangkaian Plugin API. Plugin API dapat dikompilasi menjadi plugin sebagai library statis.
Lihat contoh di Soong dan Gradle:
Soong
Perhatikan contoh Soong ini:
android_app {
name: "my-plugin",
min_sdk_version: "28",
target_sdk_version: "30",
aaptflags: ["--shared-lib"],
sdk_version: "current",
manifest: "src/main/AndroidManifest.xml",
srcs: ["src/main/java/**/*.java"],
resource_dirs: ["src/main/res"],
static_libs: [
"car-ui-lib-oem-apis",
],
// Disable optimization is mandatory to prevent R.java class from being
// stripped out
optimize: {
enabled: false,
},
certificate: ":my-plugin-certificate",
}
Gradle
Lihat file build.gradle
ini:
apply plugin: 'com.android.application'
android {
compileSdkVersion 30
defaultConfig {
minSdkVersion 28
targetSdkVersion 30
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
signingConfigs {
debug {
storeFile file('chassis_upload_key.jks')
storePassword 'chassis'
keyAlias 'chassis'
keyPassword 'chassis'
}
}
}
dependencies {
implementation project(':oem-apis')
// Or use the following if you'd like to use the maven artifact
// implementation 'com.android.car.ui:car-ui-lib-plugin-apis:1.0.0'
}
Settings.gradle
:
// You can remove the ':oem-apis' if you're using the maven artifact.
include ':oem-apis'
project(':oem-apis').projectDir = new File('./path/to/oem-apis')
include ':my-plugin'
project(':my-plugin').projectDir = new File('./my-plugin')
Plugin harus memiliki penyedia konten yang dideklarasikan dalam manifesnya yang memiliki atribut berikut:
android:authorities="com.android.car.ui.plugin"
android:enabled="true"
android:exported="true"
android:authorities="com.android.car.ui.plugin"
membuat plugin dapat ditemukan
ke library Car UI. Penyedia harus diekspor
agar dapat dikueri
waktu beroperasi. Selain itu, jika atribut enabled
disetel ke false
, nilai default
akan digunakan sebagai pengganti implementasi plugin. Konten
tidak harus ada class penyedia. Dalam hal ini, pastikan
untuk menambahkan
tools:ignore="MissingClass"
ke definisi penyedia. Lihat contohnya
entri manifes di bawah ini:
<application>
<provider
android:name="com.android.car.ui.plugin.PluginNameProvider"
android:authorities="com.android.car.ui.plugin"
android:enabled="false"
android:exported="true"
tools:ignore="MissingClass"/>
</application>
Akhirnya, sebagai langkah keamanan, Tanda tangani aplikasi Anda.
Plugin sebagai koleksi bersama
Tidak seperti pustaka statis Android yang dikompilasi langsung ke dalam aplikasi, Library bersama Android dikompilasi menjadi APK mandiri yang direferensikan oleh aplikasi lain saat runtime.
Plugin yang diterapkan sebagai library bersama Android memiliki class-nya secara otomatis ditambahkan ke classloader bersama antar-aplikasi. Ketika sebuah aplikasi menggunakan library UI Mobil menentukan dependensi runtime di library bersama plugin, classloader dapat mengakses class library bersama plugin. Plugin diterapkan seperti aplikasi Android biasa (bukan library bersama) dapat berdampak negatif pada cold aplikasi waktu mulai aplikasi.
Mengimplementasikan dan membangun library bersama
Mengembangkan dengan library bersama Android sama seperti pengembangan di Android pada umumnya aplikasi, dengan beberapa perbedaan utama.
- Gunakan tag
library
pada tagapplication
dengan paket plugin dalam manifes aplikasi plugin:
<application>
<library android:name="com.chassis.car.ui.plugin" />
...
</application>
- Mengonfigurasi aturan build
android_app
Soong (Android.bp
) dengan AAPT flagshared-lib
, yang digunakan untuk membangun library bersama:
android_app {
...
aaptflags: ["--shared-lib"],
...
}
Dependensi pada library bersama
Untuk setiap aplikasi pada sistem yang menggunakan library UI Mobil, sertakan
uses-library
di manifes aplikasi di bagian
Tag application
dengan nama paket plugin:
<manifest>
<application
android:name=".MyApp"
...>
<uses-library android:name="com.chassis.car.ui.plugin" android:required="false"/>
...
</application>
</manifest>
Menginstal plugin
Plugin HARUS diinstal sebelumnya di partisi sistem dengan menyertakan modulnya
di PRODUCT_PACKAGES
. Paket yang telah diinstal sebelumnya dapat diperbarui mirip dengan
aplikasi terinstal lainnya.
Jika Anda mengupdate plugin yang sudah ada pada sistem, semua aplikasi yang menggunakan plugin tersebut menutup secara otomatis. Setelah dibuka kembali oleh pengguna, mereka memiliki perubahan yang diperbarui. Jika aplikasi tidak berjalan, saat berikutnya dimulai, aplikasi akan memiliki plugin.
Saat menginstal plugin dengan Android Studio, ada beberapa pertimbangan yang harus dipertimbangkan. Pada saat penulisan, ada {i>bug<i} di proses penginstalan aplikasi Android Studio yang menyebabkan update pada plugin tidak diterapkan. Masalah ini dapat diperbaiki dengan memilih opsi Selalu instal dengan pengelola paket (menonaktifkan pengoptimalan deployment di Android 11 dan yang lebih baru) di konfigurasi build plugin.
Selain itu, saat menginstal plugin, Android Studio melaporkan error yang tidak dapat menemukan aktivitas utama untuk diluncurkan. Hal ini wajar karena plugin tidak memiliki aktivitas apa pun (kecuali intent kosong yang digunakan untuk me-resolve intent). Kepada menghilangkan error, ubah opsi Launch ke Nothing dalam build konfigurasi Anda.
Gambar 1. Konfigurasi Plugin Android Studio
Plugin proxy
Penyesuaian aplikasi yang menggunakan library UI Mobil membutuhkan RRO yang menargetkan setiap aplikasi tertentu yang akan dimodifikasi, termasuk ketika penyesuaian identik di seluruh aplikasi. Ini berarti RRO per aplikasi wajib diisi. Melihat aplikasi mana yang menggunakan library UI Mobil.
Plugin proxy library UI Mobil adalah contohnya library bersama plugin yang mendelegasikan implementasi komponennya ke library library Car UI. Plugin ini dapat ditargetkan dengan RRO, yang dapat digunakan sebagai titik tunggal penyesuaian untuk aplikasi yang menggunakan library UI Mobil tanpa perlu menerapkan plugin fungsional. Untuk mengetahui informasi selengkapnya tentang RRO, lihat Mengubah nilai resource aplikasi di runtime.
{i>Plugin<i} {i>proxy<i} hanyalah sebuah contoh dan titik awal untuk melakukan penyesuaian menggunakan plugin. Untuk penyesuaian di luar RRO, kita dapat mengimplementasikan subset plugin komponennya dan gunakan plugin proxy untuk sisanya, atau implementasikan semua komponen secara menyeluruh dari awal.
Meskipun {i>plugin<i} proxy menyediakan satu titik penyesuaian RRO untuk aplikasi, aplikasi yang memilih untuk tidak menggunakan plugin tetap akan memerlukan RRO yang langsung menargetkan aplikasi itu sendiri.
Mengimplementasikan API plugin
Titik masuk utama ke plugin adalah
Class com.android.car.ui.plugin.PluginVersionProviderImpl
. Semua plugin harus
menyertakan class dengan nama yang sama persis dan nama paket ini. Kelas ini harus memiliki
konstruktor default dan mengimplementasikan antarmuka PluginVersionProviderOEMV1
.
Plugin CarUi harus berfungsi dengan aplikasi yang lebih lama atau lebih baru dari plugin. Kepada
memfasilitasi hal ini, semua API plugin diberi versi dengan V#
di bagian akhir
nama class. Jika versi baru library UI Mobil dirilis dengan fitur baru,
bagian tersebut adalah bagian
dari komponen versi V2
. Library UI Mobil melakukan
sebaiknya membuat fitur baru berfungsi
dalam cakupan komponen {i>plugin<i} yang lama.
Misalnya, dengan mengonversi jenis tombol baru di toolbar menjadi MenuItems
.
Namun, aplikasi dengan library UI Mobil versi lama tidak dapat beradaptasi dengan versi baru yang ditulis berdasarkan API yang lebih baru. Untuk mengatasi masalah ini, kami mengizinkan plugin untuk menampilkan berbagai implementasi sendiri berdasarkan versi OEM API didukung oleh aplikasi.
PluginVersionProviderOEMV1
memiliki satu metode di dalamnya:
Object getPluginFactory(int maxVersion, Context context, String packageName);
Metode ini menampilkan objek yang mengimplementasikan versi tertinggi dari
PluginFactoryOEMV#
didukung oleh plugin, tetapi tetap kurang dari atau
sama dengan maxVersion
. Jika plugin tidak memiliki implementasi
PluginFactory
yang lama, mungkin akan menampilkan null
, dalam hal ini metode
digunakan implementasi tertaut dari komponen CarUi.
Untuk mempertahankan kompatibilitas mundur dengan aplikasi yang dikompilasi terhadap
versi lama pustaka UI Mobil statis, disarankan untuk mendukung
maxVersion
dari 2, 5, dan yang lebih tinggi dari dalam implementasi plugin Anda
class PluginVersionProvider
. Versi 1, 3, dan 4 tidak didukung. Sebagai
informasi selengkapnya, lihat
PluginVersionProviderImpl
PluginFactory
adalah antarmuka yang membuat semua CarUi lainnya
komponen. Kode ini juga menentukan versi antarmuka mana yang harus digunakan. Jika
plugin tidak berusaha mengimplementasikan salah satu
komponen ini, plugin mungkin mengembalikan
null
dalam fungsi pembuatannya (kecuali toolbar, yang memiliki
fungsi customizesBaseLayout()
yang terpisah).
pluginFactory
membatasi versi komponen CarUi yang dapat digunakan
secara
bersamaan. Misalnya, tidak akan pernah ada pluginFactory
yang dapat membuat
Toolbar
versi 100 dan juga RecyclerView
versi 1,
tidak banyak jaminan bahwa berbagai
versi komponen akan
bekerja sama. Untuk menggunakan toolbar versi 100, developer diharapkan
menyediakan implementasi versi pluginFactory
yang membuat
versi 100, yang kemudian membatasi opsi pada versi
komponen yang dapat dibuat. Versi komponen lain mungkin tidak
sama, misalnya pluginFactoryOEMV100
dapat membuat
ToolbarControllerOEMV100
dan RecyclerViewOEMV70
.
Toolbar
Layout dasar
Toolbar dan "tata letak dasar" terkait sangat erat, sehingga fungsi
yang membuat toolbar disebut installBaseLayoutAround
. Tujuan
tata letak dasar
adalah konsep yang memungkinkan bilah alat diposisikan di mana saja di sekitar
konten, untuk memungkinkan toolbar di bagian atas/bawah aplikasi, secara vertikal
di sepanjang sisi, atau bahkan {i>toolbar<i}
melingkar yang menutupi seluruh aplikasi. Ini adalah
dicapai dengan meneruskan tampilan ke installBaseLayoutAround
untuk toolbar/dasar
yang dapat dibaca.
Plugin harus mengambil tampilan yang diberikan, melepaskannya dari induknya, meng-inflate
tata letak plugin itu sendiri di indeks yang sama dengan induk dan
LayoutParams
sebagai tampilan yang baru saja dilepas, lalu dilampirkan kembali tampilan
di dalam tata letak yang
baru saja di-inflate. Tata letak yang di-inflate akan
berisi {i>toolbar<i}, jika diminta oleh aplikasi.
Aplikasi dapat meminta tata letak dasar tanpa toolbar. Jika ya,
installBaseLayoutAround
harus menampilkan null. Untuk sebagian besar plugin, hanya itu
diperlukan, tetapi jika pembuat plugin ingin menerapkan, mis. dekorasi
di sekitar tepi aplikasi, yang masih bisa
dilakukan dengan tata letak dasar. Ini
dekorasi sangat berguna untuk perangkat
dengan layar non-persegi panjang, karena
mereka dapat mendorong aplikasi ke dalam ruang persegi panjang dan menambahkan transisi yang bersih ke
ruang non-persegi panjang.
installBaseLayoutAround
juga menerima Consumer<InsetsOEMV1>
. Ini
konsumen dapat digunakan untuk menyampaikan ke aplikasi bahwa plugin sudah
menutupi konten aplikasi (dengan toolbar atau lainnya). Aplikasi ini akan
tahu untuk tetap menggambar di ruang ini, tetapi pertahankan interaksi pengguna yang penting
komponennya. Efek ini digunakan dalam desain referensi kita, agar
{i>toolbar<i} semi-transparan, dan daftar dapat bergulir di bawahnya. Jika fitur ini tadinya
tidak diterapkan, item pertama dalam daftar akan tertahan di bawah toolbar
dan tidak dapat diklik. Jika efek ini tidak diperlukan, plugin bisa mengabaikan
Konsumen.
Gambar 2. Konten yang di-scroll di bawah toolbar
Dari perspektif aplikasi, saat plugin mengirim inset baru, plugin akan menerima
dari aktivitas atau fragmen apa pun yang mengimplementasikan InsetsChangedListener
. Jika
aktivitas atau fragmen tidak mengimplementasikan InsetsChangedListener
, UI Mobil
library akan menangani inset secara default dengan menerapkan inset sebagai padding ke
Activity
atau FragmentActivity
yang berisi fragmen. Library ini tidak
menerapkan inset secara default ke fragmen. Berikut contoh cuplikan
implementasi yang menerapkan inset sebagai padding pada RecyclerView
dalam
aplikasi:
public class MainActivity extends Activity implements InsetsChangedListener {
@Override
public void onCarUiInsetsChanged(Insets insets) {
CarUiRecyclerView rv = requireViewById(R.id.recyclerview);
rv.setPadding(insets.getLeft(), insets.getTop(),
insets.getRight(), insets.getBottom());
}
}
Terakhir, plugin diberi petunjuk fullscreen
, yang digunakan untuk menunjukkan apakah
tampilan yang harus digabungkan akan memenuhi seluruh aplikasi atau hanya sebagian kecil.
Ini dapat digunakan untuk menghindari penerapan
beberapa dekorasi di sepanjang tepi yang
hanya masuk akal jika muncul
di sepanjang tepi seluruh layar. Contoh
yang menggunakan tata letak dasar non-layar penuh adalah {i>Settings<i}, di mana setiap panel
tata letak panel ganda memiliki {i>toolbar<i} sendiri.
Karena installBaseLayoutAround
diharapkan menampilkan null saat
toolbarEnabled
adalah false
, agar plugin dapat menunjukkan bahwa plugin tersebut tidak
ingin menyesuaikan tata letak dasar, tata letak harus mengembalikan false
dari
customizesBaseLayout
.
Tata letak dasar harus berisi FocusParkingView
dan FocusArea
untuk sepenuhnya
mendukung kontrol putar. Tampilan ini dapat dihilangkan di perangkat yang
tidak mendukung rotasi. FocusParkingView/FocusAreas
diimplementasikan dalam
library CarUi statis, sehingga setRotaryFactories
digunakan untuk menyediakan factory untuk
membuat tampilan dari konteks.
Konteks yang digunakan untuk membuat tampilan Fokus harus berupa konteks sumber, bukan konteks
konteks plugin. FocusParkingView
harus paling dekat dengan tampilan pertama
di pohon sebanyak mungkin, karena itulah yang difokuskan ketika
tidak menjadi fokus yang terlihat oleh pengguna. FocusArea
harus menggabungkan toolbar di
tata letak dasar untuk menunjukkan bahwa ini adalah zona dorongan dari tombol putar. Jika FocusArea
bukan
disediakan, pengguna tidak dapat menavigasi ke tombol apa pun di {i>toolbar<i} dengan
tombol putar.
Pengontrol toolbar
ToolbarController
sebenarnya yang ditampilkan seharusnya jauh lebih mudah untuk
implementasikan daripada tata letak dasar. Tugasnya adalah mengambil informasi yang diteruskan ke
penyetel dan menampilkannya dalam tata letak dasar. Lihat Javadoc untuk mengetahui informasi tentang
sebagian besar metode. Beberapa metode yang lebih kompleks dibahas di bawah ini.
getImeSearchInterface
digunakan untuk menampilkan hasil penelusuran dalam IME (keyboard)
jendela. Elemen ini dapat berguna untuk menampilkan/menganimasikan hasil penelusuran bersama dengan
{i>keyboard<i}, misalnya jika {i>keyboard<i}
hanya memenuhi setengah layar. Sebagian besar
fungsionalitasnya diimplementasikan dalam
library CarUi statis, yang
di plugin hanya menyediakan metode bagi library statis untuk mendapatkan
Callback TextView
dan onPrivateIMECommand
. Untuk mendukung hal ini, plugin
harus menggunakan subclass TextView
yang menggantikan onPrivateIMECommand
dan meneruskan
panggilan ke pemroses yang disediakan sebagai TextView
kotak penelusurannya.
setMenuItems
hanya menampilkan MenuItems di layar, tetapi akan dipanggil
secara mengejutkan. Karena API plugin untuk MenuItems tidak
dapat diubah, setiap kali
MenuItem diubah, panggilan setMenuItems
baru akan terjadi. Hal ini dapat
terjadi untuk sesuatu yang sepele seperti
pengguna mengeklik {i>switch<i} MenuItem, dan itu
klik menyebabkan tombol beralih. Karena alasan kinerja dan animasi,
oleh karena itu, disarankan untuk menghitung selisih antara model lama dan baru
MenuItems, dan hanya memperbarui tampilan yang benar-benar berubah. MenuItem
sediakan kolom key
yang dapat membantu hal ini, karena kuncinya harus sama
di berbagai panggilan yang berbeda ke setMenuItems
untuk MenuItem yang sama.
AppStyledView
AppStyledView
adalah penampung untuk tampilan yang tidak disesuaikan sama sekali. Ini
dapat digunakan untuk memberikan batas di sekitar tampilan tersebut yang membuatnya menonjol dari
seluruh aplikasi, dan menunjukkan kepada pengguna
bahwa ini adalah jenis proses yang berbeda
dalam antarmuka berbasis web
yang sederhana. Tampilan yang digabungkan oleh AppStyledView diberikan dalam
setContent
. AppStyledView
juga dapat memiliki tombol kembali atau tutup sebagai
yang diminta oleh aplikasi.
AppStyledView
tidak langsung menyisipkan tampilannya ke dalam hierarki tampilan
seperti yang dilakukan installBaseLayoutAround
, ia hanya mengembalikan tampilannya ke
library statis melalui getView
, yang kemudian melakukan penyisipan. Posisi dan
ukuran AppStyledView
juga dapat dikontrol dengan menerapkan
getDialogWindowLayoutParam
.
Konteks
Plugin harus berhati-hati saat menggunakan Konteks, karena terdapat plugin dan
"sumber" konteks tambahan. Konteks plugin diberikan sebagai argumen untuk
getPluginFactory
, dan merupakan satu-satunya konteks yang memiliki
resource plugin di dalamnya. Ini berarti ini adalah satu-satunya konteks
yang dapat digunakan untuk
meng-inflate tata letak dalam plugin.
Namun, konteks plugin mungkin tidak memiliki konfigurasi yang benar. Kepada
untuk mendapatkan konfigurasi yang benar, kami menyediakan
konteks sumber dalam metode yang membuat
komponen. Konteks sumber biasanya berupa aktivitas, tetapi di beberapa kasus mungkin
juga dapat berupa Layanan atau
komponen Android lainnya. Untuk menggunakan konfigurasi dari
konteks sumber dengan resource dari konteks plugin, konteks baru harus
dibuat menggunakan createConfigurationContext
. Jika konfigurasi yang benar belum
digunakan, akan ada pelanggaran mode ketat Android, dan tampilan yang di-inflate mungkin
tidak memiliki dimensi yang benar.
Context layoutInflationContext = pluginContext.createConfigurationContext(
sourceContext.getResources().getConfiguration());
Perubahan mode
Beberapa plugin dapat mendukung beberapa mode untuk komponennya, seperti mode olahraga atau mode eco yang terlihat berbeda secara visual. Tidak ada dukungan bawaan untuk fungsi tersebut di CarUi, tetapi tidak ada yang menghentikan plugin agar tidak mengimplementasikannya sepenuhnya secara internal. Plugin ini dapat memantau kondisi apa pun yang ingin diketahui kapan harus beralih mode, seperti untuk mendeteksi {i>broadcast.<i} Plugin tidak dapat memicu perubahan konfigurasi untuk mengubah mode, tetapi tidak disarankan untuk mengandalkan perubahan konfigurasi bagaimanapun, karena memperbarui tampilan setiap komponen secara manual akan lebih lancar kepada pengguna dan juga memungkinkan transisi yang tidak mungkin dilakukan dengan perubahan konfigurasi.
Jetpack Compose
Plugin dapat diterapkan menggunakan Jetpack Compose, tetapi ini adalah plugin level alfa dan sebaiknya tidak dianggap stabil.
Plugin dapat menggunakan
ComposeView
untuk membuat platform yang mendukung Compose sebagai tujuan rendering. ComposeView
ini akan menjadi
apa yang ditampilkan ke aplikasi dari metode getView
dalam komponen.
Satu masalah utama dengan penggunaan ComposeView
adalah bahwa fungsi ini menetapkan tag pada tampilan root
pada tata letak untuk menyimpan variabel global yang dibagikan di seluruh
ComposeView yang berbeda dalam hierarki. Karena ID resource plugin tidak
namespaced terpisah dari aplikasi, ini bisa menyebabkan konflik ketika
plugin menetapkan tag
pada tampilan yang sama. Kustom
ComposeViewWithLifecycle
yang memindahkan variabel global ini ke bawah
ComposeView
disediakan di bawah ini. Sekali lagi, hal ini tidak boleh dianggap stabil.
ComposeViewWithLifecycle
:
class ComposeViewWithLifecycle @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr),
LifecycleOwner, ViewModelStoreOwner, SavedStateRegistryOwner {
private val lifeCycle = LifecycleRegistry(this)
private val modelStore = ViewModelStore()
private val savedStateRegistryController = SavedStateRegistryController.create(this)
private var composeView: ComposeView? = null
private var content = @Composable {}
init {
ViewTreeLifecycleOwner.set(this, this)
ViewTreeViewModelStoreOwner.set(this, this)
ViewTreeSavedStateRegistryOwner.set(this, this)
compositionContext = createCompositionContext()
}
fun setContent(content: @Composable () -> Unit) {
this.content = content
composeView?.setContent(content)
}
override fun getLifecycle(): Lifecycle {
return lifeCycle
}
override fun getViewModelStore(): ViewModelStore {
return modelStore
}
override fun getSavedStateRegistry(): SavedStateRegistry {
return savedStateRegistryController.savedStateRegistry
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
savedStateRegistryController.performRestore(Bundle())
lifeCycle.currentState = Lifecycle.State.RESUMED
composeView = ComposeView(context)
composeView?.setContent(content)
addView(composeView, LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT))
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
lifeCycle.currentState = Lifecycle.State.DESTROYED
modelStore.clear()
removeAllViews()
composeView = null
}
// Exact copy of View.createCompositionContext() in androidx's WindowRecomposer.android.kt
private fun createCompositionContext(): CompositionContext {
val currentThreadContext = AndroidUiDispatcher.CurrentThread
val pausableClock = currentThreadContext[MonotonicFrameClock]?.let {
PausableMonotonicFrameClock(it).apply { pause() }
}
val contextWithClock = currentThreadContext + (pausableClock ?: EmptyCoroutineContext)
val recomposer = Recomposer(contextWithClock)
val runRecomposeScope = CoroutineScope(contextWithClock)
val viewTreeLifecycleOwner = checkNotNull(ViewTreeLifecycleOwner.get(this)) {
"ViewTreeLifecycleOwner not found from $this"
}
viewTreeLifecycleOwner.lifecycle.addObserver(
LifecycleEventObserver { _, event ->
@Suppress("NON_EXHAUSTIVE_WHEN")
when (event) {
Lifecycle.Event.ON_CREATE ->
// Undispatched launch since we've configured this scope
// to be on the UI thread
runRecomposeScope.launch(start = CoroutineStart.UNDISPATCHED) {
recomposer.runRecomposeAndApplyChanges()
}
Lifecycle.Event.ON_START -> pausableClock?.resume()
Lifecycle.Event.ON_STOP -> pausableClock?.pause()
Lifecycle.Event.ON_DESTROY -> {
recomposer.cancel()
}
}
}
)
return recomposer
}
// TODO: ComposeViewWithLifecycle should handle saving state and other lifecycle things
// override fun onSaveInstanceState(): Parcelable? {
// val superState = super.onSaveInstanceState()
// val bundle = Bundle()
// savedStateRegistryController.performSave(bundle)
// }
}