From ab5a63b169fef58b543f83c1512d5641a6870557 Mon Sep 17 00:00:00 2001 From: Drew Hamilton Date: Fri, 23 Jun 2023 15:08:44 -0700 Subject: [PATCH 1/5] Reset internal networkFetcher and networkCache when L.setFetcher and L.setCacheProvider are called, respectively --- lottie/src/main/java/com/airbnb/lottie/L.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lottie/src/main/java/com/airbnb/lottie/L.java b/lottie/src/main/java/com/airbnb/lottie/L.java index a27d7da7c8..c5d5d1ea00 100644 --- a/lottie/src/main/java/com/airbnb/lottie/L.java +++ b/lottie/src/main/java/com/airbnb/lottie/L.java @@ -75,10 +75,16 @@ private static LottieTrace getTrace() { public static void setFetcher(LottieNetworkFetcher customFetcher) { fetcher = customFetcher; + synchronized (NetworkCache.class) { + networkFetcher = null; + } } public static void setCacheProvider(LottieNetworkCacheProvider customProvider) { cacheProvider = customProvider; + synchronized (NetworkFetcher.class) { + networkCache = null; + } } @NonNull From 5366c067d92965ecc37318daa82356256648c0c5 Mon Sep 17 00:00:00 2001 From: Drew Hamilton Date: Fri, 23 Jun 2023 15:09:07 -0700 Subject: [PATCH 2/5] Remove duplicate setNetworkCacheEnabled call in Lottie.initialize --- lottie/src/main/java/com/airbnb/lottie/Lottie.java | 1 - 1 file changed, 1 deletion(-) diff --git a/lottie/src/main/java/com/airbnb/lottie/Lottie.java b/lottie/src/main/java/com/airbnb/lottie/Lottie.java index c585572ac1..816fff71bf 100644 --- a/lottie/src/main/java/com/airbnb/lottie/Lottie.java +++ b/lottie/src/main/java/com/airbnb/lottie/Lottie.java @@ -20,7 +20,6 @@ public static void initialize(@NonNull final LottieConfig lottieConfig) { L.setCacheProvider(lottieConfig.cacheProvider); L.setTraceEnabled(lottieConfig.enableSystraceMarkers); L.setNetworkCacheEnabled(lottieConfig.enableNetworkCache); - L.setNetworkCacheEnabled(lottieConfig.enableNetworkCache); L.setDisablePathInterpolatorCache(lottieConfig.disablePathInterpolatorCache); } } From 5c293aa834f567e90092a935d3d282bc56f1fb3a Mon Sep 17 00:00:00 2001 From: Drew Hamilton Date: Fri, 23 Jun 2023 15:41:32 -0700 Subject: [PATCH 3/5] Add LottieInitializeTest --- .../airbnb/lottie/LottieInitializeTest.java | 128 ++++++++++++++++++ lottie/src/test/resources/test1.json | 116 ++++++++++++++++ 2 files changed, 244 insertions(+) create mode 100644 lottie/src/test/java/com/airbnb/lottie/LottieInitializeTest.java create mode 100644 lottie/src/test/resources/test1.json diff --git a/lottie/src/test/java/com/airbnb/lottie/LottieInitializeTest.java b/lottie/src/test/java/com/airbnb/lottie/LottieInitializeTest.java new file mode 100644 index 0000000000..688dd73c00 --- /dev/null +++ b/lottie/src/test/java/com/airbnb/lottie/LottieInitializeTest.java @@ -0,0 +1,128 @@ +package com.airbnb.lottie; + +import static org.junit.Assert.assertNotNull; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.test.platform.app.InstrumentationRegistry; + +import com.airbnb.lottie.network.LottieFetchResult; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.InputStream; +import java.util.Objects; + +public class LottieInitializeTest extends BaseTest { + + @Rule + public final TemporaryFolder temporaryFolder1 = new TemporaryFolder(); + + @Rule + public final TemporaryFolder temporaryFolder2 = new TemporaryFolder(); + + private final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); + + @Before + public void setExecutor() { + LottieTask.EXECUTOR = Runnable::run; + } + + @Test + public void fetchAfterSecondInitialize() { + initializeLottie(temporaryFolder1); + // Fetching here causes the resource to be cached in temporaryFolder1: + LottieResult result1 = LottieCompositionFactory.fromUrlSync(context, "resources://test1.json"); + assertNotNull(result1.getValue()); + + // Manually delete to simulate the end of a test: + temporaryFolder1.delete(); + + initializeLottie(temporaryFolder2); + // Fetching here fails if L.setCacheProvider doesn't reset both its internal networkFetcher and its internal networkCache, because + // temporaryFolder1 has been deleted: + LottieResult result2 = LottieCompositionFactory.fromUrlSync(context, "resources://test1.json"); + assertNotNull(result2.getValue()); + } + + private void initializeLottie(TemporaryFolder temporaryFolder) { + LottieConfig lottieConfig = new LottieConfig.Builder() + .setNetworkCacheDir(temporaryFolder.getRoot()) + .setNetworkFetcher(url -> { + if (url.startsWith("resources://")) { + InputStream stream = Objects.requireNonNull(getClass().getClassLoader()) + .getResourceAsStream(url.substring("resources://".length())); + if (stream != null) { + return new LottieFetchSuccess(stream); + } + } + + return new LottieFetchFailure("Could not load <$url>"); + }) + .build(); + Lottie.initialize(lottieConfig); + } + + private static class LottieFetchSuccess implements LottieFetchResult { + + @NonNull private final InputStream jsonStream; + + LottieFetchSuccess(@NonNull InputStream jsonStream) { + this.jsonStream = jsonStream; + } + + @Override public boolean isSuccessful() { + return true; + } + + @Override @NonNull public InputStream bodyByteStream() { + return jsonStream; + } + + @Override public String contentType() { + return "application/json"; + } + + @Override @Nullable public String error() { + return null; + } + + @Override public void close() { + // No-op + } + } + + private static class LottieFetchFailure implements LottieFetchResult { + + @NonNull private final String errorMessage; + + LottieFetchFailure(@NonNull String errorMessage) { + this.errorMessage = errorMessage; + } + + @Override public boolean isSuccessful() { + return false; + } + + @Override @NonNull public InputStream bodyByteStream() { + throw new RuntimeException("LottieFetchFailure has no body"); + } + + @Override @Nullable public String contentType() { + return null; + } + + @Override public String error() { + return errorMessage; + } + + @Override public void close() { + // No-op + } + } +} diff --git a/lottie/src/test/resources/test1.json b/lottie/src/test/resources/test1.json new file mode 100644 index 0000000000..95394ce8fb --- /dev/null +++ b/lottie/src/test/resources/test1.json @@ -0,0 +1,116 @@ +{ + "v": "4.11.1", + "fr": 60, + "ip": 0, + "op": 180, + "w": 300, + "h": 300, + "nm": "Comp 1", + "ddd": 0, + "assets": [], + "layers": [ + { + "ddd": 0, + "ind": 1, + "ty": 4, + "nm": "Shape Layer 1", + "sr": 1, + "ks": { + "o": { + "a": 0, + "k": 100, + "ix": 11 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "p": { + "a": 0, + "k": [ + 150, + 150, + 0 + ], + "ix": 2 + }, + "a": { + "a": 0, + "k": [ + 0, + 0, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100, + 100 + ], + "ix": 6 + } + }, + "ao": 0, + "shapes": [ + { + "ty": "rc", + "d": 1, + "s": { + "a": 0, + "k": [ + 100, + 100 + ], + "ix": 2 + }, + "p": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 3 + }, + "r": { + "a": 0, + "k": 0, + "ix": 4 + }, + "nm": "Rectangle Path 1", + "mn": "ADBE Vector Shape - Rect", + "hd": false + }, + { + "ty": "fl", + "c": { + "a": 0, + "k": [ + 0.928262987324, + 0, + 0, + 1 + ], + "ix": 4 + }, + "o": { + "a": 0, + "k": 100, + "ix": 5 + }, + "r": 1, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill", + "hd": false + } + ], + "ip": 0, + "op": 180, + "st": 0, + "bm": 0 + } + ] +} From 0ed9bff34415165d2d3cdafb3a1258c2b93cad56 Mon Sep 17 00:00:00 2001 From: Drew Hamilton Date: Mon, 26 Jun 2023 11:04:03 -0700 Subject: [PATCH 4/5] Make L.setFetcher and L.setCacheProvider idempotent --- lottie/src/main/java/com/airbnb/lottie/L.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lottie/src/main/java/com/airbnb/lottie/L.java b/lottie/src/main/java/com/airbnb/lottie/L.java index c5d5d1ea00..6116f694da 100644 --- a/lottie/src/main/java/com/airbnb/lottie/L.java +++ b/lottie/src/main/java/com/airbnb/lottie/L.java @@ -74,15 +74,17 @@ private static LottieTrace getTrace() { } public static void setFetcher(LottieNetworkFetcher customFetcher) { + boolean changing = !fetcher.equals(customFetcher); fetcher = customFetcher; - synchronized (NetworkCache.class) { + if (changing) { networkFetcher = null; } } public static void setCacheProvider(LottieNetworkCacheProvider customProvider) { + boolean changing = !cacheProvider.equals(customProvider); cacheProvider = customProvider; - synchronized (NetworkFetcher.class) { + if (changing) { networkCache = null; } } From 499afd0eaed6cfcd6be419c9121ade8220f51f99 Mon Sep 17 00:00:00 2001 From: Drew Hamilton Date: Tue, 11 Jul 2023 17:03:13 -0700 Subject: [PATCH 5/5] Allow null fetcher and cache provider in L.java --- lottie/src/main/java/com/airbnb/lottie/L.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/lottie/src/main/java/com/airbnb/lottie/L.java b/lottie/src/main/java/com/airbnb/lottie/L.java index 6116f694da..ae9f92c57c 100644 --- a/lottie/src/main/java/com/airbnb/lottie/L.java +++ b/lottie/src/main/java/com/airbnb/lottie/L.java @@ -74,19 +74,21 @@ private static LottieTrace getTrace() { } public static void setFetcher(LottieNetworkFetcher customFetcher) { - boolean changing = !fetcher.equals(customFetcher); - fetcher = customFetcher; - if (changing) { - networkFetcher = null; + if ((fetcher == null && customFetcher == null) || (fetcher != null && fetcher.equals(customFetcher))) { + return; } + + fetcher = customFetcher; + networkFetcher = null; } public static void setCacheProvider(LottieNetworkCacheProvider customProvider) { - boolean changing = !cacheProvider.equals(customProvider); - cacheProvider = customProvider; - if (changing) { - networkCache = null; + if ((cacheProvider == null && customProvider == null) || (cacheProvider != null && cacheProvider.equals(customProvider))) { + return; } + + cacheProvider = customProvider; + networkCache = null; } @NonNull