[Compose] Upgrade to Compose Alpha 12 (#1743)

Also upgraded Mavericks and Dagger in the compose sample app
diff --git a/build.gradle b/build.gradle
index ab1533e..a180923 100644
--- a/build.gradle
+++ b/build.gradle
@@ -2,9 +2,9 @@
 
 buildscript {
   ext {
-    composeVersion = '1.0.0-alpha11'
-    kotlinVersion = '1.4.21-2'
-    daggerVersion = '2.30.1'
+    composeVersion = '1.0.0-alpha12'
+    kotlinVersion = '1.4.30'
+    daggerVersion = '2.32'
   }
 
   repositories {
@@ -16,7 +16,7 @@
   }
   dependencies {
     classpath 'org.ajoberstar:grgit:1.9.3'
-    classpath 'com.android.tools.build:gradle:7.0.0-alpha05'
+    classpath 'com.android.tools.build:gradle:7.0.0-alpha06'
     classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
     classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlinVersion"
     classpath 'org.ajoberstar:grgit:1.9.3'
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 79f7a09..0ea3e8a 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https://services.gradle.org/distributions/gradle-6.8-all.zip
+distributionUrl=https://services.gradle.org/distributions/gradle-6.8.2-all.zip
diff --git a/lottie-compose/build.gradle b/lottie-compose/build.gradle
index 0d60d39..7cecc15 100644
--- a/lottie-compose/build.gradle
+++ b/lottie-compose/build.gradle
@@ -6,7 +6,7 @@
 
 android {
   compileSdkVersion 30
-  buildToolsVersion "30.0.2"
+  buildToolsVersion "30.0.3"
 
   defaultConfig {
     minSdkVersion 21
diff --git a/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimation.kt b/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimation.kt
index 8cf482e..1c7a10a 100644
--- a/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimation.kt
+++ b/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimation.kt
@@ -3,21 +3,14 @@
 import androidx.annotation.FloatRange
 import androidx.compose.foundation.Canvas
 import androidx.compose.foundation.layout.aspectRatio
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.SideEffect
-import androidx.compose.runtime.dispatch.withFrameNanos
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.DisposableEffect
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
+import androidx.compose.runtime.*
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
 import androidx.compose.ui.graphics.drawscope.withTransform
 import androidx.compose.ui.graphics.nativeCanvas
 import androidx.compose.ui.platform.AmbientContext
+import androidx.compose.ui.platform.LocalContext
 import com.airbnb.lottie.LottieComposition
 import com.airbnb.lottie.LottieCompositionFactory
 import com.airbnb.lottie.LottieDrawable
@@ -32,7 +25,7 @@
  */
 @Composable
 fun rememberLottieComposition(spec: LottieAnimationSpec): LottieCompositionResult {
-    val context = AmbientContext.current
+    val context = LocalContext.current
     var result: LottieCompositionResult by remember { mutableStateOf(LottieCompositionResult.Loading) }
     DisposableEffect(spec) {
         var isDisposed = false
diff --git a/sample-compose/build.gradle b/sample-compose/build.gradle
index dfc9d74..91745e7 100644
--- a/sample-compose/build.gradle
+++ b/sample-compose/build.gradle
@@ -34,7 +34,9 @@
         "-Xallow-jvm-ir-dependencies",
         "-Xskip-prerelease-check",
         "-Xuse-experimental=kotlinx.coroutines.ExperimentalCoroutinesApi",
+        "-Xuse-experimental=androidx.compose.animation.ExperimentalAnimationApi",
         "-Xopt-in=androidx.compose.material.ExperimentalMaterialApi",
+        "-Xopt-in=kotlin.RequiresOptIn",
         "-Xopt-in=com.airbnb.mvrx.InternalMavericksApi"
     ]
   }
@@ -59,18 +61,18 @@
   implementation project(':lottie-compose')
   implementation 'androidx.core:core-ktx:1.3.2'
   implementation 'androidx.multidex:multidex:2.0.1'
-  implementation 'androidx.appcompat:appcompat:1.2.0'
-  implementation 'com.google.android.material:material:1.2.1'
+  implementation 'androidx.activity:activity-ktx:1.2.0'
+  implementation 'androidx.activity:activity-compose:1.3.0-alpha02'
+  implementation 'com.google.android.material:material:1.3.0'
   implementation "androidx.compose.ui:ui:$composeVersion"
   implementation "androidx.compose.material:material:$composeVersion"
   implementation "androidx.compose.material:material-icons-extended:$composeVersion"
   implementation "androidx.compose.ui:ui-tooling:$composeVersion"
-  implementation "androidx.navigation:navigation-compose:1.0.0-alpha06"
-  implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.0-rc01'
-  implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0-rc01'
+  implementation "androidx.navigation:navigation-compose:1.0.0-alpha07"
+  implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.0'
+  implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0'
 
-  implementation "androidx.navigation:navigation-fragment-ktx:2.3.2"
-  implementation "androidx.navigation:navigation-ui-ktx:2.3.2"
+  implementation "androidx.navigation:navigation-ui-ktx:2.3.3"
 
   implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2'
   implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.2'
@@ -78,13 +80,10 @@
   implementation "com.google.dagger:dagger:$daggerVersion"
   kapt "com.google.dagger:dagger-compiler:$daggerVersion"
 
-  compileOnly 'com.squareup.inject:assisted-inject-annotations-dagger2:0.6.0'
-  kapt 'com.squareup.inject:assisted-inject-processor-dagger2:0.6.0'
-
   implementation 'com.squareup.retrofit2:retrofit:2.9.0'
   implementation 'com.squareup.retrofit2:converter-moshi:2.9.0'
-  implementation "dev.chrisbanes.accompanist:accompanist-coil:0.5.0"
-  implementation 'com.airbnb.android:mvrx:2.0.0-beta3'
+  implementation "dev.chrisbanes.accompanist:accompanist-coil:0.5.1"
+  implementation 'com.airbnb.android:mavericks:2.0.0'
 
   testImplementation 'junit:junit:4.13.1'
   androidTestImplementation 'androidx.test.ext:junit:1.1.2'
diff --git a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/Ambients.kt b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/Ambients.kt
deleted file mode 100644
index 7f2370a..0000000
--- a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/Ambients.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-package com.airbnb.lottie.sample.compose
-
-import androidx.activity.OnBackPressedDispatcher
-import androidx.compose.runtime.ambientOf
-
-val AmbientBackPressedDispatcher = ambientOf<OnBackPressedDispatcher> { error("No BackPressedDispatcher specified.") }
\ No newline at end of file
diff --git a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/ComposeActivity.kt b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/ComposeActivity.kt
index 318758c..ee7ae9e 100644
--- a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/ComposeActivity.kt
+++ b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/ComposeActivity.kt
@@ -1,33 +1,22 @@
 package com.airbnb.lottie.sample.compose
 
 import android.os.Bundle
-import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
 import androidx.annotation.DrawableRes
 import androidx.annotation.StringRes
 import androidx.appcompat.app.AppCompatActivity
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.padding
-import androidx.compose.material.BottomNavigation
-import androidx.compose.material.BottomNavigationItem
-import androidx.compose.material.Icon
-import androidx.compose.material.Scaffold
-import androidx.compose.material.Text
+import androidx.compose.material.*
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Providers
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.platform.AmbientContext
-import androidx.compose.ui.platform.setContent
+import androidx.compose.ui.res.painterResource
 import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.res.vectorResource
 import androidx.compose.ui.unit.dp
-import androidx.navigation.compose.KEY_ROUTE
-import androidx.navigation.compose.NavHost
-import androidx.navigation.compose.composable
-import androidx.navigation.compose.currentBackStackEntryAsState
-import androidx.navigation.compose.navigate
-import androidx.navigation.compose.rememberNavController
+import androidx.navigation.compose.*
 import com.airbnb.lottie.compose.LottieAnimationSpec
 import com.airbnb.lottie.sample.compose.lottiefiles.LottieFilesPage
 import com.airbnb.lottie.sample.compose.player.PlayerPage
@@ -36,7 +25,7 @@
 import com.airbnb.lottie.sample.compose.ui.LottieTheme
 import com.airbnb.lottie.sample.compose.ui.Teal
 import com.airbnb.lottie.sample.compose.ui.toColorSafe
-import com.airbnb.lottie.sample.compose.utils.AmbientNavController
+import com.airbnb.lottie.sample.compose.utils.LocalNavController
 import com.airbnb.lottie.sample.compose.utils.getBase64String
 
 class ComposeActivity : AppCompatActivity() {
@@ -53,8 +42,7 @@
         val navController = rememberNavController()
 
         Providers(
-            AmbientNavController provides navController,
-            AmbientBackPressedDispatcher provides (AmbientContext.current as ComponentActivity).onBackPressedDispatcher
+            LocalNavController provides navController,
         ) {
             LottieTheme {
                 Scaffold(
@@ -71,7 +59,7 @@
                                 BottomNavigationItem(
                                     icon = {
                                         Icon(
-                                            imageVector = vectorResource(item.iconRes),
+                                            painter = painterResource(item.iconRes),
                                             contentDescription = null
                                         )
                                     },
diff --git a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/composables/SeekBar.kt b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/composables/SeekBar.kt
deleted file mode 100644
index 8eacd76..0000000
--- a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/composables/SeekBar.kt
+++ /dev/null
@@ -1,90 +0,0 @@
-package com.airbnb.lottie.sample.compose.composables
-
-import androidx.compose.foundation.Canvas
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.preferredHeight
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.geometry.Size
-import androidx.compose.ui.gesture.DragObserver
-import androidx.compose.ui.gesture.dragGestureFilter
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.layout.onGloballyPositioned
-import androidx.compose.ui.tooling.preview.Preview
-import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.dp
-
-@Composable
-fun SeekBar(
-    progress: Float,
-    onProgressChanged: (Float) -> Unit,
-    modifier: Modifier = Modifier,
-    trackHeight: Dp = 2.dp,
-    thumbRadius: Dp = 4.dp,
-    unfilledTrackColor: Color = Color.LightGray,
-    filledTrackColor: Color = Color.Red,
-) {
-    var width = 0
-    var dragStartProgress = 0f
-    var dragDistanceX = 0f
-    val dragObserver = remember {
-        object : DragObserver {
-            override fun onStart(downPosition: Offset) {
-                dragStartProgress = downPosition.x / width.toFloat()
-                if (width > 0) onProgressChanged(dragStartProgress)
-            }
-            override fun onDrag(dragDistance: Offset): Offset {
-                dragDistanceX += dragDistance.x
-                if (width > 0) {
-                    val newProgress = (dragStartProgress + dragDistanceX / width.toFloat()).coerceIn(0f, 1f)
-                    onProgressChanged(newProgress)
-                }
-                return dragDistance
-            }
-
-            override fun onStop(velocity: Offset) {
-                dragDistanceX = 0f
-            }
-        }
-    }
-    Row(
-        modifier = Modifier
-            .onGloballyPositioned { width = it.size.width }
-            .dragGestureFilter(dragObserver, startDragImmediately = true)
-            .padding(vertical = 12.dp)
-            .then(modifier)
-    ) {
-        Canvas(
-            modifier = Modifier
-                .preferredHeight(thumbRadius * 2f)
-                .fillMaxWidth()
-        ) {
-            val thumbX = size.width * progress
-            drawRect(
-                color = unfilledTrackColor,
-                topLeft = Offset(thumbX, size.height / 2f - trackHeight.toPx() / 2f),
-                size = Size(size.width - thumbX, trackHeight.toPx())
-            )
-            drawRect(
-                color = filledTrackColor,
-                topLeft = Offset(0f, size.height / 2f - trackHeight.toPx() / 2f),
-                size = Size(thumbX, trackHeight.toPx())
-            )
-            drawCircle(
-                color = filledTrackColor,
-                radius = thumbRadius.toPx(),
-                center = Offset(thumbX, size.height / 2f)
-            )
-        }
-    }
-}
-
-@Preview
-@Composable
-fun SeekBarPreview() {
-    SeekBar(0.5f, {})
-}
\ No newline at end of file
diff --git a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/dagger/DaggerMavericksViewModelFactory.kt b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/dagger/DaggerMavericksViewModelFactory.kt
index 3887139..4d25a91 100644
--- a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/dagger/DaggerMavericksViewModelFactory.kt
+++ b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/dagger/DaggerMavericksViewModelFactory.kt
@@ -4,7 +4,10 @@
 import com.airbnb.lottie.sample.compose.LottieComposeApplication
 import com.airbnb.mvrx.*
 
-abstract class DaggerMvRxViewModelFactory<VM : MavericksViewModel<S>, S : MavericksState>(
+inline fun <reified VM : MavericksViewModel<S>, S : MavericksState> daggerMavericksViewModelFactory() = DaggerMavericksViewModelFactory<VM, S>(VM::class.java)
+
+
+class DaggerMavericksViewModelFactory<VM : MavericksViewModel<S>, S : MavericksState>(
         private val viewModelClass: Class<out MavericksViewModel<S>>
 ) : MavericksViewModelFactory<VM, S> {
 
diff --git a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/dagger/MavericksModule.kt b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/dagger/MavericksModule.kt
index 3d0fedc..6b8893b 100644
--- a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/dagger/MavericksModule.kt
+++ b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/dagger/MavericksModule.kt
@@ -3,13 +3,11 @@
 import com.airbnb.lottie.sample.compose.lottiefiles.LottieFilesRecentAndPopularViewModel
 import com.airbnb.lottie.sample.compose.lottiefiles.LottieFilesSearchViewModel
 import com.airbnb.lottie.sample.compose.showcase.ShowcaseViewModel
-import com.squareup.inject.assisted.dagger2.AssistedModule
 import dagger.Binds
 import dagger.Module
 import dagger.multibindings.IntoMap
 
-@AssistedModule
-@Module(includes = [AssistedInject_AppModule::class])
+@Module
 interface AppModule {
 
     @Binds
diff --git a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/lottiefiles/LottieFilesPage.kt b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/lottiefiles/LottieFilesPage.kt
index 8cfe62e..a2edd2c 100644
--- a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/lottiefiles/LottieFilesPage.kt
+++ b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/lottiefiles/LottieFilesPage.kt
@@ -3,20 +3,17 @@
 import androidx.annotation.StringRes
 import androidx.compose.animation.core.animateDpAsState
 import androidx.compose.animation.core.animateFloatAsState
-import androidx.compose.animation.core.spring
 import androidx.compose.foundation.background
 import androidx.compose.foundation.clickable
 import androidx.compose.foundation.layout.*
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.Text
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.setValue
 import androidx.compose.runtime.*
-import androidx.compose.runtime.savedinstancestate.savedInstanceState
+import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.onGloballyPositioned
-import androidx.compose.ui.platform.AmbientDensity
+import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
 import com.airbnb.lottie.sample.compose.R
@@ -30,7 +27,7 @@
 
 @Composable
 fun LottieFilesPage() {
-    var tab by savedInstanceState { LottieFilesTab.Recent }
+    var tab by rememberSaveable { mutableStateOf(LottieFilesTab.Recent) }
 
     Column {
         Marquee("LottieFiles")
@@ -74,7 +71,7 @@
     onClick: () -> Unit
 ) {
     val textWidth = remember { mutableStateOf(0) }
-    val pxRatio = with(AmbientDensity.current) { 1.dp.toPx() }
+    val pxRatio = with(LocalDensity.current) { 1.dp.toPx() }
     val tabWidth by animateDpAsState(if (isSelected) (textWidth.value / pxRatio).dp else 0.dp)
     val tabAlpha by animateFloatAsState(if (isSelected) 1f else 0f)
     Column(
diff --git a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/lottiefiles/LottieFilesRecentsAndPopularPage.kt b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/lottiefiles/LottieFilesRecentsAndPopularPage.kt
index d7d0ca8..1348d19 100644
--- a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/lottiefiles/LottieFilesRecentsAndPopularPage.kt
+++ b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/lottiefiles/LottieFilesRecentsAndPopularPage.kt
@@ -16,23 +16,23 @@
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
 import androidx.navigation.compose.navigate
-import com.airbnb.lottie.sample.compose.R
 import com.airbnb.lottie.sample.compose.Route
 import com.airbnb.lottie.sample.compose.api.AnimationDataV2
 import com.airbnb.lottie.sample.compose.api.LottieFilesApi
 import com.airbnb.lottie.sample.compose.composables.AnimationRow
 import com.airbnb.lottie.sample.compose.dagger.AssistedViewModelFactory
-import com.airbnb.lottie.sample.compose.dagger.DaggerMvRxViewModelFactory
+import com.airbnb.lottie.sample.compose.dagger.daggerMavericksViewModelFactory
+import com.airbnb.lottie.sample.compose.utils.collectState
 import com.airbnb.lottie.sample.compose.utils.findNavController
-import com.airbnb.lottie.sample.compose.utils.mavericksViewModelAndState
+import com.airbnb.lottie.sample.compose.utils.mavericksViewModel
 import com.airbnb.mvrx.MavericksState
 import com.airbnb.mvrx.MavericksViewModel
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
-import kotlinx.coroutines.Dispatchers
+import com.airbnb.mvrx.MavericksViewModelFactory
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.launch
 
@@ -67,7 +67,7 @@
     fun fetchNextPage() = withState { state ->
         fetchJob?.cancel()
         if (state.currentPage >= state.lastPage) return@withState
-        fetchJob = viewModelScope.launch(Dispatchers.IO) {
+        fetchJob = viewModelScope.launch {
             val response = try {
                 Log.d(TAG, "Fetching page ${state.currentPage + 1}")
                 when (state.mode) {
@@ -92,19 +92,21 @@
 
     fun setMode(mode: LottieFilesMode) = setState { copy(mode = mode) }
 
-    @AssistedInject.Factory
+    @AssistedFactory
     interface Factory : AssistedViewModelFactory<LottieFilesRecentAndPopularViewModel, LottieFilesRecentAndPopularState> {
         override fun create(initialState: LottieFilesRecentAndPopularState): LottieFilesRecentAndPopularViewModel
     }
 
-    companion object : DaggerMvRxViewModelFactory<LottieFilesRecentAndPopularViewModel, LottieFilesRecentAndPopularState>(LottieFilesRecentAndPopularViewModel::class.java) {
+    companion object :
+        MavericksViewModelFactory<LottieFilesRecentAndPopularViewModel, LottieFilesRecentAndPopularState> by daggerMavericksViewModelFactory() {
         private const val TAG = "LottieFilesVM"
     }
 }
 
 @Composable
 fun LottieFilesRecentAndPopularPage(mode: LottieFilesMode) {
-    val (viewModel, state) = mavericksViewModelAndState<LottieFilesRecentAndPopularViewModel, LottieFilesRecentAndPopularState>()
+    val viewModel: LottieFilesRecentAndPopularViewModel = mavericksViewModel()
+    val state = viewModel.collectState()
     val navController = findNavController()
     SideEffect {
         viewModel.setMode(mode)
diff --git a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/lottiefiles/LottieFilesSearchPage.kt b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/lottiefiles/LottieFilesSearchPage.kt
index a075b6c..37e48e2 100644
--- a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/lottiefiles/LottieFilesSearchPage.kt
+++ b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/lottiefiles/LottieFilesSearchPage.kt
@@ -7,11 +7,7 @@
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.foundation.lazy.itemsIndexed
-import androidx.compose.material.FloatingActionButton
-import androidx.compose.material.Icon
-import androidx.compose.material.OutlinedTextField
-import androidx.compose.material.Surface
-import androidx.compose.material.Text
+import androidx.compose.material.*
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.Repeat
 import androidx.compose.runtime.Composable
@@ -29,13 +25,16 @@
 import com.airbnb.lottie.sample.compose.api.LottieFilesApi
 import com.airbnb.lottie.sample.compose.composables.AnimationRow
 import com.airbnb.lottie.sample.compose.dagger.AssistedViewModelFactory
-import com.airbnb.lottie.sample.compose.dagger.DaggerMvRxViewModelFactory
+import com.airbnb.lottie.sample.compose.dagger.daggerMavericksViewModelFactory
+import com.airbnb.lottie.sample.compose.utils.collectState
 import com.airbnb.lottie.sample.compose.utils.findNavController
-import com.airbnb.lottie.sample.compose.utils.mavericksViewModelAndState
+import com.airbnb.lottie.sample.compose.utils.mavericksViewModel
 import com.airbnb.mvrx.MavericksState
 import com.airbnb.mvrx.MavericksViewModel
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
+import com.airbnb.mvrx.MavericksViewModelFactory
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.launch
@@ -60,7 +59,7 @@
             if (query.isBlank()) {
                 setState { copy(results = emptyList(), currentPage = 1, lastPage = 1, fetchException = false) }
             } else {
-                fetchJob = viewModelScope.launch(Dispatchers.IO) {
+                fetchJob = viewModelScope.launch {
                     val results = try {
                         api.search(query, 1)
                     } catch (e: Exception) {
@@ -83,7 +82,7 @@
     fun fetchNextPage() = withState { state ->
         fetchJob?.cancel()
         if (state.currentPage >= state.lastPage) return@withState
-        fetchJob = viewModelScope.launch(Dispatchers.IO) {
+        fetchJob = viewModelScope.launch {
             val response = try {
                 Log.d("Gabe", "Fetching page ${state.currentPage + 1}")
                 api.search(state.query, state.currentPage + 1)
@@ -103,17 +102,18 @@
 
     fun setQuery(query: String) = setState { copy(query = query, currentPage = 1, results = emptyList()) }
 
-    @AssistedInject.Factory
+    @AssistedFactory
     interface Factory : AssistedViewModelFactory<LottieFilesSearchViewModel, LottieFilesSearchState> {
         override fun create(initialState: LottieFilesSearchState): LottieFilesSearchViewModel
     }
 
-    companion object : DaggerMvRxViewModelFactory<LottieFilesSearchViewModel, LottieFilesSearchState>(LottieFilesSearchViewModel::class.java)
+    companion object : MavericksViewModelFactory<LottieFilesSearchViewModel, LottieFilesSearchState> by daggerMavericksViewModelFactory()
 }
 
 @Composable
 fun LottieFilesSearchPage() {
-    val (viewModel, state) = mavericksViewModelAndState<LottieFilesSearchViewModel, LottieFilesSearchState>()
+    val viewModel: LottieFilesSearchViewModel = mavericksViewModel()
+    val state = viewModel.collectState()
     val navController = findNavController()
     LottieFilesSearchPage(
         state,
diff --git a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/player/PlayerPage.kt b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/player/PlayerPage.kt
index 85083db..a8c81f4 100644
--- a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/player/PlayerPage.kt
+++ b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/player/PlayerPage.kt
@@ -1,50 +1,20 @@
 package com.airbnb.lottie.sample.compose.player
 
 import androidx.activity.OnBackPressedDispatcher
+import androidx.activity.compose.LocalOnBackPressedDispatcherOwner
 import androidx.compose.animation.AnimatedVisibility
-import androidx.compose.animation.ExperimentalAnimationApi
 import androidx.compose.animation.expandVertically
 import androidx.compose.animation.shrinkVertically
 import androidx.compose.foundation.*
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.PaddingValues
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.fillMaxHeight
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.heightIn
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.preferredSize
-import androidx.compose.foundation.layout.preferredWidth
+import androidx.compose.foundation.layout.*
 import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.foundation.lazy.itemsIndexed
 import androidx.compose.foundation.shape.CircleShape
 import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material.Icon
-import androidx.compose.material.IconButton
-import androidx.compose.material.Scaffold
-import androidx.compose.material.Surface
-import androidx.compose.material.Text
-import androidx.compose.material.TopAppBar
+import androidx.compose.material.*
 import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Close
-import androidx.compose.material.icons.filled.Pause
-import androidx.compose.material.icons.filled.PlayArrow
-import androidx.compose.material.icons.filled.RemoveRedEye
-import androidx.compose.material.icons.filled.Repeat
-import androidx.compose.material.icons.filled.Warning
-import androidx.compose.material.rememberScaffoldState
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.MutableState
-import androidx.compose.runtime.Providers
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.onCommit
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
+import androidx.compose.material.icons.filled.*
+import androidx.compose.runtime.*
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.clip
@@ -58,31 +28,23 @@
 import androidx.compose.ui.unit.sp
 import androidx.compose.ui.window.Dialog
 import com.airbnb.lottie.LottieComposition
-import com.airbnb.lottie.compose.LottieAnimation
-import com.airbnb.lottie.compose.LottieAnimationSpec
-import com.airbnb.lottie.compose.LottieAnimationState
-import com.airbnb.lottie.compose.LottieCompositionResult
-import com.airbnb.lottie.compose.rememberLottieAnimationState
-import com.airbnb.lottie.compose.rememberLottieComposition
-import com.airbnb.lottie.sample.compose.AmbientBackPressedDispatcher
+import com.airbnb.lottie.compose.*
 import com.airbnb.lottie.sample.compose.BuildConfig
 import com.airbnb.lottie.sample.compose.R
 import com.airbnb.lottie.sample.compose.composables.DebouncedCircularProgressIndicator
-import com.airbnb.lottie.sample.compose.composables.SeekBar
 import com.airbnb.lottie.sample.compose.ui.Teal
-import com.airbnb.lottie.sample.compose.utils.drawTopBorder
+import com.airbnb.lottie.sample.compose.utils.drawBottomBorder
 import com.airbnb.lottie.sample.compose.utils.maybeBackground
 import com.airbnb.lottie.sample.compose.utils.maybeDrawBorder
 import kotlin.math.ceil
 import kotlin.math.roundToInt
 
-@OptIn(ExperimentalAnimationApi::class)
 @Composable
 fun PlayerPage(
     spec: LottieAnimationSpec,
     animationBackgroundColor: Color? = null,
 ) {
-    val backPressedDispatcher = AmbientBackPressedDispatcher.current
+    val backPressedDispatcher = LocalOnBackPressedDispatcherOwner.current.onBackPressedDispatcher
     val compositionResult = rememberLottieComposition(spec)
     val animationState = rememberLottieAnimationState(autoPlay = true, repeatCount = Integer.MAX_VALUE)
     val scaffoldState = rememberScaffoldState()
@@ -180,24 +142,22 @@
                     )
                 }
             }
-            if (speedToolbar.value && !focusMode) {
+            ExpandVisibility(speedToolbar.value && !focusMode) {
                 SpeedToolbar(
                     speed = animationState.speed,
                     onSpeedChanged = { animationState.speed = it }
                 )
             }
-            if (backgroundColorToolbar.value && !focusMode) {
+            ExpandVisibility(!focusMode && backgroundColorToolbar.value) {
                 BackgroundColorToolbar(
                     animationBackgroundColor = animationBackgroundColor,
                     onColorChanged = { backgroundColor = it }
                 )
             }
-            AnimatedVisibility(
-                visible = !focusMode,
-                enter = expandVertically(),
-                exit = shrinkVertically()
-            ) {
+            ExpandVisibility(!focusMode) {
                 PlayerControlsRow(animationState, compositionResult())
+            }
+            ExpandVisibility(!focusMode) {
                 Toolbar(
                     border = borderToolbar,
                     speed = speedToolbar,
@@ -215,6 +175,17 @@
 }
 
 @Composable
+private fun ColumnScope.ExpandVisibility(visible: Boolean, content: @Composable () -> Unit) {
+    AnimatedVisibility(
+        visible = visible,
+        enter = expandVertically(),
+        exit = shrinkVertically()
+    ) {
+        content()
+    }
+}
+
+@Composable
 private fun PlayerControlsRow(
     animationState: LottieAnimationState,
     composition: LottieComposition?,
@@ -231,8 +202,6 @@
     ) {
         Row(
             verticalAlignment = Alignment.CenterVertically,
-            modifier = Modifier
-                .drawTopBorder()
         ) {
             Box(
                 contentAlignment = Alignment.Center
@@ -254,11 +223,10 @@
                         .padding(top = 48.dp, bottom = 8.dp)
                 )
             }
-            SeekBar(
-                progress = animationState.progress,
-                onProgressChanged = {
-                    animationState.progress = it
-                },
+            Slider(
+                value = animationState.progress,
+                onValueChange = { animationState.progress = it },
+
                 modifier = Modifier.weight(1f)
             )
             IconButton(onClick = {
@@ -291,7 +259,7 @@
     Row(
         horizontalArrangement = Arrangement.SpaceBetween,
         modifier = Modifier
-            .drawTopBorder()
+            .drawBottomBorder()
             .padding(vertical = 12.dp, horizontal = 16.dp)
             .fillMaxWidth()
     ) {
@@ -330,7 +298,7 @@
     Row(
         horizontalArrangement = Arrangement.SpaceBetween,
         modifier = Modifier
-            .drawTopBorder()
+            .drawBottomBorder()
             .padding(vertical = 12.dp, horizontal = 16.dp)
             .fillMaxWidth()
     ) {
@@ -377,7 +345,12 @@
     outlineMasksAndMattes: MutableState<Boolean>,
     applyOpacityToLayers: MutableState<Boolean>,
 ) {
-    Row(Modifier.horizontalScroll(rememberScrollState())) {
+    Row(
+        modifier = Modifier
+            .horizontalScroll(rememberScrollState())
+            .padding(horizontal = 8.dp)
+            .padding(bottom = 8.dp)
+    ) {
         ToolbarChip(
             iconRes = R.drawable.ic_masks_and_mattes,
             label = stringResource(R.string.toolbar_item_masks),
@@ -440,7 +413,7 @@
                             textAlign = TextAlign.Left,
                             modifier = Modifier
                                 .fillMaxWidth()
-                                .run { if (i != 0) drawTopBorder() else this }
+                                .run { if (i != 0) drawBottomBorder() else this }
                                 .padding(vertical = 12.dp, horizontal = 16.dp)
                         )
                     }
@@ -459,9 +432,5 @@
 @Preview(name = "Player")
 @Composable
 fun PlayerPagePreview() {
-    Providers(
-        AmbientBackPressedDispatcher provides OnBackPressedDispatcher()
-    ) {
-        PlayerPage(LottieAnimationSpec.Url("https://lottiefiles.com/download/public/32922"))
-    }
+    PlayerPage(LottieAnimationSpec.Url("https://lottiefiles.com/download/public/32922"))
 }
\ No newline at end of file
diff --git a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/player/ToolbarChip.kt b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/player/ToolbarChip.kt
index 8a7fda9..69904b8 100644
--- a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/player/ToolbarChip.kt
+++ b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/player/ToolbarChip.kt
@@ -13,8 +13,8 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.clipToBounds
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.painterResource
 import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.res.vectorResource
 import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.unit.dp
@@ -46,7 +46,7 @@
         ) {
             if (iconRes != 0) {
                 Icon(
-                    vectorResource(iconRes),
+                    painterResource(iconRes),
                     tint = if (isActivated) Color.White else unActivatedColor,
                     modifier = Modifier
                         .preferredSize(12.dp),
diff --git a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/preview/PreviewPage.kt b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/preview/PreviewPage.kt
index 95047f9..3db713b 100644
--- a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/preview/PreviewPage.kt
+++ b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/preview/PreviewPage.kt
@@ -4,40 +4,26 @@
 import androidx.annotation.StringRes
 import androidx.compose.foundation.background
 import androidx.compose.foundation.clickable
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.preferredHeight
+import androidx.compose.foundation.layout.*
 import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material.Divider
-import androidx.compose.material.Icon
-import androidx.compose.material.OutlinedTextField
-import androidx.compose.material.Surface
-import androidx.compose.material.Text
-import androidx.compose.material.TextButton
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
+import androidx.compose.material.*
+import androidx.compose.runtime.*
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.platform.AmbientContext
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.painterResource
 import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.res.vectorResource
 import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
 import androidx.compose.ui.window.Dialog
+import androidx.navigation.compose.navigate
 import com.airbnb.lottie.sample.compose.R
 import com.airbnb.lottie.sample.compose.Route
 import com.airbnb.lottie.sample.compose.composables.Marquee
 import com.airbnb.lottie.sample.compose.ui.LottieTheme
 import com.airbnb.lottie.sample.compose.utils.findNavController
-import androidx.navigation.compose.navigate
 
 @Composable
 fun PreviewPage() {
@@ -92,7 +78,7 @@
                     .preferredHeight(48.dp)
             ) {
                 Icon(
-                    vectorResource(iconRes),
+                    painterResource(iconRes),
                     modifier = Modifier
                         .align(Alignment.CenterVertically)
                         .padding(16.dp),
@@ -112,7 +98,7 @@
 @Composable
 fun AssetsDialog(isShowing: Boolean, onDismiss: () -> Unit, onAssetSelected: (assetName: String) -> Unit) {
     if (!isShowing) return
-    val context = AmbientContext.current
+    val context = LocalContext.current
     val assets = context.assets.list("")
         ?.asSequence()
         ?.filter { it.endsWith(".json") || it.endsWith(".zip") }
diff --git a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/showcase/ShowcasePage.kt b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/showcase/ShowcasePage.kt
index 11caae5..e00de2d 100644
--- a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/showcase/ShowcasePage.kt
+++ b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/showcase/ShowcasePage.kt
@@ -16,15 +16,17 @@
 import com.airbnb.lottie.sample.compose.composables.Loader
 import com.airbnb.lottie.sample.compose.composables.Marquee
 import com.airbnb.lottie.sample.compose.ui.LottieTheme
+import com.airbnb.lottie.sample.compose.utils.collectState
 import com.airbnb.lottie.sample.compose.utils.findNavController
-import com.airbnb.lottie.sample.compose.utils.mavericksViewModelAndState
+import com.airbnb.lottie.sample.compose.utils.mavericksViewModel
 import com.airbnb.mvrx.Loading
 import com.airbnb.mvrx.Uninitialized
 
 @Composable
 fun ShowcasePage() {
-    val (_, showcaseState) = mavericksViewModelAndState<ShowcaseViewModel, ShowcaseState>()
-    val featuredAnimations = showcaseState.animations
+    val viewModel: ShowcaseViewModel = mavericksViewModel()
+    val state = viewModel.collectState()
+    val featuredAnimations = state.animations
     val navController = findNavController()
     Box(
         modifier = Modifier.fillMaxSize()
diff --git a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/showcase/ShowcaseViewModel.kt b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/showcase/ShowcaseViewModel.kt
index aea98d0..d92c7c2 100644
--- a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/showcase/ShowcaseViewModel.kt
+++ b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/showcase/ShowcaseViewModel.kt
@@ -3,19 +3,19 @@
 import com.airbnb.lottie.sample.compose.api.AnimationsResponseV2
 import com.airbnb.lottie.sample.compose.api.LottieFilesApi
 import com.airbnb.lottie.sample.compose.dagger.AssistedViewModelFactory
-import com.airbnb.lottie.sample.compose.dagger.DaggerMvRxViewModelFactory
+import com.airbnb.lottie.sample.compose.dagger.daggerMavericksViewModelFactory
 import com.airbnb.mvrx.*
-import com.squareup.inject.assisted.Assisted
-import com.squareup.inject.assisted.AssistedInject
-import kotlinx.coroutines.Dispatchers
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
 
 data class ShowcaseState(
     val animations: Async<AnimationsResponseV2> = Uninitialized
 ) : MavericksState
 
 class ShowcaseViewModel @AssistedInject constructor(
-    @Assisted initialState: ShowcaseState,
-    private var api: LottieFilesApi
+        @Assisted initialState: ShowcaseState,
+        private var api: LottieFilesApi
 ) : MavericksViewModel<ShowcaseState>(initialState) {
 
     init {
@@ -25,15 +25,13 @@
     private fun fetchFeatured() {
         suspend {
             api.getFeatured()
-        }.execute(Dispatchers.IO) {
-            copy(animations = it)
-        }
+        }.execute { copy(animations = it) }
     }
 
-    @AssistedInject.Factory
+    @AssistedFactory
     interface Factory : AssistedViewModelFactory<ShowcaseViewModel, ShowcaseState> {
         override fun create(initialState: ShowcaseState): ShowcaseViewModel
     }
 
-    companion object : DaggerMvRxViewModelFactory<ShowcaseViewModel, ShowcaseState>(ShowcaseViewModel::class.java)
+    companion object : MavericksViewModelFactory<ShowcaseViewModel, ShowcaseState> by daggerMavericksViewModelFactory()
 }
\ No newline at end of file
diff --git a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/utils/ComposeExtensions.kt b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/utils/ComposeExtensions.kt
index 05caca8..09e827b 100644
--- a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/utils/ComposeExtensions.kt
+++ b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/utils/ComposeExtensions.kt
@@ -23,6 +23,10 @@
     drawRect(color, Offset.Zero, size = Size(size.width, 1f))
 })
 
+fun Modifier.drawBottomBorder(color: Color = Color.DarkGray) = this.then(drawBehind {
+    drawRect(color, Offset(0f, size.height - 1f), size = Size(size.width, 1f))
+})
+
 
 fun Modifier.maybeDrawBorder(draw: Boolean, color: Color = Color.Black, width: Dp = 1.dp): Modifier {
     return if (draw) {
diff --git a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/utils/MavericksExtensions.kt b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/utils/MavericksExtensions.kt
index d758483..7571440 100644
--- a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/utils/MavericksExtensions.kt
+++ b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/utils/MavericksExtensions.kt
@@ -1,31 +1,53 @@
 package com.airbnb.lottie.sample.compose.utils
 
 
-import android.annotation.SuppressLint
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
-import androidx.compose.ui.platform.AmbientContext
+import androidx.compose.runtime.remember
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalLifecycleOwner
+import androidx.fragment.app.Fragment
 import androidx.fragment.app.FragmentActivity
-import com.airbnb.mvrx.ActivityViewModelContext
-import com.airbnb.mvrx.Mavericks
-import com.airbnb.mvrx.MavericksState
-import com.airbnb.mvrx.MavericksViewModel
-import com.airbnb.mvrx.MavericksViewModelProvider
-import com.airbnb.mvrx.withState
+import androidx.navigation.NavBackStackEntry
+import com.airbnb.mvrx.*
 
 @Composable
-inline fun <reified VM : MavericksViewModel<S>, reified S : MavericksState> mavericksViewModelAndState(): Pair<VM, S> {
-    val viewModelClass = VM::class
-    val activity = AmbientContext.current as? FragmentActivity ?: error("Composable is not hosted in a FragmentActivity")
-    val keyFactory = { viewModelClass.java.name }
-    @SuppressLint("RestrictedApi")
-    val viewModel = MavericksViewModelProvider.get(
-        viewModelClass = viewModelClass.java,
-        stateClass = S::class.java,
-        viewModelContext = ActivityViewModelContext(activity, activity.intent.extras?.get(Mavericks.KEY_ARG)),
-        key = keyFactory()
-    )
-    val state by viewModel.stateFlow.collectAsState(initial = withState(viewModel) { it })
-    return viewModel to state
+fun <VM : MavericksViewModel<S>, S : MavericksState> VM.collectState(): S {
+    val state by stateFlow.collectAsState(initial = withState(this) { it })
+    return state
 }
+
+@Composable
+inline fun <reified VM : MavericksViewModel<S>, reified S : MavericksState> mavericksViewModel(): VM {
+    val viewModelClass = VM::class
+    val context = LocalContext.current
+    val viewModelContext = when (val lifecycleOwner = LocalLifecycleOwner.current) {
+        is Fragment -> {
+            val activity = lifecycleOwner.requireActivity()
+            val args = lifecycleOwner.arguments?.get(Mavericks.KEY_ARG)
+            FragmentViewModelContext(activity, args, lifecycleOwner)
+        }
+        is FragmentActivity -> {
+            val args = lifecycleOwner.intent.extras?.get(Mavericks.KEY_ARG)
+            ActivityViewModelContext(lifecycleOwner, args)
+        }
+        is NavBackStackEntry -> {
+            val args = lifecycleOwner.arguments?.get(Mavericks.KEY_ARG)
+            val activity = context as? FragmentActivity ?: error("Local context should be a FragmentActivity but it is a ${context::class.simpleName}!")
+            ActivityViewModelContext(activity, args)
+        }
+        else -> error("Unknown LifecycleOwner ${lifecycleOwner::class.simpleName}. Must be Fragment or Activity for now.")
+    }
+    val activity = context as? FragmentActivity ?: error("Composable is not hosted in a FragmentActivity")
+    return remember(viewModelClass, activity) {
+        val keyFactory = { viewModelClass.java.name }
+        @Suppress("RestrictedApi")
+        MavericksViewModelProvider.get(
+            viewModelClass = viewModelClass.java,
+            stateClass = S::class.java,
+            viewModelContext = viewModelContext,
+            key = keyFactory()
+        )
+    }
+}
\ No newline at end of file
diff --git a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/utils/NavigationExtensions.kt b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/utils/NavigationExtensions.kt
index 68d1819..976ae33 100644
--- a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/utils/NavigationExtensions.kt
+++ b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/utils/NavigationExtensions.kt
@@ -3,13 +3,13 @@
 import android.os.Bundle
 import android.util.Base64
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.staticAmbientOf
+import androidx.compose.runtime.staticCompositionLocalOf
 import androidx.navigation.NavController
 import java.nio.charset.StandardCharsets
 
-val AmbientNavController = staticAmbientOf<NavController> { error("You must specify a NavController.") }
+val LocalNavController = staticCompositionLocalOf<NavController> { error("You must specify a NavController.") }
 
 @Composable
-fun findNavController() = AmbientNavController.current
+fun findNavController() = LocalNavController.current
 
 fun Bundle.getBase64String(key: String) = String(Base64.decode(getString(key), Base64.DEFAULT), StandardCharsets.UTF_8)
diff --git a/sample/src/main/kotlin/com/airbnb/lottie/samples/PreviewFragment.kt b/sample/src/main/kotlin/com/airbnb/lottie/samples/PreviewFragment.kt
index cdb3d1b..d6ad662 100644
--- a/sample/src/main/kotlin/com/airbnb/lottie/samples/PreviewFragment.kt
+++ b/sample/src/main/kotlin/com/airbnb/lottie/samples/PreviewFragment.kt
@@ -34,6 +34,7 @@
                 if (requireContext().hasPermission(Manifest.permission.CAMERA)) {
                     startActivity(QRScanActivity.intent(requireContext()))
                 } else {
+                    @Suppress("DEPRECATION")
                     requestPermissions(arrayOf(Manifest.permission.CAMERA), RC_CAMERA_PERMISSION)
                 }
             }
@@ -49,6 +50,7 @@
                         type = "*/*"
                         addCategory(Intent.CATEGORY_OPENABLE)
                     }
+                    @Suppress("DEPRECATION")
                     startActivityForResult(Intent.createChooser(intent, "Select a JSON file"), RC_FILE)
                 } catch (ex: ActivityNotFoundException) {
                     // Potentially direct the user to the Market with a Dialog
diff --git a/sample/src/main/kotlin/com/airbnb/lottie/samples/ShowcaseFragment.kt b/sample/src/main/kotlin/com/airbnb/lottie/samples/ShowcaseFragment.kt
index f5e879a..339a3ee 100644
--- a/sample/src/main/kotlin/com/airbnb/lottie/samples/ShowcaseFragment.kt
+++ b/sample/src/main/kotlin/com/airbnb/lottie/samples/ShowcaseFragment.kt
@@ -13,7 +13,6 @@
 import com.airbnb.lottie.samples.views.marquee
 import com.airbnb.lottie.samples.views.showcaseCarousel
 import com.airbnb.mvrx.*
-import kotlinx.coroutines.Dispatchers
 
 data class ShowcaseState(val response: Async<AnimationResponseV2> = Uninitialized) : MvRxState
 
@@ -21,11 +20,11 @@
     init {
         suspend {
             api.getCollection()
-        }.execute(Dispatchers.IO) { copy(response = it) }
+        }.execute { copy(response = it) }
     }
 
     companion object : MvRxViewModelFactory<ShowcaseViewModel, ShowcaseState> {
-        override fun create(viewModelContext: ViewModelContext, state: ShowcaseState): ShowcaseViewModel? {
+        override fun create(viewModelContext: ViewModelContext, state: ShowcaseState): ShowcaseViewModel {
             val service = viewModelContext.app<LottieApplication>().lottiefilesService
             return ShowcaseViewModel(state, service)
         }
diff --git a/sample/src/main/kotlin/com/airbnb/lottie/samples/testing/FilmStripSnapshotActivity.kt b/sample/src/main/kotlin/com/airbnb/lottie/samples/testing/FilmStripSnapshotActivity.kt
index aab597f..bcc9147 100644
--- a/sample/src/main/kotlin/com/airbnb/lottie/samples/testing/FilmStripSnapshotActivity.kt
+++ b/sample/src/main/kotlin/com/airbnb/lottie/samples/testing/FilmStripSnapshotActivity.kt
@@ -89,6 +89,7 @@
                     binding.filmStripView.draw(canvas)
 
                     val outputFileName = file.name.replace(".json", ".png")
+                    @Suppress("DEPRECATION")
                     val outputFilePath = "${Environment.getExternalStorageDirectory()}/lottie/snapshots/$outputFileName"
                     FileOutputStream(outputFilePath).use {
                         bitmap.compress(Bitmap.CompressFormat.PNG, 100, it)