Include day/night mode in cache keys
diff --git a/LottieSample/src/androidTest/java/com/airbnb/lottie/samples/LottieTest.kt b/LottieSample/src/androidTest/java/com/airbnb/lottie/samples/LottieTest.kt
index fe1baef..2360939 100644
--- a/LottieSample/src/androidTest/java/com/airbnb/lottie/samples/LottieTest.kt
+++ b/LottieSample/src/androidTest/java/com/airbnb/lottie/samples/LottieTest.kt
@@ -1,8 +1,10 @@
 package com.airbnb.lottie.samples
 
 import android.Manifest
+import android.content.res.Configuration
 import android.content.res.Resources
 import android.graphics.*
+import android.os.Build.VERSION
 import android.util.DisplayMetrics
 import android.util.Log
 import android.view.View
@@ -34,7 +36,6 @@
 import java.io.FileInputStream
 import java.util.concurrent.TimeUnit
 import java.util.zip.ZipInputStream
-import com.airbnb.lottie.samples.R as SampleAppR
 
 /**
  * Run these with: ./gradlew recordMode screenshotTests
@@ -106,6 +107,7 @@
             testText()
             testPartialFrameProgress()
             snapshotProdAnimations()
+            testNightMode()
             snapshotter.finalizeReportAndUpload()
         }
     }
@@ -764,6 +766,44 @@
         }
     }
 
+    private suspend fun testNightMode() {
+        if (VERSION.SDK_INT < 29 /* Build.VERSION_CODES.Q */) return
+
+        var newConfig = Configuration(activity.getResources().getConfiguration())
+		newConfig.uiMode = newConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK.inv();
+		newConfig.uiMode = newConfig.uiMode or Configuration.UI_MODE_NIGHT_NO;
+		val dayContext = activity.createConfigurationContext(newConfig)
+        var result = LottieCompositionFactory.fromRawResSync(dayContext, R.raw.day_night)
+        var composition = result.value!!
+        var drawable = LottieDrawable()
+        drawable.setComposition(composition)
+        var bitmap = bitmapPool.acquire(drawable.intrinsicWidth, drawable.intrinsicHeight)
+        var canvas = Canvas(bitmap)
+        log("Drawing day_night day")
+        drawable.draw(canvas)
+        snapshotter.record(bitmap, "Day/Night", "Day")
+        activity.recordSnapshot("Day/Night", "Day")
+        LottieCompositionCache.getInstance().clear()
+        bitmapPool.release(bitmap)
+
+        newConfig = Configuration(activity.getResources().getConfiguration())
+        newConfig.uiMode = newConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK.inv();
+        newConfig.uiMode = newConfig.uiMode or Configuration.UI_MODE_NIGHT_YES;
+        val nightContext = activity.createConfigurationContext(newConfig)
+        result = LottieCompositionFactory.fromRawResSync(nightContext, R.raw.day_night)
+        composition = result.value!!
+        drawable = LottieDrawable()
+        drawable.setComposition(composition)
+        bitmap = bitmapPool.acquire(drawable.intrinsicWidth, drawable.intrinsicHeight)
+        canvas = Canvas(bitmap)
+        log("Drawing day_night day")
+        drawable.draw(canvas)
+        snapshotter.record(bitmap, "Day/Night", "Night")
+        activity.recordSnapshot("Day/Night", "Night")
+        LottieCompositionCache.getInstance().clear()
+        bitmapPool.release(bitmap)
+    }
+
     private suspend fun withDrawable(assetName: String, snapshotName: String, snapshotVariant: String, callback: (LottieDrawable) -> Unit) {
         val result = LottieCompositionFactory.fromAssetSync(activity, assetName)
         val composition = result.value
diff --git a/LottieSample/src/androidTest/java/com/airbnb/lottie/samples/SuspendingSemaphore.kt b/LottieSample/src/androidTest/java/com/airbnb/lottie/samples/SuspendingSemaphore.kt
index 7c810c2..7fe571b 100644
--- a/LottieSample/src/androidTest/java/com/airbnb/lottie/samples/SuspendingSemaphore.kt
+++ b/LottieSample/src/androidTest/java/com/airbnb/lottie/samples/SuspendingSemaphore.kt
@@ -29,5 +29,6 @@
         }
     }
 
