Added support for markers (#1105)
This adds new APIs for setting the min/max/min and max frame based on a marker comment name.
Fixes #487
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e274a98..77cd7af 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,8 @@
-# 3.0.0
+# 3.0.0-beta2
+### Features and Improvements
+* Added support for markers. You can now call `setMinFrame`, `setMaxFrame` and `setMinAndMaxFrame` with a marker name.
+
+# 3.0.0-beta1
### Features and Improvements
* **Significant** mask and matte performance improvements by only calling saveLayer() on the intersection bounds of the content and mask/matte.
* Removed **all** memory allocations during playback including autoboxing.
diff --git a/LottieSample/src/androidTest/java/com/airbnb/lottie/LottieTest.kt b/LottieSample/src/androidTest/java/com/airbnb/lottie/LottieTest.kt
index 31a4bf9..070f921 100644
--- a/LottieSample/src/androidTest/java/com/airbnb/lottie/LottieTest.kt
+++ b/LottieSample/src/androidTest/java/com/airbnb/lottie/LottieTest.kt
@@ -85,6 +85,7 @@
snapshotFrameBoundaries()
snapshotScaleTypes()
testDynamicProperties()
+ testMarkers()
snapshotter.finalizeReportAndUpload()
}
}
@@ -493,6 +494,18 @@
}
}
+ private suspend fun CoroutineScope.testMarkers() {
+ withAnimationView("Tests/Marker.json", "Marker", "startFrame") { animationView ->
+ animationView.setMinAndMaxFrame("Marker A")
+ animationView.frame = animationView.minFrame.toInt()
+ }
+
+ withAnimationView("Tests/Marker.json", "Marker", "endFrame") { animationView ->
+ animationView.setMinAndMaxFrame("Marker A")
+ animationView.frame = animationView.maxFrame.toInt()
+ }
+ }
+
private suspend fun CoroutineScope.withAnimationView(
animationName: String,
snapshotName: String? = null,
diff --git a/LottieSample/src/main/assets/Tests/Marker.json b/LottieSample/src/main/assets/Tests/Marker.json
new file mode 100644
index 0000000..e264f81
--- /dev/null
+++ b/LottieSample/src/main/assets/Tests/Marker.json
@@ -0,0 +1 @@
+{"v":"5.3.4","fr":29.9700012207031,"ip":0,"op":61.0000024845809,"w":200,"h":200,"nm":"Marker","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":[100,100,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":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[51.816,51.816],"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":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[-67.092,-65.592],"e":[67.092,65.592],"to":[22.3639335632324,21.8639335632324],"ti":[-22.3639335632324,-21.8639335632324]},{"t":60.0000024438501}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":61.0000024845809,"st":0,"bm":0}],"markers":[{"tm":14.0000005702317,"cm":"Marker A","dr":30.0000012219251},{"tm":33.0000013441176,"cm":"Marker B\r","dr":0}]}
\ No newline at end of file
diff --git a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/PlayerViewModel.kt b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/PlayerViewModel.kt
index 0594bc7..dac6429 100644
--- a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/PlayerViewModel.kt
+++ b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/PlayerViewModel.kt
@@ -7,6 +7,7 @@
import com.airbnb.lottie.LottieComposition
import com.airbnb.lottie.LottieCompositionFactory
import com.airbnb.lottie.LottieTask
+import com.airbnb.lottie.model.LottieCompositionCache
import com.airbnb.lottie.samples.model.CompositionArgs
import com.airbnb.mvrx.*
import java.io.FileInputStream
diff --git a/lottie/src/main/java/com/airbnb/lottie/LottieAnimationView.java b/lottie/src/main/java/com/airbnb/lottie/LottieAnimationView.java
index 937f1b7..88d088a 100644
--- a/lottie/src/main/java/com/airbnb/lottie/LottieAnimationView.java
+++ b/lottie/src/main/java/com/airbnb/lottie/LottieAnimationView.java
@@ -462,6 +462,31 @@
}
/**
+ * Sets the minimum frame to the start time of the specified marker.
+ * @throws IllegalArgumentException if the marker is not found.
+ */
+ public void setMinFrame(String markerName) {
+ lottieDrawable.setMinFrame(markerName);
+ }
+
+ /**
+ * Sets the maximum frame to the start time + duration of the specified marker.
+ * @throws IllegalArgumentException if the marker is not found.
+ */
+ public void setMaxFrame(String markerName) {
+ lottieDrawable.setMaxFrame(markerName);
+ }
+
+ /**
+ * Sets the minimum and maximum frame to the start time and start time + duration
+ * of the specified marker.
+ * @throws IllegalArgumentException if the marker is not found.
+ */
+ public void setMinAndMaxFrame(String markerName) {
+ lottieDrawable.setMinAndMaxFrame(markerName);
+ }
+
+ /**
* @see #setMinFrame(int)
* @see #setMaxFrame(int)
*/
diff --git a/lottie/src/main/java/com/airbnb/lottie/LottieComposition.java b/lottie/src/main/java/com/airbnb/lottie/LottieComposition.java
index 2497a58..8ecc781 100644
--- a/lottie/src/main/java/com/airbnb/lottie/LottieComposition.java
+++ b/lottie/src/main/java/com/airbnb/lottie/LottieComposition.java
@@ -14,6 +14,7 @@
import com.airbnb.lottie.model.Font;
import com.airbnb.lottie.model.FontCharacter;
+import com.airbnb.lottie.model.Marker;
import com.airbnb.lottie.model.layer.Layer;
import org.json.JSONObject;
@@ -43,6 +44,7 @@
private Map<String, LottieImageAsset> images;
/** Map of font names to fonts */
private Map<String, Font> fonts;
+ private List<Marker> markers;
private SparseArrayCompat<FontCharacter> characters;
private LongSparseArray<Layer> layerMap;
private List<Layer> layers;
@@ -66,7 +68,8 @@
public void init(Rect bounds, float startFrame, float endFrame, float frameRate,
List<Layer> layers, LongSparseArray<Layer> layerMap, Map<String,
List<Layer>> precomps, Map<String, LottieImageAsset> images,
- SparseArrayCompat<FontCharacter> characters, Map<String, Font> fonts) {
+ SparseArrayCompat<FontCharacter> characters, Map<String, Font> fonts,
+ List<Marker> markers) {
this.bounds = bounds;
this.startFrame = startFrame;
this.endFrame = endFrame;
@@ -77,6 +80,7 @@
this.images = images;
this.characters = characters;
this.fonts = fonts;
+ this.markers = markers;
}
@RestrictTo(RestrictTo.Scope.LIBRARY)
@@ -168,6 +172,22 @@
return fonts;
}
+ public List<Marker> getMarkers() {
+ return markers;
+ }
+
+ @Nullable
+ public Marker getMarker(String markerName) {
+ int size = markers.size();
+ for (int i = 0; i < markers.size(); i++) {
+ Marker marker = markers.get(i);
+ if (markerName.equals(marker.name)) {
+ return marker;
+ }
+ }
+ return null;
+ }
+
public boolean hasImages() {
return !images.isEmpty();
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java b/lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java
index c9aa349..59f69c7 100644
--- a/lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java
+++ b/lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java
@@ -12,18 +12,20 @@
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.os.Build;
+import android.util.Log;
+import android.view.View;
+
import androidx.annotation.FloatRange;
import androidx.annotation.IntDef;
import androidx.annotation.IntRange;
import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import android.util.Log;
-import android.view.View;
import com.airbnb.lottie.manager.FontAssetManager;
import com.airbnb.lottie.manager.ImageAssetManager;
import com.airbnb.lottie.model.KeyPath;
+import com.airbnb.lottie.model.Marker;
import com.airbnb.lottie.model.layer.CompositionLayer;
import com.airbnb.lottie.parser.LayerParser;
import com.airbnb.lottie.utils.LottieValueAnimator;
@@ -61,14 +63,21 @@
private final Set<ColorFilterData> colorFilterData = new HashSet<>();
private final ArrayList<LazyCompositionTask> lazyCompositionTasks = new ArrayList<>();
- @Nullable private ImageAssetManager imageAssetManager;
- @Nullable private String imageAssetsFolder;
- @Nullable private ImageAssetDelegate imageAssetDelegate;
- @Nullable private FontAssetManager fontAssetManager;
- @Nullable FontAssetDelegate fontAssetDelegate;
- @Nullable TextDelegate textDelegate;
+ @Nullable
+ private ImageAssetManager imageAssetManager;
+ @Nullable
+ private String imageAssetsFolder;
+ @Nullable
+ private ImageAssetDelegate imageAssetDelegate;
+ @Nullable
+ private FontAssetManager fontAssetManager;
+ @Nullable
+ FontAssetDelegate fontAssetDelegate;
+ @Nullable
+ TextDelegate textDelegate;
private boolean enableMergePaths;
- @Nullable private CompositionLayer compositionLayer;
+ @Nullable
+ private CompositionLayer compositionLayer;
private int alpha = 255;
private boolean performanceTrackingEnabled;
/**
@@ -80,7 +89,8 @@
@IntDef({RESTART, REVERSE})
@Retention(RetentionPolicy.SOURCE)
- public @interface RepeatMode {}
+ public @interface RepeatMode {
+ }
/**
* When the animation reaches the end and <code>repeatCount</code> is INFINITE
@@ -100,7 +110,8 @@
public LottieDrawable() {
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override public void onAnimationUpdate(ValueAnimator animation) {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
if (compositionLayer != null) {
compositionLayer.setProgress(animator.getAnimatedValueAbsolute());
}
@@ -128,7 +139,7 @@
/**
* Enable this to get merge path support for devices running KitKat (19) and above.
- *
+ * <p>
* 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.
@@ -156,15 +167,15 @@
* 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 call
* `setImageAssetsFolder("airbnb_loader/");`.
- *
- *
+ * <p>
+ * <p>
* If you use LottieDrawable directly, you MUST call {@link #recycleBitmaps()} when you
* are done. Calling {@link #recycleBitmaps()} doesn't have to be final and {@link LottieDrawable}
* will recreate the bitmaps if needed but they will leak if you don't recycle them.
- *
+ * <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
@@ -175,7 +186,8 @@
this.imageAssetsFolder = imageAssetsFolder;
}
- @Nullable public String getImageAssetsFolder() {
+ @Nullable
+ public String getImageAssetsFolder() {
return imageAssetsFolder;
}
@@ -244,7 +256,8 @@
invalidateSelf();
}
- @Override public void invalidateSelf() {
+ @Override
+ public void invalidateSelf() {
if (isDirty) {
return;
}
@@ -255,23 +268,28 @@
}
}
- @Override public void setAlpha(@IntRange(from = 0, to = 255) int alpha) {
+ @Override
+ public void setAlpha(@IntRange(from = 0, to = 255) int alpha) {
this.alpha = alpha;
}
- @Override public int getAlpha() {
+ @Override
+ public int getAlpha() {
return alpha;
}
- @Override public void setColorFilter(@Nullable ColorFilter colorFilter) {
+ @Override
+ public void setColorFilter(@Nullable ColorFilter colorFilter) {
Log.w(L.TAG, "Use addColorFilter instead.");
}
- @Override public int getOpacity() {
+ @Override
+ public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
- @Override public void draw(@NonNull Canvas canvas) {
+ @Override
+ public void draw(@NonNull Canvas canvas) {
isDirty = false;
L.beginSection("Drawable#draw");
if (compositionLayer == null) {
@@ -323,16 +341,19 @@
// <editor-fold desc="animator">
@MainThread
- @Override public void start() {
+ @Override
+ public void start() {
playAnimation();
}
@MainThread
- @Override public void stop() {
+ @Override
+ public void stop() {
endAnimation();
}
- @Override public boolean isRunning() {
+ @Override
+ public boolean isRunning() {
return isAnimating();
}
@@ -344,7 +365,8 @@
public void playAnimation() {
if (compositionLayer == null) {
lazyCompositionTasks.add(new LazyCompositionTask() {
- @Override public void run(LottieComposition composition) {
+ @Override
+ public void run(LottieComposition composition) {
playAnimation();
}
});
@@ -367,7 +389,8 @@
public void resumeAnimation() {
if (compositionLayer == null) {
lazyCompositionTasks.add(new LazyCompositionTask() {
- @Override public void run(LottieComposition composition) {
+ @Override
+ public void run(LottieComposition composition) {
resumeAnimation();
}
});
@@ -402,16 +425,17 @@
/**
* Sets the minimum progress that the animation will start from when playing or looping.
*/
- public void setMinProgress(final float minProgress) {
- if (composition == null) {
- lazyCompositionTasks.add(new LazyCompositionTask() {
- @Override public void run(LottieComposition composition) {
- setMinProgress(minProgress);
- }
- });
- return;
- }
- setMinFrame((int) MiscUtils.lerp(composition.getStartFrame(), composition.getEndFrame(), minProgress));
+ public void setMinProgress(final float minProgress) {
+ if (composition == null) {
+ lazyCompositionTasks.add(new LazyCompositionTask() {
+ @Override
+ public void run(LottieComposition composition) {
+ setMinProgress(minProgress);
+ }
+ });
+ return;
+ }
+ setMinFrame((int) MiscUtils.lerp(composition.getStartFrame(), composition.getEndFrame(), minProgress));
}
/**
@@ -443,7 +467,8 @@
public void setMaxProgress(@FloatRange(from = 0f, to = 1f) final float maxProgress) {
if (composition == null) {
lazyCompositionTasks.add(new LazyCompositionTask() {
- @Override public void run(LottieComposition composition) {
+ @Override
+ public void run(LottieComposition composition) {
setMaxProgress(maxProgress);
}
});
@@ -453,6 +478,71 @@
}
/**
+ * Sets the minimum frame to the start time of the specified marker.
+ * @throws IllegalArgumentException if the marker is not found.
+ */
+ public void setMinFrame(final String markerName) {
+ if (composition == null) {
+ lazyCompositionTasks.add(new LazyCompositionTask() {
+ @Override
+ public void run(LottieComposition composition) {
+ setMinFrame(markerName);
+ }
+ });
+ return;
+ }
+ Marker marker = composition.getMarker(markerName);
+ if (marker == null) {
+ throw new IllegalArgumentException("Cannot find marker with name " + markerName + ".");
+ }
+ setMinFrame((int) marker.startFrame);
+ }
+
+ /**
+ * Sets the maximum frame to the start time + duration of the specified marker.
+ * @throws IllegalArgumentException if the marker is not found.
+ */
+ public void setMaxFrame(final String markerName) {
+ if (composition == null) {
+ lazyCompositionTasks.add(new LazyCompositionTask() {
+ @Override
+ public void run(LottieComposition composition) {
+ setMaxFrame(markerName);
+ }
+ });
+ return;
+ }
+ Marker marker = composition.getMarker(markerName);
+ if (marker == null) {
+ throw new IllegalArgumentException("Cannot find marker with name " + markerName + ".");
+ }
+ setMaxFrame((int) (marker.startFrame + marker.durationFrames));
+ }
+
+ /**
+ * Sets the minimum and maximum frame to the start time and start time + duration
+ * of the specified marker.
+ * @throws IllegalArgumentException if the marker is not found.
+ */
+ public void setMinAndMaxFrame(final String markerName) {
+ if (composition == null) {
+ lazyCompositionTasks.add(new LazyCompositionTask() {
+ @Override
+ public void run(LottieComposition composition) {
+ setMinAndMaxFrame(markerName);
+ }
+ });
+ return;
+ }
+ Marker marker = composition.getMarker(markerName);
+ if (marker == null) {
+ throw new IllegalArgumentException("Cannot find marker with name " + markerName + ".");
+ }
+ int startFrame = (int) marker.startFrame;
+ setMinAndMaxFrame(startFrame, startFrame + (int) marker.durationFrames);
+ }
+
+ /**
* @see #setMinFrame(int)
* @see #setMaxFrame(int)
*/
@@ -478,7 +568,8 @@
@FloatRange(from = 0f, to = 1f) final float maxProgress) {
if (composition == null) {
lazyCompositionTasks.add(new LazyCompositionTask() {
- @Override public void run(LottieComposition composition) {
+ @Override
+ public void run(LottieComposition composition) {
setMinAndMaxProgress(minProgress, maxProgress);
}
});
@@ -486,11 +577,12 @@
}
setMinAndMaxFrame((int) MiscUtils.lerp(composition.getStartFrame(), composition.getEndFrame(), minProgress),
- (int) MiscUtils.lerp(composition.getStartFrame(), composition.getEndFrame(), maxProgress));
+ (int) MiscUtils.lerp(composition.getStartFrame(), composition.getEndFrame(), maxProgress));
}
/**
* Reverses the current animation speed. This does NOT play the animation.
+ *
* @see #setSpeed(float)
* @see #playAnimation()
* @see #resumeAnimation()
@@ -545,7 +637,8 @@
public void setFrame(final int frame) {
if (composition == null) {
lazyCompositionTasks.add(new LazyCompositionTask() {
- @Override public void run(LottieComposition composition) {
+ @Override
+ public void run(LottieComposition composition) {
setFrame(frame);
}
});
@@ -565,7 +658,8 @@
public void setProgress(@FloatRange(from = 0f, to = 1f) final float progress) {
if (composition == null) {
lazyCompositionTasks.add(new LazyCompositionTask() {
- @Override public void run(LottieComposition composition) {
+ @Override
+ public void run(LottieComposition composition) {
setProgress(progress);
}
});
@@ -575,7 +669,6 @@
}
/**
- *
* @see #setRepeatCount(int)
*/
@Deprecated
@@ -591,7 +684,7 @@
* @param mode {@link #RESTART} or {@link #REVERSE}
*/
public void setRepeatMode(@RepeatMode int mode) {
- animator.setRepeatMode(mode);
+ animator.setRepeatMode(mode);
}
/**
@@ -640,12 +733,12 @@
/**
* Set the scale on the current composition. The only cost of this function is re-rendering the
* current frame so you may call it frequent to scale something up or down.
- *
+ * <p>
* The smaller the animation is, the better the performance will be. You may find that scaling an
* animation down then rendering it in a larger ImageView and letting ImageView scale it back up
* with a scaleType such as centerInside will yield better performance with little perceivable
* quality loss.
- *
+ * <p>
* You can also use a fixed view width/height in conjunction with the normal ImageView
* scaleTypes centerCrop and centerInside.
*/
@@ -658,7 +751,7 @@
* 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
@@ -688,7 +781,8 @@
this.textDelegate = textDelegate;
}
- @Nullable public TextDelegate getTextDelegate() {
+ @Nullable
+ public TextDelegate getTextDelegate() {
return textDelegate;
}
@@ -728,18 +822,20 @@
return animator.getAnimatedValueAbsolute();
}
- @Override public int getIntrinsicWidth() {
+ @Override
+ public int getIntrinsicWidth() {
return composition == null ? -1 : (int) (composition.getBounds().width() * getScale());
}
- @Override public int getIntrinsicHeight() {
+ @Override
+ public int getIntrinsicHeight() {
return composition == null ? -1 : (int) (composition.getBounds().height() * getScale());
}
/**
* Takes a {@link KeyPath}, potentially with wildcards or globstars and resolve it to a list of
* zero or more actual {@link KeyPath Keypaths} that exist in the current animation.
- *
+ * <p>
* If you want to set value callbacks for any of these values, it is recommend to use the
* returned {@link KeyPath} objects because they will be internally resolved to their content
* and won't trigger a tree walk of the animation contents when applied.
@@ -757,7 +853,7 @@
/**
* Add an property callback for the specified {@link KeyPath}. This {@link KeyPath} can resolve
* to multiple contents. In that case, the callbacks's value will apply to all of them.
- *
+ * <p>
* Internally, this will check if the {@link KeyPath} has already been resolved with
* {@link #resolveKeyPath(KeyPath)} and will resolve it if it hasn't.
*/
@@ -765,7 +861,8 @@
final KeyPath keyPath, final T property, final LottieValueCallback<T> callback) {
if (compositionLayer == null) {
lazyCompositionTasks.add(new LazyCompositionTask() {
- @Override public void run(LottieComposition composition) {
+ @Override
+ public void run(LottieComposition composition) {
addValueCallback(keyPath, property, callback);
}
});
@@ -801,9 +898,10 @@
* drawable.addValueCallback(yourKeyPath, LottieProperty.COLOR) { yourColor }
*/
public <T> void addValueCallback(KeyPath keyPath, T property,
- final SimpleLottieValueCallback<T> callback) {
+ final SimpleLottieValueCallback<T> callback) {
addValueCallback(keyPath, property, new LottieValueCallback<T>() {
- @Override public T getValue(LottieFrameInfo<T> frameInfo) {
+ @Override
+ public T getValue(LottieFrameInfo<T> frameInfo) {
return callback.getValue(frameInfo);
}
});
@@ -821,7 +919,7 @@
ImageAssetManager bm = getImageAssetManager();
if (bm == null) {
Log.w(L.TAG, "Cannot update bitmap. Most likely the drawable is not added to a View " +
- "which prevents Lottie from getting a Context.");
+ "which prevents Lottie from getting a Context.");
return null;
}
Bitmap ret = bm.updateBitmap(id, bitmap);
@@ -829,7 +927,8 @@
return ret;
}
- @Nullable public Bitmap getImageAsset(String id) {
+ @Nullable
+ public Bitmap getImageAsset(String id) {
ImageAssetManager bm = getImageAssetManager();
if (bm != null) {
return bm.bitmapForId(id);
@@ -855,7 +954,8 @@
return imageAssetManager;
}
- @Nullable public Typeface getTypeface(String fontFamily, String style) {
+ @Nullable
+ public Typeface getTypeface(String fontFamily, String style) {
FontAssetManager assetManager = getFontAssetManager();
if (assetManager != null) {
return assetManager.getTypeface(fontFamily, style);
@@ -876,7 +976,8 @@
return fontAssetManager;
}
- @Nullable private Context getContext() {
+ @Nullable
+ private Context getContext() {
Callback callback = getCallback();
if (callback == null) {
return null;
@@ -892,7 +993,8 @@
* These Drawable.Callback methods proxy the calls so that this is the drawable that is
* actually invalidated, not a child one which will not pass the view's validateDrawable check.
*/
- @Override public void invalidateDrawable(@NonNull Drawable who) {
+ @Override
+ public void invalidateDrawable(@NonNull Drawable who) {
Callback callback = getCallback();
if (callback == null) {
return;
@@ -900,7 +1002,8 @@
callback.invalidateDrawable(this);
}
- @Override public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
+ @Override
+ public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
Callback callback = getCallback();
if (callback == null) {
return;
@@ -908,7 +1011,8 @@
callback.scheduleDrawable(this, what, when);
}
- @Override public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
+ @Override
+ public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
Callback callback = getCallback();
if (callback == null) {
return;
@@ -929,17 +1033,20 @@
private static class ColorFilterData {
final String layerName;
- @Nullable final String contentName;
- @Nullable final ColorFilter colorFilter;
+ @Nullable
+ final String contentName;
+ @Nullable
+ final ColorFilter colorFilter;
ColorFilterData(@Nullable String layerName, @Nullable String contentName,
- @Nullable ColorFilter colorFilter) {
+ @Nullable ColorFilter colorFilter) {
this.layerName = layerName;
this.contentName = contentName;
this.colorFilter = colorFilter;
}
- @Override public int hashCode() {
+ @Override
+ public int hashCode() {
int hashCode = 17;
if (layerName != null) {
hashCode = hashCode * 31 * layerName.hashCode();
@@ -951,7 +1058,8 @@
return hashCode;
}
- @Override public boolean equals(Object obj) {
+ @Override
+ public boolean equals(Object obj) {
if (this == obj) {
return true;
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/Marker.java b/lottie/src/main/java/com/airbnb/lottie/model/Marker.java
new file mode 100644
index 0000000..bab7413
--- /dev/null
+++ b/lottie/src/main/java/com/airbnb/lottie/model/Marker.java
@@ -0,0 +1,14 @@
+package com.airbnb.lottie.model;
+
+public class Marker {
+
+ public final String name;
+ public final float startFrame;
+ public final float durationFrames;
+
+ public Marker(String name, float startFrame, float durationFrames) {
+ this.name = name;
+ this.durationFrames = durationFrames;
+ this.startFrame = startFrame;
+ }
+}
diff --git a/lottie/src/main/java/com/airbnb/lottie/parser/LottieCompositionParser.java b/lottie/src/main/java/com/airbnb/lottie/parser/LottieCompositionParser.java
index c8a387d..53004d9 100644
--- a/lottie/src/main/java/com/airbnb/lottie/parser/LottieCompositionParser.java
+++ b/lottie/src/main/java/com/airbnb/lottie/parser/LottieCompositionParser.java
@@ -10,6 +10,7 @@
import com.airbnb.lottie.LottieImageAsset;
import com.airbnb.lottie.model.Font;
import com.airbnb.lottie.model.FontCharacter;
+import com.airbnb.lottie.model.Marker;
import com.airbnb.lottie.model.layer.Layer;
import com.airbnb.lottie.utils.Utils;
@@ -35,6 +36,7 @@
Map<String, List<Layer>> precomps = new HashMap<>();
Map<String, LottieImageAsset> images = new HashMap<>();
Map<String, Font> fonts = new HashMap<>();
+ List<Marker> markers = new ArrayList<>();
SparseArrayCompat<FontCharacter> characters = new SparseArrayCompat<>();
LottieComposition composition = new LottieComposition();
@@ -80,6 +82,9 @@
case "chars":
parseChars(reader, composition, characters);
break;
+ case "markers":
+ parseMarkers(reader, composition, markers);
+ break;
default:
reader.skipValue();
}
@@ -91,7 +96,7 @@
Rect bounds = new Rect(0, 0, scaledWidth, scaledHeight);
composition.init(bounds, startFrame, endFrame, frameRate, layers, layerMap, precomps,
- images, characters, fonts);
+ images, characters, fonts, markers);
return composition;
}
@@ -174,7 +179,6 @@
}
private static void parseFonts(JsonReader reader, Map<String, Font> fonts) throws IOException {
-
reader.beginObject();
while (reader.hasNext()) {
switch (reader.nextName()) {
@@ -203,4 +207,33 @@
}
reader.endArray();
}
+
+ private static void parseMarkers(
+ JsonReader reader, LottieComposition composition, List<Marker> markers) throws IOException{
+ reader.beginArray();
+ while (reader.hasNext()) {
+ String comment = null;
+ float frame = 0f;
+ float durationFrames = 0f;
+ reader.beginObject();
+ while (reader.hasNext()) {
+ switch (reader.nextName()) {
+ case "cm":
+ comment = reader.nextString();
+ break;
+ case "tm":
+ frame = (float) reader.nextDouble();
+ break;
+ case "dr":
+ durationFrames = (float) reader.nextDouble();
+ break;
+ default:
+ reader.skipValue();
+ }
+ }
+ reader.endObject();
+ markers.add(new Marker(comment, frame, durationFrames));
+ }
+ reader.endArray();
+ }
}
diff --git a/lottie/src/test/java/com/airbnb/lottie/LottieDrawableTest.java b/lottie/src/test/java/com/airbnb/lottie/LottieDrawableTest.java
index 195420e..bb42983 100644
--- a/lottie/src/test/java/com/airbnb/lottie/LottieDrawableTest.java
+++ b/lottie/src/test/java/com/airbnb/lottie/LottieDrawableTest.java
@@ -5,6 +5,7 @@
import androidx.collection.SparseArrayCompat;
import com.airbnb.lottie.model.Font;
import com.airbnb.lottie.model.FontCharacter;
+import com.airbnb.lottie.model.Marker;
import com.airbnb.lottie.model.layer.Layer;
import org.junit.Test;
@@ -22,7 +23,7 @@
composition.init(new Rect(), startFrame, endFrame, 1000, new ArrayList<Layer>(),
new LongSparseArray<Layer>(0), new HashMap<String, List<Layer>>(0),
new HashMap<String, LottieImageAsset>(0), new SparseArrayCompat<FontCharacter>(0),
- new HashMap<String, Font>(0));
+ new HashMap<String, Font>(0), new ArrayList<Marker>());
return composition;
}
diff --git a/lottie/src/test/java/com/airbnb/lottie/LottieValueAnimatorUnitTest.java b/lottie/src/test/java/com/airbnb/lottie/LottieValueAnimatorUnitTest.java
index 8a582ad..08b0b8c 100644
--- a/lottie/src/test/java/com/airbnb/lottie/LottieValueAnimatorUnitTest.java
+++ b/lottie/src/test/java/com/airbnb/lottie/LottieValueAnimatorUnitTest.java
@@ -7,6 +7,7 @@
import androidx.collection.SparseArrayCompat;
import com.airbnb.lottie.model.Font;
import com.airbnb.lottie.model.FontCharacter;
+import com.airbnb.lottie.model.Marker;
import com.airbnb.lottie.model.layer.Layer;
import com.airbnb.lottie.utils.LottieValueAnimator;
import org.junit.Before;
@@ -62,7 +63,7 @@
composition.init(new Rect(), startFrame, endFrame, 1000, new ArrayList<Layer>(),
new LongSparseArray<Layer>(0), new HashMap<String, List<Layer>>(0),
new HashMap<String, LottieImageAsset>(0), new SparseArrayCompat<FontCharacter>(0),
- new HashMap<String, Font>(0));
+ new HashMap<String, Font>(0), new ArrayList<Marker>());
return composition;
}