[Compose] Add support for images
diff --git a/.idea/misc.xml b/.idea/misc.xml
index c3085c1..d72f78c 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -26,6 +26,8 @@
         <entry key="../../../../layout/compose-model-1615661124216.xml" value="0.28631756756756754" />
         <entry key="../../../../layout/compose-model-1615661338631.xml" value="1.0" />
         <entry key="../../../../layout/compose-model-1615661344364.xml" value="0.28607594936708863" />
+        <entry key="../../../../layout/custom_preview.xml" value="1.0" />
+        <entry key="issue-repro/src/main/res/layout/issue_repro_activity.xml" value="0.2545289855072464" />
       </map>
     </option>
   </component>
diff --git a/.idea/modules.xml b/.idea/modules.xml
index 2590511..ec33ca5 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -4,6 +4,7 @@
     <modules>
       <module fileurl="file://$PROJECT_DIR$/.idea/lottie-android.iml" filepath="$PROJECT_DIR$/.idea/lottie-android.iml" />
       <module fileurl="file://$PROJECT_DIR$/.idea/modules/issue-repro/lottie-android.issue-repro.iml" filepath="$PROJECT_DIR$/.idea/modules/issue-repro/lottie-android.issue-repro.iml" />
+      <module fileurl="file://$PROJECT_DIR$/.idea/modules/issue-repro-compose/lottie-android.issue-repro-compose.iml" filepath="$PROJECT_DIR$/.idea/modules/issue-repro-compose/lottie-android.issue-repro-compose.iml" />
       <module fileurl="file://$PROJECT_DIR$/.idea/modules/lottie/lottie-android.lottie.iml" filepath="$PROJECT_DIR$/.idea/modules/lottie/lottie-android.lottie.iml" />
       <module fileurl="file://$PROJECT_DIR$/.idea/modules/lottie-compose/lottie-android.lottie-compose.iml" filepath="$PROJECT_DIR$/.idea/modules/lottie-compose/lottie-android.lottie-compose.iml" />
       <module fileurl="file://$PROJECT_DIR$/.idea/modules/sample/lottie-android.sample.iml" filepath="$PROJECT_DIR$/.idea/modules/sample/lottie-android.sample.iml" />
diff --git a/issue-repro-compose/build.gradle b/issue-repro-compose/build.gradle
new file mode 100755
index 0000000..72f8700
--- /dev/null
+++ b/issue-repro-compose/build.gradle
@@ -0,0 +1,41 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+
+android {
+    compileSdkVersion 30
+    defaultConfig {
+        applicationId "com.airbnb.lottie.issues.compose"
+        minSdkVersion 21
+        targetSdkVersion 30
+        versionCode 1
+        versionName "1.0"
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+    }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+    kotlinOptions {
+        jvmTarget = '1.8'
+    }
+
+    buildFeatures {
+        compose true
+    }
+    composeOptions {
+        kotlinCompilerExtensionVersion composeVersion
+        kotlinCompilerVersion kotlinVersion
+    }
+}
+
+dependencies {
+    implementation project(':lottie-compose')
+    implementation 'androidx.appcompat:appcompat:1.3.0-beta01'
+    implementation 'androidx.activity:activity-compose:1.3.0-alpha04'
+    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"
+}
diff --git a/issue-repro-compose/src/main/AndroidManifest.xml b/issue-repro-compose/src/main/AndroidManifest.xml
new file mode 100755
index 0000000..3a75e49
--- /dev/null
+++ b/issue-repro-compose/src/main/AndroidManifest.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.airbnb.lottie.issues.compose">
+
+    <application
+        android:icon="@mipmap/ic_launcher"
+        android:label="@string/app_name"
+        android:theme="@style/Theme.AppCompat.Light.NoActionBar">
+        <activity android:name=".ComposeIssueReproActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
\ No newline at end of file
diff --git a/issue-repro-compose/src/main/java/com/airbnb/lottie/issues/compose/ComposeIssueReproActivity.kt b/issue-repro-compose/src/main/java/com/airbnb/lottie/issues/compose/ComposeIssueReproActivity.kt
new file mode 100755
index 0000000..4e55a5d
--- /dev/null
+++ b/issue-repro-compose/src/main/java/com/airbnb/lottie/issues/compose/ComposeIssueReproActivity.kt
@@ -0,0 +1,19 @@
+package com.airbnb.lottie.issues.compose
+
+import android.os.Bundle
+import androidx.activity.compose.setContent
+import androidx.appcompat.app.AppCompatActivity
+import androidx.compose.runtime.Composable
+
+class ComposeIssueReproActivity : AppCompatActivity() {
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContent {
+            Content()
+        }
+    }
+
+    @Composable
+    fun Content() {
+    }
+}
diff --git a/issue-repro-compose/src/main/res/mipmap-hdpi/ic_launcher.png b/issue-repro-compose/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100755
index 0000000..898f3ed
--- /dev/null
+++ b/issue-repro-compose/src/main/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/issue-repro-compose/src/main/res/mipmap-mdpi/ic_launcher.png b/issue-repro-compose/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100755
index 0000000..64ba76f
--- /dev/null
+++ b/issue-repro-compose/src/main/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/issue-repro-compose/src/main/res/mipmap-xhdpi/ic_launcher.png b/issue-repro-compose/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100755
index 0000000..e5ed465
--- /dev/null
+++ b/issue-repro-compose/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/issue-repro-compose/src/main/res/mipmap-xxhdpi/ic_launcher.png b/issue-repro-compose/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100755
index 0000000..b0907ca
--- /dev/null
+++ b/issue-repro-compose/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/issue-repro-compose/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/issue-repro-compose/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100755
index 0000000..2c18de9
--- /dev/null
+++ b/issue-repro-compose/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/issue-repro-compose/src/main/res/values/strings.xml b/issue-repro-compose/src/main/res/values/strings.xml
new file mode 100755
index 0000000..dd8717f
--- /dev/null
+++ b/issue-repro-compose/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+<resources>
+    <string name="app_name">Lottie Issue</string>
+</resources>
diff --git a/lottie-compose/src/main/java/com/airbnb/lottie/ImageAssetDelegateSetter.kt b/lottie-compose/src/main/java/com/airbnb/lottie/ImageAssetDelegateSetter.kt
new file mode 100644
index 0000000..222e45d
--- /dev/null
+++ b/lottie-compose/src/main/java/com/airbnb/lottie/ImageAssetDelegateSetter.kt
@@ -0,0 +1,10 @@
+package com.airbnb.lottie
+
+import com.airbnb.lottie.manager.ImageAssetManager
+
+/**
+ * Proxy for this internal API.
+ */
+internal fun LottieDrawable.setImageAssetManager(imageAssetManager: ImageAssetManager?) {
+    this.setImageAssetManager(imageAssetManager)
+}
\ No newline at end of file
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 a242575..57feaa7 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
@@ -13,6 +13,8 @@
 import com.airbnb.lottie.LottieComposition
 import com.airbnb.lottie.LottieCompositionFactory
 import com.airbnb.lottie.LottieDrawable