+    @Suppress("EXPERIMENTAL_API_USAGE")
     fun isFull() = bufferedChannel.isFull
 }
\ No newline at end of file
diff --git a/LottieSample/src/main/res/raw-night/day_night.json b/LottieSample/src/main/res/raw-night/day_night.json
new file mode 100644
index 0000000..03f88b4
--- /dev/null
+++ b/LottieSample/src/main/res/raw-night/day_night.json
@@ -0,0 +1 @@
+{"v":"5.4.4","fr":30.0000305175781,"ip":0,"op":300.000305175781,"w":300,"h":300,"nm":"Night","ddd":0,"assets":[],"fonts":{"list":[{"fName":"Helvetica","fFamily":"Helvetica","fStyle":"Regular","ascent":71.8994140625}]},"layers":[{"ddd":0,"ind":1,"ty":5,"nm":"Night","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150,191,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"t":{"d":{"k":[{"s":{"s":120,"f":"Helvetica","t":"Night","j":2,"tr":0,"lh":144,"ls":0,"fc":[0,0,0]},"t":0}]},"p":{},"m":{"g":1,"a":{"a":0,"k":[0,0],"ix":2}},"a":[]},"ip":0,"op":300.000305175781,"st":0,"bm":0}],"markers":[],"chars":[{"ch":"N","size":120,"style":"Regular","w":72.22,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[7.617,0],[16.895,0],[16.895,-57.885],[53.195,0],[64.111,0],[64.111,-71.729],[54.834,-71.729],[54.834,-13.774],[19.064,-71.729],[7.617,-71.729]],"c":true},"ix":2},"nm":"N","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"N","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"i","size":120,"style":"Regular","w":22.22,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[6.445,0],[15.381,0],[15.381,-52.051],[6.445,-52.051]],"c":true},"ix":2},"nm":"i","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[6.445,-61.768],[15.381,-61.768],[15.381,-71.729],[6.445,-71.729]],"c":true},"ix":2},"nm":"i","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"i","np":5,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"g","size":120,"style":"Regular","w":55.62,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[4.086,0],[4.264,-4.915],[0,-8.756],[-4.231,-4.02],[-5.642,0],[-2.464,1.302],[-1.751,2.67],[1.231,-2.8],[6.702,0],[1.974,1.953],[0.42,2.344],[0,0],[-3.828,-2.523],[-5.613,0],[-3.634,7.096],[0,6.641],[0,0],[0,0],[0,0],[1.653,1.14]],"o":[[-6.031,0],[-4.264,4.916],[0,9.277],[4.231,4.021],[4.053,0],[2.464,-1.302],[0.098,7.195],[-2.072,4.752],[-4.241,0],[-1.263,-1.27],[0,0],[0.455,5.371],[3.828,2.522],[10.089,0],[1.946,-3.841],[0,0],[0,0],[0,0],[-1.719,-2.18],[-3.049,-2.018]],"v":[[24.817,-53.223],[9.374,-45.85],[2.979,-25.342],[9.325,-5.396],[24.136,0.635],[33.912,-1.318],[40.234,-7.275],[38.535,7.715],[25.375,14.844],[16.051,11.914],[13.525,6.494],[4.59,6.494],[11.015,18.335],[25.177,22.119],[45.762,11.475],[48.682,-4.248],[48.682,-51.807],[40.576,-51.807],[40.576,-45.215],[35.518,-50.195]],"c":true},"ix":2},"nm":"g","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,-6.477],[1.425,-3.223],[6.282,0],[2.38,3.076],[0,5.892],[-1.199,3.093],[-6.154,0],[-2.656,-3.125]],"o":[[0,4.297],[-2.559,5.697],[-4.016,0],[-2.38,-3.076],[0,-5.013],[2.267,-5.794],[4.113,0],[2.655,3.125]],"v":[[40.576,-26.367],[38.438,-15.088],[25.177,-6.543],[15.582,-11.157],[12.012,-24.609],[13.809,-36.768],[26.44,-45.459],[36.593,-40.771]],"c":true},"ix":2},"nm":"g","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"g","np":5,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"h","size":120,"style":"Regular","w":55.62,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[-2.962,2.507],[-3.646,0],[-1.595,-2.864],[0,-3.841],[0,0],[0,0],[0,0],[1.465,2.898],[7.584,0],[2.832,-1.855],[2.083,-2.637],[0,0],[0,0]],"o":[[0,0],[0,0],[0,-6.966],[2.962,-2.506],[4.395,0],[0.977,1.791],[0,0],[0,0],[0,0],[0,-5.143],[-2.702,-5.305],[-4.232,0],[-1.66,1.074],[0,0],[0,0],[0,0]],"v":[[6.445,0],[15.234,0],[15.234,-27.734],[19.678,-41.943],[29.59,-45.703],[38.574,-41.406],[40.039,-32.959],[40.039,0],[49.072,0],[49.072,-33.545],[46.875,-45.605],[31.445,-53.564],[20.85,-50.781],[15.234,-45.215],[15.234,-71.973],[6.445,-71.973]],"c":true},"ix":2},"nm":"h","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"h","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"t","size":120,"style":"Regular","w":27.78,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[-1.465,-2.1],[-4.102,0],[-1.091,0.13],[-1.009,0.293],[0,0],[0.488,-0.021],[0.423,0],[0.684,0.359],[0,1.823],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,3.353],[1.465,2.1],[1.27,0],[1.09,-0.13],[0,0],[-0.652,0.087],[-0.488,0.021],[-1.595,0],[-1.237,-0.618],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[8.203,-52.295],[1.123,-52.295],[1.123,-45.117],[8.203,-45.117],[8.203,-10.498],[10.4,-2.319],[18.75,0.83],[22.29,0.635],[25.439,0],[25.439,-6.982],[23.73,-6.819],[22.363,-6.787],[18.945,-7.324],[17.09,-10.986],[17.09,-45.117],[25.439,-45.117],[25.439,-52.295],[17.09,-52.295],[17.09,-66.895],[8.203,-66.895]],"c":true},"ix":2},"nm":"t","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"t","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"}]}
\ No newline at end of file
diff --git a/LottieSample/src/main/res/raw/day_night.json b/LottieSample/src/main/res/raw/day_night.json
new file mode 100644
index 0000000..2e41d1d
--- /dev/null
+++ b/LottieSample/src/main/res/raw/day_night.json
@@ -0,0 +1 @@
+{"v":"5.4.4","fr":30.0000305175781,"ip":0,"op":300.000305175781,"w":300,"h":300,"nm":"Day","ddd":0,"assets":[],"fonts":{"list":[{"fName":"Helvetica","fFamily":"Helvetica","fStyle":"Regular","ascent":71.8994140625}]},"layers":[{"ddd":0,"ind":1,"ty":5,"nm":"DAY","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150,191,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"t":{"d":{"k":[{"s":{"s":120,"f":"Helvetica","t":"DAY","j":2,"tr":0,"lh":144,"ls":0,"fc":[0,0,0]},"t":0}]},"p":{},"m":{"g":1,"a":{"a":0,"k":[0,0],"ix":2}},"a":[]},"ip":0,"op":300.000305175781,"st":0,"bm":0}],"markers":[],"chars":[{"ch":"D","size":120,"style":"Regular","w":72.22,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[-3.454,-4.915],[0,-8.887],[0.478,-2.702],[1.878,-2.897],[3.692,-1.27],[3.214,0]],"o":[[0,0],[0,0],[7.671,0],[3.453,4.916],[0,2.312],[-0.828,4.525],[-2.356,3.613],[-2.069,0.684],[0,0]],"v":[[17.871,-8.301],[17.871,-63.379],[34.725,-63.379],[51.411,-56.006],[56.592,-35.303],[55.875,-27.783],[51.817,-16.65],[42.746,-9.326],[34.821,-8.301]],"c":true},"ix":2},"nm":"D","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-4.979,10.873],[0,7.617],[4.786,6.316],[9.734,0],[0,0]],"o":[[0,0],[12.142,0],[2.827,-6.152],[0,-9.863],[-5.365,-6.998],[0,0],[0,0]],"v":[[8.057,0],[36.63,0],[62.312,-16.309],[66.553,-36.963],[59.373,-61.23],[36.726,-71.729],[8.057,-71.729]],"c":true},"ix":2},"nm":"D","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"D","np":5,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"A","size":120,"style":"Regular","w":66.7,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[22.143,-29.395],[33.618,-61.084],[44.662,-29.395]],"c":true},"ix":2},"nm":"A","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[1.538,0],[11.499,0],[19.278,-21.484],[47.419,-21.484],[54.907,0],[65.552,0],[39.518,-71.729],[28.549,-71.729]],"c":true},"ix":2},"nm":"A","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"A","np":5,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"Y","size":120,"style":"Regular","w":66.7,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[29.199,-28.906],[29.199,0],[38.916,0],[38.916,-28.906],[66.082,-71.729],[54.653,-71.729],[34.033,-37.256],[13.413,-71.729],[2.083,-71.729]],"c":true},"ix":2},"nm":"Y","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"Y","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"}]}
\ No newline at end of file
diff --git a/lottie/src/main/java/com/airbnb/lottie/LottieCompositionFactory.java b/lottie/src/main/java/com/airbnb/lottie/LottieCompositionFactory.java
index 97971ad..d707c69 100644
--- a/lottie/src/main/java/com/airbnb/lottie/LottieCompositionFactory.java
+++ b/lottie/src/main/java/com/airbnb/lottie/LottieCompositionFactory.java
@@ -1,9 +1,11 @@
 package com.airbnb.lottie;
 
 import android.content.Context;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.os.Build;
 
 import com.airbnb.lottie.model.LottieCompositionCache;
 import com.airbnb.lottie.network.NetworkFetcher;
