From c4cb2254eca3c70199f1de5e39e3872c8c42e473 Mon Sep 17 00:00:00 2001 From: Jorge Betancourt Date: Wed, 3 Jul 2024 16:36:48 -0400 Subject: [PATCH] Add generalized Lottie Feature Flags API (#2512) Currently there are individual methods for enabling and disabling Merge Paths in Lottie. This PR aims to generalize these functions as we consider adding more features guarded behind opt-in flags. --- .../airbnb/lottie/compose/LottieAnimation.kt | 3 +- .../airbnb/lottie/compose/LottiePainter.kt | 3 +- .../airbnb/lottie/LottieAnimationView.java | 22 +++++++++- .../com/airbnb/lottie/LottieComposition.java | 1 - .../com/airbnb/lottie/LottieDrawable.java | 41 +++++++++++++------ .../com/airbnb/lottie/LottieFeatureFlag.java | 18 ++++++++ .../com/airbnb/lottie/LottieFeatureFlags.java | 34 +++++++++++++++ .../lottie/model/content/MergePaths.java | 3 +- 8 files changed, 107 insertions(+), 18 deletions(-) create mode 100644 lottie/src/main/java/com/airbnb/lottie/LottieFeatureFlag.java create mode 100644 lottie/src/main/java/com/airbnb/lottie/LottieFeatureFlags.java 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 3f8de0febe..4fd1b8284c 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 @@ -21,6 +21,7 @@ import androidx.compose.ui.unit.IntSize import com.airbnb.lottie.AsyncUpdates import com.airbnb.lottie.LottieComposition import com.airbnb.lottie.LottieDrawable +import com.airbnb.lottie.LottieFeatureFlag import com.airbnb.lottie.RenderMode import kotlin.math.roundToInt @@ -114,7 +115,7 @@ fun LottieAnimation( matrix.preTranslate(translation.x.toFloat(), translation.y.toFloat()) matrix.preScale(scale.scaleX, scale.scaleY) - drawable.enableMergePathsForKitKatAndAbove(enableMergePaths) + drawable.enableFeatureFlag(LottieFeatureFlag.MergePathsApi19, enableMergePaths) drawable.setSafeMode(safeMode) drawable.renderMode = renderMode drawable.asyncUpdates = asyncUpdates diff --git a/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottiePainter.kt b/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottiePainter.kt index e0e3e1187e..2f1ac24fdc 100644 --- a/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottiePainter.kt +++ b/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottiePainter.kt @@ -18,6 +18,7 @@ import androidx.compose.ui.unit.IntSize import com.airbnb.lottie.AsyncUpdates import com.airbnb.lottie.LottieComposition import com.airbnb.lottie.LottieDrawable +import com.airbnb.lottie.LottieFeatureFlag import com.airbnb.lottie.RenderMode import kotlin.math.roundToInt @@ -104,7 +105,7 @@ class LottiePainter internal constructor( matrix.reset() matrix.preScale(intSize.width / compositionSize.width, intSize.height / compositionSize.height) - drawable.enableMergePathsForKitKatAndAbove(enableMergePaths) + drawable.enableFeatureFlag(LottieFeatureFlag.MergePathsApi19, enableMergePaths) drawable.renderMode = renderMode drawable.asyncUpdates = asyncUpdates drawable.composition = composition diff --git a/lottie/src/main/java/com/airbnb/lottie/LottieAnimationView.java b/lottie/src/main/java/com/airbnb/lottie/LottieAnimationView.java index 273ba7030c..05b1518f65 100644 --- a/lottie/src/main/java/com/airbnb/lottie/LottieAnimationView.java +++ b/lottie/src/main/java/com/airbnb/lottie/LottieAnimationView.java @@ -403,14 +403,32 @@ public void setUseCompositionFrameRate(boolean useCompositionFrameRate) { * instead of using merge paths. */ public void enableMergePathsForKitKatAndAbove(boolean enable) { - lottieDrawable.enableMergePathsForKitKatAndAbove(enable); + lottieDrawable.enableFeatureFlag(LottieFeatureFlag.MergePathsApi19, enable); } /** * Returns whether merge paths are enabled for KitKat and above. */ public boolean isMergePathsEnabledForKitKatAndAbove() { - return lottieDrawable.isMergePathsEnabledForKitKatAndAbove(); + return lottieDrawable.isFeatureFlagEnabled(LottieFeatureFlag.MergePathsApi19); + } + + /** + * Enable the specified feature for this LottieView. + *

+ * Features guarded by LottieFeatureFlags are experimental or only supported by a subset of API levels. + * Please ensure that the animation supported by the enabled feature looks acceptable across all + * targeted API levels. + */ + public void enableFeatureFlag(LottieFeatureFlag flag, boolean enable) { + lottieDrawable.enableFeatureFlag(flag, enable); + } + + /** + * Returns whether the specified feature is enabled. + */ + public boolean isFeatureFlagEnabled(LottieFeatureFlag flag) { + return lottieDrawable.isFeatureFlagEnabled(flag); } /** diff --git a/lottie/src/main/java/com/airbnb/lottie/LottieComposition.java b/lottie/src/main/java/com/airbnb/lottie/LottieComposition.java index a03e392101..c91bbf6732 100644 --- a/lottie/src/main/java/com/airbnb/lottie/LottieComposition.java +++ b/lottie/src/main/java/com/airbnb/lottie/LottieComposition.java @@ -26,7 +26,6 @@ import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Map; diff --git a/lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java b/lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java index 89ec1a8165..dfcae27c7b 100644 --- a/lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java +++ b/lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java @@ -142,7 +142,7 @@ private enum OnVisibleAction { FontAssetDelegate fontAssetDelegate; @Nullable TextDelegate textDelegate; - private boolean enableMergePaths; + private final LottieFeatureFlags lottieFeatureFlags = new LottieFeatureFlags(); private boolean maintainOriginalImageBounds = false; private boolean clipToCompositionBounds = true; @Nullable @@ -285,34 +285,51 @@ public boolean hasMatte() { return compositionLayer != null && compositionLayer.hasMatte(); } + @Deprecated public boolean enableMergePathsForKitKatAndAbove() { - return enableMergePaths; + return lottieFeatureFlags.isFlagEnabled(LottieFeatureFlag.MergePathsApi19); } /** * Enable this to get merge path support for devices running KitKat (19) and above. + * Deprecated: Use enableFeatureFlag(LottieFeatureFlags.FeatureFlag.MergePathsApi19, enable) *

* Merge paths currently don't work if the the operand shape is entirely contained within the * first shape. If you need to cut out one shape from another shape, use an even-odd fill type * instead of using merge paths. */ + @Deprecated public void enableMergePathsForKitKatAndAbove(boolean enable) { - if (enableMergePaths == enable) { - return; + boolean changed = lottieFeatureFlags.enableFlag(LottieFeatureFlag.MergePathsApi19, enable); + if (composition != null && changed) { + buildCompositionLayer(); } + } - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { - Logger.warning("Merge paths are not supported pre-Kit Kat."); - return; - } - enableMergePaths = enable; - if (composition != null) { + /** + * @deprecated Replaced by {@link #enableFeatureFlag(LottieFeatureFlag, boolean)} + */ + @Deprecated + public boolean isMergePathsEnabledForKitKatAndAbove() { + return lottieFeatureFlags.isFlagEnabled(LottieFeatureFlag.MergePathsApi19); + } + + /** + * Enable the specified feature for this drawable. + *

+ * Features guarded by LottieFeatureFlags are experimental or only supported by a subset of API levels. + * Please ensure that the animation supported by the enabled feature looks acceptable across all + * targeted API levels. + */ + public void enableFeatureFlag(LottieFeatureFlag flag, boolean enable) { + boolean changed = lottieFeatureFlags.enableFlag(flag, enable); + if (composition != null && changed) { buildCompositionLayer(); } } - public boolean isMergePathsEnabledForKitKatAndAbove() { - return enableMergePaths; + public boolean isFeatureFlagEnabled(LottieFeatureFlag flag) { + return lottieFeatureFlags.isFlagEnabled(flag); } /** diff --git a/lottie/src/main/java/com/airbnb/lottie/LottieFeatureFlag.java b/lottie/src/main/java/com/airbnb/lottie/LottieFeatureFlag.java new file mode 100644 index 0000000000..228e199882 --- /dev/null +++ b/lottie/src/main/java/com/airbnb/lottie/LottieFeatureFlag.java @@ -0,0 +1,18 @@ +package com.airbnb.lottie; + +import android.os.Build; + +public enum LottieFeatureFlag { + /** + * Merge paths currently don't work if the the operand shape is entirely contained within the + * first shape. If you need to cut out one shape from another shape, use an even-odd fill type + * instead of using merge paths. + */ + MergePathsApi19(Build.VERSION_CODES.KITKAT); + + public final int minRequiredSdkVersion; + + LottieFeatureFlag(int minRequiredSdkVersion) { + this.minRequiredSdkVersion = minRequiredSdkVersion; + } +} diff --git a/lottie/src/main/java/com/airbnb/lottie/LottieFeatureFlags.java b/lottie/src/main/java/com/airbnb/lottie/LottieFeatureFlags.java new file mode 100644 index 0000000000..7e7560a81c --- /dev/null +++ b/lottie/src/main/java/com/airbnb/lottie/LottieFeatureFlags.java @@ -0,0 +1,34 @@ +package com.airbnb.lottie; + +import android.annotation.SuppressLint; +import android.os.Build; + +import com.airbnb.lottie.utils.Logger; + +import java.util.HashSet; + +class LottieFeatureFlags { + + private final HashSet enabledFlags = new HashSet<>(); + + /** + * Returns true if the flag was changed. + */ + @SuppressLint("DefaultLocale") + public boolean enableFlag(LottieFeatureFlag flag, boolean enable) { + if (enable) { + if (Build.VERSION.SDK_INT < flag.minRequiredSdkVersion) { + Logger.warning(String.format("%s is not supported pre SDK %d", flag.name(), flag.minRequiredSdkVersion)); + return false; + } + return enabledFlags.add(flag); + } else { + return enabledFlags.remove(flag); + } + } + + public boolean isFlagEnabled(LottieFeatureFlag flag) { + return enabledFlags.contains(flag); + } + +} diff --git a/lottie/src/main/java/com/airbnb/lottie/model/content/MergePaths.java b/lottie/src/main/java/com/airbnb/lottie/model/content/MergePaths.java index 315add2d08..00ec362056 100644 --- a/lottie/src/main/java/com/airbnb/lottie/model/content/MergePaths.java +++ b/lottie/src/main/java/com/airbnb/lottie/model/content/MergePaths.java @@ -4,6 +4,7 @@ import com.airbnb.lottie.LottieComposition; import com.airbnb.lottie.LottieDrawable; +import com.airbnb.lottie.LottieFeatureFlag; import com.airbnb.lottie.animation.content.Content; import com.airbnb.lottie.animation.content.MergePathsContent; import com.airbnb.lottie.model.layer.BaseLayer; @@ -60,7 +61,7 @@ public boolean isHidden() { } @Override @Nullable public Content toContent(LottieDrawable drawable, LottieComposition composition, BaseLayer layer) { - if (!drawable.enableMergePathsForKitKatAndAbove()) { + if (!drawable.isFeatureFlagEnabled(LottieFeatureFlag.MergePathsApi19)) { Logger.warning("Animation contains merge paths but they are disabled."); return null; }