+import com.airbnb.lottie.manager.ImageAssetManager
+import com.airbnb.lottie.setImageAssetManager
 import java.io.FileInputStream
 import java.util.concurrent.TimeUnit
 import java.util.zip.ZipInputStream
@@ -80,6 +82,16 @@
     modifier: Modifier = Modifier,
 ) {
     val drawable = remember { LottieDrawable() }
+    var imageAssetManager: ImageAssetManager? by remember { mutableStateOf(null) }
+
+    if (composition?.hasImages() == true) {
+        val context = LocalContext.current
+        LaunchedEffect(context, composition, state.imageAssetsFolder, state.imageAssetDelegate) {
+            imageAssetManager = ImageAssetManager(context, state.imageAssetsFolder, state.imageAssetDelegate, composition.images)
+        }
+    } else {
+        imageAssetManager = null
+    }
 
     SideEffect {
         drawable.composition = composition
@@ -119,10 +131,8 @@
             .maintainAspectRatio(composition)
     ) {
         drawIntoCanvas { canvas ->
-            drawable.progress = state.progress
-            drawable.setOutlineMasksAndMattes(state.outlineMasksAndMattes)
-            drawable.isApplyingOpacityToLayersEnabled = state.applyOpacityToLayers
-            drawable.enableMergePathsForKitKatAndAbove(state.enableMergePaths)
+            state.applyTo(drawable)
+            drawable.setImageAssetManager(imageAssetManager)
             withTransform({
                 scale(size.width / composition.bounds.width().toFloat(), size.height / composition.bounds.height().toFloat(), Offset.Zero)
             }) {
diff --git a/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimationState.kt b/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimationState.kt
index b410285..6801bd0 100644
--- a/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimationState.kt
+++ b/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimationState.kt
@@ -1,10 +1,8 @@
 package com.airbnb.lottie.compose
 
-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.runtime.*
+import com.airbnb.lottie.ImageAssetDelegate
+import com.airbnb.lottie.LottieDrawable
 
 /**
  * Create a [LottieAnimationState] and remember it
@@ -13,13 +11,18 @@
  * @param repeatCount Initial value for [LottieAnimationState.repeatCount]
  * @param initialProgress Initial value for [LottieAnimationState.progress]
  * @param enableMergePaths Initial value for [LottieAnimationState.enableMergePaths]
+ * @param imageAssetsFolder Initial value for [LottieAnimationState.imageAssetsFolder]
+ * @param imageAssetDelegate Initial value for [LottieAnimationState.imageAssetDelegate]
  */
 @Composable
 fun rememberLottieAnimationState(
     autoPlay: Boolean = true,
     repeatCount: Int = 0,
     initialProgress: Float = 0f,
-    enableMergePaths: Boolean = true
+    enableMergePaths: Boolean = true,
+    imageAssetsFolder: String? = null,
+    imageAssetDelegate: ImageAssetDelegate? = null,
+
 ): LottieAnimationState {
     // Use rememberSavedInstanceState so you can pause/resume animations
     return remember(repeatCount, autoPlay) {
@@ -27,7 +30,9 @@
             isPlaying = autoPlay,
             repeatCount = repeatCount,
             initialProgress = initialProgress,
-            enableMergePaths = enableMergePaths
+            enableMergePaths = enableMergePaths,
+            imageAssetsFolder = imageAssetsFolder,
+            imageAssetDelegate = imageAssetDelegate,
         )
     }
 }
@@ -39,6 +44,8 @@
  * @param repeatCount Initial value for [repeatCount]
  * @param initialProgress Initial value for [progress]
  * @param enableMergePaths Initial value for [enableMergePaths]
+ * @param imageAssetsFolder Initial value for [LottieAnimationState.imageAssetsFolder]
+ * @param imageAssetDelegate Initial value for [LottieAnimationState.imageAssetDelegate]
  *
  * @see rememberLottieAnimationState
  */
@@ -46,7 +53,9 @@
     isPlaying: Boolean,
     repeatCount: Int = 0,
     initialProgress: Float = 0f,
-    enableMergePaths: Boolean = true
+    enableMergePaths: Boolean = true,
+    imageAssetsFolder: String? = null,
+    imageAssetDelegate: ImageAssetDelegate? = null,
 ) {
     var progress by mutableStateOf(initialProgress)
 
@@ -100,6 +109,35 @@
      */
     var enableMergePaths by mutableStateOf(enableMergePaths)
 
+    /**
+     * If you use image assets, you must explicitly specify the folder in assets/ in which they are
+     * located because bodymovin uses the name filenames across all compositions (img_#).
+     * Do NOT rename the images themselves.
+     * <p>
+     * If your images are located in src/main/assets/airbnb_loader/ then set this to "airbnb_loader".
+     * <p>
+     * <p>
+     * Be wary if you are using many images, however. Lottie is designed to work with vector shapes
+     * from After Effects. If your images look like they could be represented with vector shapes,
+     * see if it is possible to convert them to shape layers and re-export your animation. Check
+     * the documentation at https://airbnb.io/lottie for more information about importing shapes from
+     * Sketch or Illustrator to avoid this.
+     */
+    var imageAssetsFolder by mutableStateOf(imageAssetsFolder)
+
+    /**
+     * Use this if you can't bundle images with your app. This may be useful if you download the
+     * animations from the network or have the images saved to an SD Card. In that case, Lottie
+     * will defer the loading of the bitmap to this delegate.
+     * <p>
+     * Be wary if you are using many images, however. Lottie is designed to work with vector shapes
+     * from After Effects. If your images look like they could be represented with vector shapes,
+     * see if it is possible to convert them to shape layers and re-export your animation. Check
+     * the documentation at https://airbnb.io/lottie for more information about importing shapes from
+     * Sketch or Illustrator to avoid this.
+     */
+    var imageAssetDelegate by mutableStateOf(imageAssetDelegate)
+
     internal fun updateFrame(frame: Int) {
         _frame.value = frame
     }
@@ -107,4 +145,11 @@
     fun toggleIsPlaying() {
         isPlaying = !isPlaying
     }
+
+    internal fun applyTo(drawable: LottieDrawable) {
+        drawable.progress = progress
+        drawable.setOutlineMasksAndMattes(outlineMasksAndMattes)
+        drawable.isApplyingOpacityToLayersEnabled = applyOpacityToLayers
+        drawable.enableMergePathsForKitKatAndAbove(enableMergePaths)
+    }
 }
\ No newline at end of file
diff --git a/lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java b/lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java
index 8bfecd9..14ff5ac 100644
--- a/lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java
+++ b/lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java
@@ -23,6 +23,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
 
 import com.airbnb.lottie.manager.FontAssetManager;
 import com.airbnb.lottie.manager.ImageAssetManager;
@@ -75,6 +76,15 @@
       }
     }
   };