@@ -130,7 +132,7 @@
   public static LottieTask<LottieComposition> fromRawRes(Context context, @RawRes final int rawRes) {
     // Prevent accidentally leaking an Activity.
     final Context appContext = context.getApplicationContext();
-    return cache(rawResCacheKey(rawRes), new Callable<LottieResult<LottieComposition>>() {
+    return cache(rawResCacheKey(context, rawRes), new Callable<LottieResult<LottieComposition>>() {
       @Override
       public LottieResult<LottieComposition> call() {
         return fromRawResSync(appContext, rawRes);
@@ -146,14 +148,22 @@
   @WorkerThread
   public static LottieResult<LottieComposition> fromRawResSync(Context context, @RawRes int rawRes) {
     try {
-      return fromJsonInputStreamSync(context.getResources().openRawResource(rawRes), rawResCacheKey(rawRes));
+      return fromJsonInputStreamSync(context.getResources().openRawResource(rawRes), rawResCacheKey(context, rawRes));
     } catch (Resources.NotFoundException e) {
       return new LottieResult<>(e);
     }
   }
 
-  private static String rawResCacheKey(@RawRes int resId) {
-    return "rawRes_" + resId;
+  private static String rawResCacheKey(Context context, @RawRes int resId) {
+    return "rawRes" + (isNightMode(context) ? "_night_" : "_day_") + resId;
+  }
+
+  /**
+   * It is important to include day/night in the cache key so that if it changes, the cache won't return an animation from the wrong bucket.
+   */
+  private static boolean isNightMode(Context context) {
+    int nightModeMasked = context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
+    return nightModeMasked == Configuration.UI_MODE_NIGHT_YES;
   }
 
   /**