Używaj wtyczek do biblioteki interfejsu Car UI, by tworzyć pełne implementacje komponentu dostosowania w bibliotece interfejsu samochodu, zamiast korzystać z nakładek zasobów środowiska wykonawczego. (RRO). RRO umożliwiają zmianę tylko zasobów XML biblioteki interfejsu Car UI komponentów, co ogranicza zakres możliwości dostosowania.
Utwórz wtyczkę
Wtyczka biblioteki interfejsu Car UI to plik APK zawierający klasy implementujące zestaw Interfejsy API wtyczek. Interfejsy API wtyczek można skompilować w jako biblioteki statycznej.
Zobacz przykłady w narzędziach Soong i Gradle:
Soong
Przyjrzyjmy się przykładowi Song:
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
Zobacz ten plik build.gradle
:
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')
Wtyczka musi mieć w pliku manifestu zadeklarowanego dostawcy treści z następujące atrybuty:
android:authorities="com.android.car.ui.plugin"
android:enabled="true"
android:exported="true"
android:authorities="com.android.car.ui.plugin"
sprawia, że wtyczka jest wykrywalna
do biblioteki UI samochodu. Dostawca musi zostać wyeksportowany, aby można było wysłać do niego zapytania na stronie
w środowisku wykonawczym. Ponadto, jeśli atrybut enabled
ma wartość false
, zostanie użyta wartość domyślna.
zamiast implementacji wtyczki. Treść
klasa dostawcy nie musi istnieć. W takim przypadku dodaj
tools:ignore="MissingClass"
do definicji dostawcy. Zobacz przykład
wpis w pliku manifestu poniżej:
<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>
Wreszcie, ze względów bezpieczeństwa, Podpisywanie aplikacji.
Wtyczki jako biblioteka udostępniona
W przeciwieństwie do bibliotek statycznych Androida, które są kompilowane bezpośrednio w aplikacje, Biblioteki udostępnione Androida są scalane w samodzielny plik APK, do którego odwołuje się aplikacja przez inne aplikacje w czasie działania.
Wtyczki wdrożone jako biblioteka udostępniona na Androidzie mają swoje klasy. są automatycznie dodawane do udostępnianego modułu klasy między aplikacjami. Gdy aplikacja, która korzysta z biblioteki Car UI określa zależność środowiska wykonawczego od biblioteki współdzielonej wtyczki, program classloader może uzyskać dostęp do klas z biblioteki udostępnionej wtyczki. Wdrożone wtyczki Zwykłe aplikacje na Androida (nie zasoby wspólne) mogą negatywnie wpływać na wybór aplikacji. czasy rozpoczęcia.
Wdrażanie i tworzenie bibliotek udostępnionych
Programowanie przy użyciu bibliotek udostępnionych Androida przebiega prawie tak samo jak w przypadku zwykłego Androida. aplikacji mobilnych.
- Użyj tagu
library
pod tagiemapplication
z pakietem wtyczki nazwa w pliku manifestu aplikacji wtyczki:
<application>
<library android:name="com.chassis.car.ui.plugin" />
...
</application>
- Skonfiguruj regułę kompilacji Soong
android_app
(Android.bp
) z użyciem AAPT flagashared-lib
, która służy do tworzenia zasobów wspólnych:
android_app {
...
aaptflags: ["--shared-lib"],
...
}
Zależności od bibliotek udostępnionych
W przypadku każdej aplikacji w systemie, która korzysta z biblioteki interfejsu Car UI, dodaj parametr
uses-library
w pliku manifestu aplikacji w sekcji
tag application
z nazwą pakietu wtyczki:
<manifest>
<application
android:name=".MyApp"
...>
<uses-library android:name="com.chassis.car.ui.plugin" android:required="false"/>
...
</application>
</manifest>
Instalowanie wtyczki
Wtyczki MUSZĄ być wstępnie zainstalowane na partycji systemowej poprzez dołączenie modułu
za PRODUCT_PACKAGES
. Wstępnie zainstalowany pakiet można zaktualizować w podobny sposób
z innych zainstalowanych aplikacji.
Jeśli aktualizujesz w systemie wtyczkę, wszystkie aplikacje używające tej wtyczki zamknij automatycznie. Po ponownym otwarciu aplikacji przez użytkownika wprowadzone zostaną zaktualizowane zmiany. Jeśli aplikacja nie była uruchomiona, po następnym uruchomieniu zostanie zaktualizowana wtyczki.
Podczas instalowania wtyczki w Android Studio które trzeba wziąć pod uwagę. W momencie pisania wystąpił błąd w procesu instalacji aplikacji Android Studio, który powoduje aktualizację wtyczki nie będzie obowiązywać. Można to naprawić, wybierając opcję Zawsze instaluj za pomocą menedżera pakietów (wyłącza optymalizacje wdrażania w Androidzie 11 i nowszych) w konfiguracji kompilacji wtyczki.
Dodatkowo podczas instalowania wtyczki Android Studio zgłasza błąd, nie może znaleźć głównego działania do uruchomienia. To normalne, bo wtyczka nie nie mają żadnych działań (z wyjątkiem pustej intencji używanej do rozwiązania intencji). Do Wyeliminuj błąd, zmień opcję Launch (Uruchom) na Nothing (Brak) w kompilacji konfiguracji.
Rysunek 1. Konfiguracja wtyczki Android Studio
Wtyczka serwera proxy
Dostosowywanie aplikacje korzystające z biblioteki interfejsu Car wymaga RRO kierowanego na każdą konkretną aplikację, która ma zostać zmodyfikowana; Dotyczy to też sytuacji, w których dostosowania są identyczne w poszczególnych aplikacjach. Oznacza to, że RRO w każdym przypadku Aplikacja jest wymagana. Zobacz, które aplikacje korzystają z biblioteki interfejsu Car UI.
Przykładem jest wtyczka proxy biblioteki Car UI biblioteka współużytkowana wtyczki, która przekazuje swoje implementacje komponentu statycznemu wersji biblioteki interfejsu użytkownika. Na tę wtyczkę można kierować RRO, czyli Używany jako pojedynczy punkt dostosowania w aplikacjach korzystających z biblioteki Car UI bez konieczności wdrażania funkcjonalnej wtyczki. Więcej informacji na temat: Instrukcje dla RRO znajdziesz w sekcji Zmienianie wartości zasobów aplikacji na stronie w czasie działania.
Wtyczka serwera proxy to tylko przykład i punkt wyjścia do dostosowywania ani wtyczki. Aby dostosować go do własnych potrzeb, można zaimplementować podzbiór wtyczki a w pozostałych przypadkach użyj wtyczki serwera proxy lub wdróż całą wtyczkę całkowicie od zera.
Wtyczka serwera proxy zapewnia jeden punkt dostosowania RRO dla aplikacji, aplikacje, które zrezygnowały z korzystania z wtyczki, nadal będą wymagały RRO, które jest kierowana na samą aplikację.
Wdróż interfejsy API wtyczki
Głównym punktem wejścia wtyczki jest
com.android.car.ui.plugin.PluginVersionProviderImpl
zajęcia. Wszystkie wtyczki muszą
dodaj klasę o tej samej nazwie i nazwie pakietu. Te zajęcia muszą mieć
domyślnego konstruktora i wdrażać interfejs PluginVersionProviderOEMV1
.
Wtyczki CarUi muszą działać w aplikacjach, które są starsze lub nowsze od wtyczki. Do
aby to ułatwić, wszystkie interfejsy API wtyczek mają wersję V#
na końcu
classname (nazwa klasy). Jeśli zostanie udostępniona nowa wersja biblioteki interfejsu Car UI z nowymi funkcjami,
wchodzą w skład komponentu w wersji V2
. Biblioteka UI samochodu
żeby nowe funkcje działały w zakresie starszego komponentu wtyczki.
Możesz na przykład przekonwertować przycisk nowego typu na pasku narzędzi na MenuItems
.
Jednak aplikacji ze starszą wersją biblioteki UI samochodu nie można dostosować do nowej wtyczki napisanej w nowszych interfejsach API. Aby rozwiązać ten problem, zezwalamy wtyczkom na zwracają różne implementacje w zależności od wersji interfejsu API OEM obsługiwanych przez aplikacje.
Pole PluginVersionProviderOEMV1
zawiera 1 metodę:
Object getPluginFactory(int maxVersion, Context context, String packageName);
Ta metoda zwraca obiekt, w którym zaimplementowano najwyższą wersję
PluginFactoryOEMV#
obsługiwane przez wtyczkę, ale nadal będzie mniejsze niż lub
równe maxVersion
. Jeśli wtyczka nie ma implementacji
PluginFactory
, może zwrócić null
. W takim przypadku statycznie-
stosowane są połączone implementacje komponentów CarUi.
Aby zachować zgodność wsteczną z aplikacjami skompilowanymi na
starszych wersji statycznej biblioteki Car Ui, zaleca się obsługę
maxVersion
z 2, 5 lub wyższego poziomu z implementacji wtyczki
klasy PluginVersionProvider
. Wersje 1, 3 i 4 nie są obsługiwane. Dla:
więcej informacji znajdziesz w
PluginVersionProviderImpl
PluginFactory
to interfejs, który tworzy wszystkie pozostałe elementy CarUi.
Określa też, której wersji ich interfejsów należy użyć. Jeśli
wtyczka nie dąży do implementacji żadnego z tych komponentów, może zwrócić
null
w funkcji tworzenia (z wyjątkiem paska narzędzi, w którym jest
osobną funkcję customizesBaseLayout()
).
pluginFactory
określa, które wersje komponentów CarUi mogą być używane
razem. Na przykład nigdy nie będzie elementu pluginFactory
, który mógłby utworzyć
wersji 100 systemu Toolbar
oraz wersji 1 systemu RecyclerView
,
nie daje żadnej gwarancji, że szeroki wybór wersji komponentów
nad ich współpracą. Aby można było używać paska narzędzi w wersji 100, programiści powinni
udostępnia implementację wersji pluginFactory
, która tworzy
paska narzędzi w wersji 100, która ogranicza opcje w wersjach innych
które można utworzyć. Wersje innych komponentów mogą nie
równy, np. pluginFactoryOEMV100
może utworzyć
ToolbarControllerOEMV100
i RecyclerViewOEMV70
.
Pasek narzędzi
Układ podstawowy
Pasek narzędzi i „układ podstawowy” są ściśle powiązane, dlatego funkcja
który tworzy pasek narzędzi, nazywa się installBaseLayoutAround
.
układ podstawowy
umożliwia umieszczenie paska narzędzi w dowolnym miejscu
treści, aby umieścić pasek narzędzi u góry/u dołu aplikacji, w pionie
wzdłuż boków, a nawet okrągły pasek narzędzi otaczający całą aplikację. To jest
można uzyskać, przekazując widok do interfejsu installBaseLayoutAround
dla paska narzędzi/bazy.
układ, który można zawinąć.
Wtyczka powinna przyjąć udostępniony widok, odłączyć go od elementu nadrzędnego,
z własnym układem wtyczki w tym samym indeksie elementu nadrzędnego i tym samym
LayoutParams
jako widok, który właśnie został odłączony, a następnie ponownie dołącz widok
gdzieś w układzie, który został przed chwilą powiększony. Powiększony układ
zawierać pasek narzędzi, jeśli poprosi o to aplikacja.
Aplikacja może żądać układu podstawowego bez paska narzędzi. Jeśli tak,
Funkcja installBaseLayoutAround
powinna zwrócić wartość null. To wszystko.
ale nie jest to konieczne, ale jeśli autor wtyczki zechce go zastosować np. dekoracja
wokół krawędzi aplikacji, co nadal można zrobić przy użyciu układu podstawowego. Te
są szczególnie przydatne w przypadku urządzeń
z nieprostokątnymi ekranami,
umieścić aplikację w prostokątnej przestrzeni i umieścić wyraźne przejścia
do przestrzeni nieprostokątnej.
installBaseLayoutAround
przekroczył(a) też ocenę Consumer<InsetsOEMV1>
. Ten
można użyć do poinformowania aplikacji, że wtyczka jest częściowo
zasłaniając zawartość aplikacji (za pomocą paska narzędzi lub w inny sposób). Aplikacja będzie
aby móc dalej rysować w tym obszarze, ale zachować wszystkie kluczowe elementy interaktywne,
z każdego z nich. Ten efekt jest używany w naszym projekcie referencyjnym, aby zapewnić
półprzezroczystego paska narzędzi, a pod nim listy przewijają się. Jeśli ta funkcja była
nie zaimplementowano, pierwszy element na liście utknie pod paskiem narzędzi
i nieklikalny. Jeśli ten efekt nie jest potrzebny, wtyczka może zignorować parametr
Klientowi.
Rysunek 2. Przewijanie treści pod paskiem narzędzi
Z punktu widzenia aplikacji, gdy wtyczka wyśle nowe wstawki, otrzyma
ich z wszelkich działań lub fragmentów, które implementują InsetsChangedListener
. Jeśli
działanie lub fragment nie implementuje funkcji InsetsChangedListener
, interfejs użytkownika Car Ui
będzie domyślnie obsługiwać wstawki, stosując wstawki jako dopełnienie
Activity
lub FragmentActivity
zawierający fragment. Biblioteka nie
domyślnie stosuje wstawki do fragmentów. Oto przykładowy fragment
i stosuje wstawki jako dopełnienie w elemencie RecyclerView
w tabeli
aplikacja:
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());
}
}
Na koniec wtyczka otrzymuje wskazówkę fullscreen
, która wskazuje, czy
widok, który należy uwzględnić, obejmuje całą aplikację lub tylko jej małą sekcję.
Pozwala to uniknąć stosowania dekoracji na krawędzi,
mają sens tylko wtedy, gdy wyświetlają się wzdłuż krawędzi ekranu. Przykład
aplikacji, która korzysta z niepełnoekranowych układów podstawowych, to Ustawienia, w której każdy panel
układ z dwoma panelami ma własny pasek narzędzi.
Ponieważ funkcja installBaseLayoutAround
powinna zwrócić wartość null,
Parametr toolbarEnabled
ma wartość false
, co oznacza, że wtyczka wskazuje, że nie
chcesz dostosować układ podstawowy, musi on zwracać wartość false
z
customizesBaseLayout
Układ podstawowy musi zawierać elementy FocusParkingView
i FocusArea
, aby w pełni
obsługują pokrętła. Te widoki można pominąć na urządzeniach, które
ale nie obsługują pokrętła. FocusParkingView/FocusAreas
są zaimplementowane w
statycznej biblioteki CarUi, więc setRotaryFactories
umożliwia fabrykom
tworzyć widoki na podstawie kontekstów.
Konteksty użyte do utworzenia widoków skupienia muszą być kontekstem źródła, a nie
o kontekście wtyczki. Wartość FocusParkingView
powinna być najbliższa pierwszemu widokowi
które można kontrolować, skupiając się na aspektach,
nie może być widoczna dla użytkownika. FocusArea
musi otaczać pasek narzędzi w
układ podstawowy, aby wskazać, że jest to strefa obrotowa. Jeśli FocusArea
nie jest
użytkownik nie może przejść do żadnych przycisków na pasku narzędzi z
z kontrolerem obrotowym.
Kontroler paska narzędzi
Rzeczywista wartość ToolbarController
powinna być znacznie łatwiejsza do
niż w układzie podstawowym. Jego zadaniem jest przekazywanie informacji
i wyświetlaj je w układzie podstawowym. Więcej informacji na ten temat można znaleźć w dokumencie Javadoc .
większości metod. Poniżej omówiliśmy niektóre z bardziej złożonych metod.
getImeSearchInterface
służy do wyświetlania wyników wyszukiwania w IME (klawiaturze)
okno. Jest to przydatne do wyświetlania/animowania wyników wyszukiwania obok
klawiatury – np. zajmowała tylko połowę ekranu. Większość
jest zaimplementowana w bibliotece statycznej CarUi,
udostępnia tylko metody dla biblioteki statycznej umożliwiające
TextView
i onPrivateIMECommand
wywołań zwrotnych. Żeby to umożliwić, wtyczka
powinien używać podklasy TextView
, która zastępuje onPrivateIMECommand
i przekazuje
wywołanie podanego detektora jako TextView
na pasku wyszukiwania.
setMenuItems
po prostu wyświetla na ekranie element MenuItems, ale ma on nazwę
zaskakująco często. Interfejs API wtyczki dla MenuItems jest stały,
Element MenuItem został zmieniony, nastąpi zupełnie nowe wywołanie funkcji setMenuItems
. Może to spowodować
może się zdarzyć w przypadku czegoś tak błahego, jak to, że użytkownik kliknął przełącznik MenuItem,
spowodowało przełączenie przełącznika. Zarówno w przypadku wydajności, jak i animacji,
dlatego zachęcamy do obliczania różnicy między starym a nowym kontem
MenuItems oraz aktualizować tylko te widoki, które rzeczywiście się zmieniły. Element menu
podaj pole key
, które może w tym pomóc, ponieważ klucz powinien być taki sam
w różnych wywołaniach setMenuItems
dla tego samego elementu MenuItem.
Widok stylu aplikacji
AppStyledView
to kontener dla widoku, który w ogóle nie jest dostosowany. it
można ustawić za pomocą obramowania
pozostałej części aplikacji i poinformować użytkownika, że jest to inny rodzaj
za pomocą prostego interfejsu online. Widok opakowany przez AppStyledView jest
setContent
AppStyledView
może też mieć przycisk Wstecz lub Zamknij,
żądania aplikacji.
Element AppStyledView
nie wstawia od razu swoich widoków do hierarchii widoków
jak ma to miejsce w przypadku installBaseLayoutAround
, zwraca go tylko
statyczną za pomocą funkcji getView
, która następnie przeprowadza wstawianie. Pozycja
rozmiaru pliku AppStyledView
można również kontrolować, implementując
getDialogWindowLayoutParam
Konteksty
Podczas korzystania z kontekstów wtyczka musi być ostrożna, ponieważ zawiera ona zarówno wtyczkę,
„source” kontekstach. Kontekst wtyczki jest podawany jako argument dla
getPluginFactory
i jest jedynym kontekstem, w którym występuje parametr
zasobach wtyczki. Oznacza to, że jest to jedyny kontekst, którego można użyć do
powiększa układy we wtyczce.
Jednak kontekst wtyczki może nie mieć ustawionej prawidłowej konfiguracji. Do
aby uzyskać prawidłową konfigurację, w metodach tworzenia
Kontekstem źródła jest zwykle działanie, ale w niektórych przypadkach
może być również Usługą lub innym komponentem Androida. Aby użyć konfiguracji z
z zasobami z kontekstu wtyczki, nowy kontekst musi być
utworzone za pomocą funkcji createConfigurationContext
. Jeśli poprawna konfiguracja jest nieprawidłowa
pojawi się naruszenie trybu ścisłego w Androidzie i większa liczba wyświetleń może
ma nieprawidłowe wymiary.
Context layoutInflationContext = pluginContext.createConfigurationContext(
sourceContext.getResources().getConfiguration());
Zmiany trybu
Niektóre wtyczki mogą obsługiwać wiele trybów komponentów, na przykład tryb sportowy lub eko, które wyglądają indywidualnie. Brak wbudowanej obsługi takich funkcji w CarUi, ale nic nie stoi na przeszkodzie, całkowicie wewnętrznie wdrożyć wtyczkę. Wtyczka może monitorować w warunkach, w których chce określić, kiedy przełączyć tryby: nasłuchiwanie transmisji. Wtyczka nie może wywołać zmiany konfiguracji do zmiany trybów, ale nie zalecamy polegania na zmianach konfiguracji a ręczna aktualizacja wyglądu każdego komponentu jest płynniejsza. dla użytkownika, a także umożliwia przejścia, które są niedostępne w przypadku zmian konfiguracji.
Jetpack Compose
Wtyczki można wdrażać za pomocą Jetpack Compose, ale jest to poziom alfa i nie powinny być uważane za stabilne.
Wtyczki, których można używać
ComposeView
aby utworzyć platformę z obsługą funkcji tworzenia wiadomości, na której będą renderowane. ComposeView
będzie
jakie dane są zwracane do aplikacji za pomocą metody getView
w komponentach.
Jednym z poważnych problemów z używaniem atrybutu ComposeView
jest to, że ustawia on tagi w widoku głównym.
w układzie, aby przechowywać zmienne globalne, które są udostępniane
różnych obiektów ComposeView w hierarchii. Ponieważ identyfikatory zasobów wtyczki nie są
w przestrzeni nazw niezależnie od aplikacji, może to powodować konflikty, gdy
tagów aplikacji i zestawu wtyczek w tym samym widoku. Niestandardowy
ComposeViewWithLifecycle
, który przenosi te zmienne globalne do sekcji
ComposeView
podano poniżej. Również w tym przypadku nie należy traktować tego parametru jako stabilnego.
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)
// }
}