+
+  /**
+   * ImageAssetManager created externally. By Compose, for example.
+   */
+  @Nullable
+  private ImageAssetManager imageAssetManagerOverride;
+  /**
+   * ImageAssetManager created automatically by Lottie for views.
+   */
   @Nullable
   private ImageAssetManager imageAssetManager;
   @Nullable
@@ -1095,7 +1105,18 @@
     return null;
   }
 
+  /**
+   * Use by Lottie internally when outside of a normal View tree such as for Jetpack Compose.
+   */
+  @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+  void setImageAssetManager(@Nullable ImageAssetManager imageAssetManager) {
+    this.imageAssetManagerOverride = imageAssetManager;
+  }
+
   private ImageAssetManager getImageAssetManager() {
+    if (imageAssetManagerOverride != null) {
+      return imageAssetManagerOverride;
+    }
     if (getCallback() == null) {
       // We can't get a bitmap since we can't get a Context from the callback.
       return null;
diff --git a/lottie/src/main/java/com/airbnb/lottie/manager/ImageAssetManager.java b/lottie/src/main/java/com/airbnb/lottie/manager/ImageAssetManager.java
index 7182b70..833cf61 100644
--- a/lottie/src/main/java/com/airbnb/lottie/manager/ImageAssetManager.java
+++ b/lottie/src/main/java/com/airbnb/lottie/manager/ImageAssetManager.java
@@ -24,16 +24,16 @@
   private static final Object bitmapHashLock = new Object();
 
   private final Context context;
-  private String imagesFolder;
+  private final String imagesFolder;
   @Nullable private ImageAssetDelegate delegate;
   private final Map<String, LottieImageAsset> imageAssets;
 
   public ImageAssetManager(Drawable.Callback callback, String imagesFolder,
       ImageAssetDelegate delegate, Map<String, LottieImageAsset> imageAssets) {
-    this.imagesFolder = imagesFolder;
-    if (!TextUtils.isEmpty(imagesFolder) &&
-        this.imagesFolder.charAt(this.imagesFolder.length() - 1) != '/') {
-      this.imagesFolder += '/';
+    if (!TextUtils.isEmpty(imagesFolder) && imagesFolder.charAt(imagesFolder.length() - 1) != '/') {
+      this.imagesFolder = imagesFolder + '/';
+    } else {
+      this.imagesFolder = imagesFolder;
     }
 
     if (!(callback instanceof View)) {
@@ -48,6 +48,17 @@
     setDelegate(delegate);
   }
 
+  public ImageAssetManager(Context context, String imagesFolder, ImageAssetDelegate delegate, Map<String, LottieImageAsset> imageAssets) {
+    this.context = context;
+    if (!TextUtils.isEmpty(imagesFolder) && imagesFolder.charAt(imagesFolder.length() - 1) != '/') {
+      this.imagesFolder = imagesFolder + '/';
+    } else {
+      this.imagesFolder = imagesFolder;
+    }
+    this.imageAssets = imageAssets;
+    setDelegate(delegate);
+  }
+
   public void setDelegate(@Nullable ImageAssetDelegate assetDelegate) {
     this.delegate = assetDelegate;
   }
diff --git a/sample-compose/build.gradle b/sample-compose/build.gradle
index 6c57c64..8b6535d 100644
--- a/sample-compose/build.gradle
+++ b/sample-compose/build.gradle
@@ -56,20 +56,20 @@
   implementation 'androidx.core:core-ktx:1.3.2'
   implementation 'androidx.multidex:multidex:2.0.1'
   implementation 'androidx.activity:activity-ktx:1.2.1'
-  implementation 'androidx.activity:activity-compose:1.3.0-alpha03'
+  implementation 'androidx.activity:activity-compose:1.3.0-alpha04'
   implementation 'androidx.appcompat:appcompat:1.3.0-beta01'
   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-alpha08"
+  implementation "androidx.navigation:navigation-compose:1.0.0-alpha09"
   implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.0'
   implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0'
 
   implementation "androidx.navigation:navigation-ui-ktx:2.3.4"
 
-  implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2'
+  implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3'
   implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.2'
 
   implementation "com.google.dagger:dagger:$daggerVersion"
@@ -77,7 +77,7 @@
 
   implementation 'com.squareup.retrofit2:retrofit:2.9.0'
   implementation 'com.squareup.retrofit2:converter-moshi:2.9.0'
-  implementation "dev.chrisbanes.accompanist:accompanist-coil:0.6.0"
+  implementation "dev.chrisbanes.accompanist:accompanist-coil:0.6.1"
   implementation 'com.airbnb.android:mavericks:2.1.0'
   implementation 'com.airbnb.android:mavericks-compose:2.1.0-alpha01'
 
diff --git a/settings.gradle b/settings.gradle
index 428b963..a895a44 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -3,3 +3,4 @@
 include ':sample'
 include ':sample-compose'
 include ':issue-repro'
+include ':issue-repro-compose'