Extract more factory classes (#139)
Another change for #3
Pretty mechanical and straightforward change. Extracted a bunch of constructors into more static inner factory classes with a newInstance method by convention.
Later on we can pull out all these factories into a separate module that provides the default json parsing implementation.
diff --git a/LottieSample/src/androidTest/java/com/airbnb/lottie/LottieTest.java b/LottieSample/src/androidTest/java/com/airbnb/lottie/LottieTest.java
index 4fead43..a7ed1ad 100644
--- a/LottieSample/src/androidTest/java/com/airbnb/lottie/LottieTest.java
+++ b/LottieSample/src/androidTest/java/com/airbnb/lottie/LottieTest.java
@@ -22,34 +22,35 @@
MainActivity.class);
@Test public void testAll() {
- TestRobot.testAnimation(activityRule.getActivity(), "9squares-AlBoardman.json");
- TestRobot.testAnimation(activityRule.getActivity(), "EmptyState.json");
- TestRobot.testAnimation(activityRule.getActivity(), "HamburgerArrow.json");
- TestRobot.testAnimation(activityRule.getActivity(), "LottieLogo1.json");
- TestRobot.testAnimation(activityRule.getActivity(), "LottieLogo2.json");
- TestRobot.testAnimation(activityRule.getActivity(), "MotionCorpse-Jrcanest.json");
- TestRobot.testAnimation(activityRule.getActivity(), "PinJump.json");
- TestRobot.testAnimation(activityRule.getActivity(), "TwitterHeart.json");
- TestRobot.testAnimation(activityRule.getActivity(), "Tests/Hosts.json");
- TestRobot.testAnimation(activityRule.getActivity(), "Tests/LightBulb.json", null,
+ MainActivity activity = activityRule.getActivity();
+ TestRobot.testAnimation(activity, "9squares-AlBoardman.json");
+ TestRobot.testAnimation(activity, "EmptyState.json");
+ TestRobot.testAnimation(activity, "HamburgerArrow.json");
+ TestRobot.testAnimation(activity, "LottieLogo1.json");
+ TestRobot.testAnimation(activity, "LottieLogo2.json");
+ TestRobot.testAnimation(activity, "MotionCorpse-Jrcanest.json");
+ TestRobot.testAnimation(activity, "PinJump.json");
+ TestRobot.testAnimation(activity, "TwitterHeart.json");
+ TestRobot.testAnimation(activity, "Tests/Hosts.json");
+ TestRobot.testAnimation(activity, "Tests/LightBulb.json", null,
new float[]{0f, 0.05f, 0.10f, 0.2f, 0.3f, 0.4f, 0.5f, 1f});
- TestRobot.testAnimation(activityRule.getActivity(), "Tests/LoopPlayOnce.json");
- TestRobot.testAnimation(activityRule.getActivity(), "Tests/Alarm.json");
- TestRobot.testAnimation(activityRule.getActivity(), "Tests/CheckSwitch.json");
- TestRobot.testAnimation(activityRule.getActivity(), "Tests/EllipseTrimPath.json");
- TestRobot.testAnimation(activityRule.getActivity(), "Tests/SplitDimensions.json");
- TestRobot.testAnimation(activityRule.getActivity(), "Tests/TrimPathsFull.json");
- TestRobot.testAnimation(activityRule.getActivity(), "Tests/Laugh4.json");
- TestRobot.testAnimation(activityRule.getActivity(), "Tests/Star.json");
- TestRobot.testAnimation(activityRule.getActivity(), "Tests/Polygon.json");
- TestRobot.testAnimation(activityRule.getActivity(), "Tests/AllSet.json");
- TestRobot.testAnimation(activityRule.getActivity(), "Tests/City.json");
- TestRobot.testAnimation(activityRule.getActivity(), "Tests/PreCompMadness.json");
- TestRobot.testAnimation(activityRule.getActivity(), "Tests/MatteParentPrecomp.json");
- TestRobot.testAnimation(activityRule.getActivity(), "Tests/Image.json", "Tests/weaccept");
- TestRobot.testStatic(activityRule.getActivity(), "Tests/TrimPathFill.json");
- TestRobot.testStatic(activityRule.getActivity(), "Tests/Mask_26.json");
- TestRobot.testStatic(activityRule.getActivity(), "Tests/MatteInv.json");
- TestRobot.testStatic(activityRule.getActivity(), "Tests/MaskInv.json");
+ TestRobot.testAnimation(activity, "Tests/LoopPlayOnce.json");
+ TestRobot.testAnimation(activity, "Tests/Alarm.json");
+ TestRobot.testAnimation(activity, "Tests/CheckSwitch.json");
+ TestRobot.testAnimation(activity, "Tests/EllipseTrimPath.json");
+ TestRobot.testAnimation(activity, "Tests/SplitDimensions.json");
+ TestRobot.testAnimation(activity, "Tests/TrimPathsFull.json");
+ TestRobot.testAnimation(activity, "Tests/Laugh4.json");
+ TestRobot.testAnimation(activity, "Tests/Star.json");
+ TestRobot.testAnimation(activity, "Tests/Polygon.json");
+ TestRobot.testAnimation(activity, "Tests/AllSet.json");
+ TestRobot.testAnimation(activity, "Tests/City.json");
+ TestRobot.testAnimation(activity, "Tests/PreCompMadness.json");
+ TestRobot.testAnimation(activity, "Tests/MatteParentPrecomp.json");
+ TestRobot.testAnimation(activity, "Tests/Image.json", "Tests/weaccept");
+ TestRobot.testStatic(activity, "Tests/TrimPathFill.json");
+ TestRobot.testStatic(activity, "Tests/Mask_26.json");
+ TestRobot.testStatic(activity, "Tests/MatteInv.json");
+ TestRobot.testStatic(activity, "Tests/MaskInv.json");
}
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/AnimatableTransform.java b/lottie/src/main/java/com/airbnb/lottie/AnimatableTransform.java
index 29bd13f..cf3d67e 100644
--- a/lottie/src/main/java/com/airbnb/lottie/AnimatableTransform.java
+++ b/lottie/src/main/java/com/airbnb/lottie/AnimatableTransform.java
@@ -3,63 +3,81 @@
import org.json.JSONObject;
class AnimatableTransform {
- private AnimatablePathValue anchorPoint;
- private IAnimatablePathValue position;
- private AnimatableScaleValue scale;
- private AnimatableFloatValue rotation;
- private AnimatableIntegerValue opacity;
+ private final AnimatablePathValue anchorPoint;
+ private final IAnimatablePathValue position;
+ private final AnimatableScaleValue scale;
+ private final AnimatableFloatValue rotation;
+ private final AnimatableIntegerValue opacity;
- AnimatableTransform(LottieComposition composition) {
- this.anchorPoint = new AnimatablePathValue();
- this.position = new AnimatablePathValue();
- this.scale = new AnimatableScaleValue(composition);
- this.rotation = new AnimatableFloatValue(composition, 0f);
- this.opacity = new AnimatableIntegerValue(composition, 255);
+ AnimatableTransform(AnimatablePathValue anchorPoint, IAnimatablePathValue position,
+ AnimatableScaleValue scale, AnimatableFloatValue rotation, AnimatableIntegerValue opacity) {
+ this.anchorPoint = anchorPoint;
+ this.position = position;
+ this.scale = scale;
+ this.rotation = rotation;
+ this.opacity = opacity;
}
- AnimatableTransform(JSONObject json, LottieComposition composition) {
- JSONObject anchorJson = json.optJSONObject("a");
- if (anchorJson != null) {
- anchorPoint = new AnimatablePathValue(anchorJson.opt("k"), composition);
- } else {
- throwMissingTransform("anchor");
+ static class Factory {
+ static AnimatableTransform newInstance(LottieComposition composition) {
+ AnimatablePathValue anchorPoint = new AnimatablePathValue();
+ IAnimatablePathValue position = new AnimatablePathValue();
+ AnimatableScaleValue scale = new AnimatableScaleValue(composition);
+ AnimatableFloatValue rotation = new AnimatableFloatValue(composition, 0f);
+ AnimatableIntegerValue opacity = new AnimatableIntegerValue(composition, 255);
+ return new AnimatableTransform(anchorPoint, position, scale, rotation, opacity);
}
- JSONObject positionJson = json.optJSONObject("p");
- if (positionJson != null) {
- position =
- AnimatablePathValue.createAnimatablePathOrSplitDimensionPath(positionJson, composition);
- } else {
- throwMissingTransform("position");
+ static AnimatableTransform newInstance(JSONObject json, LottieComposition composition) {
+ AnimatablePathValue anchorPoint = null;
+ IAnimatablePathValue position = null;
+ AnimatableScaleValue scale = null;
+ AnimatableFloatValue rotation = null;
+ AnimatableIntegerValue opacity = null;
+ JSONObject anchorJson = json.optJSONObject("a");
+ if (anchorJson != null) {
+ anchorPoint = new AnimatablePathValue(anchorJson.opt("k"), composition);
+ } else {
+ throwMissingTransform("anchor");
+ }
+
+ JSONObject positionJson = json.optJSONObject("p");
+ if (positionJson != null) {
+ position =
+ AnimatablePathValue.createAnimatablePathOrSplitDimensionPath(positionJson, composition);
+ } else {
+ throwMissingTransform("position");
+ }
+
+ JSONObject scaleJson = json.optJSONObject("s");
+ if (scaleJson != null) {
+ scale = new AnimatableScaleValue(scaleJson, composition, false);
+ } else {
+ throwMissingTransform("scale");
+ }
+
+ JSONObject rotationJson = json.optJSONObject("r");
+ if (rotationJson == null) {
+ rotationJson = json.optJSONObject("rz");
+ }
+ if (rotationJson != null) {
+ rotation = new AnimatableFloatValue(rotationJson, composition, false);
+ } else {
+ throwMissingTransform("rotation");
+ }
+
+ JSONObject opacityJson = json.optJSONObject("o");
+ if (opacityJson != null) {
+ opacity = new AnimatableIntegerValue(opacityJson, composition, false, true);
+ } else {
+ throwMissingTransform("opacity");
+ }
+ return new AnimatableTransform(anchorPoint, position, scale, rotation, opacity);
}
- JSONObject scaleJson = json.optJSONObject("s");
- if (scaleJson != null) {
- scale = new AnimatableScaleValue(scaleJson, composition, false);
- } else {
- throwMissingTransform("scale");
+ private static void throwMissingTransform(String missingProperty) {
+ throw new IllegalArgumentException("Missing transform for " + missingProperty);
}
-
- JSONObject rotationJson = json.optJSONObject("r");
- if (rotationJson == null) {
- rotationJson = json.optJSONObject("rz");
- }
- if (rotationJson != null) {
- rotation = new AnimatableFloatValue(rotationJson, composition, false);
- } else {
- throwMissingTransform("rotation");
- }
-
- JSONObject opacityJson = json.optJSONObject("o");
- if (opacityJson != null) {
- opacity = new AnimatableIntegerValue(opacityJson, composition, false, true);
- } else {
- throwMissingTransform("opacity");
- }
- }
-
- private void throwMissingTransform(String missingProperty) {
- throw new IllegalArgumentException("Missing transform for " + missingProperty);
}
AnimatablePathValue getAnchorPoint() {
diff --git a/lottie/src/main/java/com/airbnb/lottie/CircleShape.java b/lottie/src/main/java/com/airbnb/lottie/CircleShape.java
index 9cbf300..7d659af 100644
--- a/lottie/src/main/java/com/airbnb/lottie/CircleShape.java
+++ b/lottie/src/main/java/com/airbnb/lottie/CircleShape.java
@@ -6,10 +6,18 @@
private final IAnimatablePathValue position;
private final AnimatablePointValue size;
- CircleShape(JSONObject json, LottieComposition composition) {
- position = AnimatablePathValue.createAnimatablePathOrSplitDimensionPath(
- json.optJSONObject("p"), composition);
- size = new AnimatablePointValue(json.optJSONObject("s"), composition);
+ private CircleShape(IAnimatablePathValue position, AnimatablePointValue size) {
+ this.position = position;
+ this.size = size;
+ }
+
+ static class Factory {
+ static CircleShape newInstance(JSONObject json, LottieComposition composition) {
+ return new CircleShape(
+ AnimatablePathValue
+ .createAnimatablePathOrSplitDimensionPath(json.optJSONObject("p"), composition),
+ new AnimatablePointValue(json.optJSONObject("s"), composition));
+ }
}
public IAnimatablePathValue getPosition() {
diff --git a/lottie/src/main/java/com/airbnb/lottie/ImageAsset.java b/lottie/src/main/java/com/airbnb/lottie/ImageAsset.java
index 244c7ca..077314b 100644
--- a/lottie/src/main/java/com/airbnb/lottie/ImageAsset.java
+++ b/lottie/src/main/java/com/airbnb/lottie/ImageAsset.java
@@ -3,11 +3,10 @@
import org.json.JSONObject;
class ImageAsset {
-
- private int width;
- private int height;
- private String id;
- private String fileName;
+ private final int width;
+ private final int height;
+ private final String id;
+ private final String fileName;
ImageAsset(JSONObject imageJson) {
width = imageJson.optInt("w");
diff --git a/lottie/src/main/java/com/airbnb/lottie/ImageAssetBitmapManager.java b/lottie/src/main/java/com/airbnb/lottie/ImageAssetBitmapManager.java
index 45a199a..687b5f6 100644
--- a/lottie/src/main/java/com/airbnb/lottie/ImageAssetBitmapManager.java
+++ b/lottie/src/main/java/com/airbnb/lottie/ImageAssetBitmapManager.java
@@ -16,7 +16,6 @@
import static junit.framework.Assert.assertNotNull;
class ImageAssetBitmapManager {
-
private final Context context;
private String imagesFolder;
private final Map<String, ImageAsset> imageAssets;
diff --git a/lottie/src/main/java/com/airbnb/lottie/Keyframe.java b/lottie/src/main/java/com/airbnb/lottie/Keyframe.java
index 609e54c..bf1b6c4 100644
--- a/lottie/src/main/java/com/airbnb/lottie/Keyframe.java
+++ b/lottie/src/main/java/com/airbnb/lottie/Keyframe.java
@@ -15,7 +15,7 @@
import java.util.List;
public class Keyframe<T> {
- private static Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
+ private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
static <T> List<Keyframe<T>> parseKeyframes(JSONArray json, LottieComposition composition,
float scale, AnimatableValue<T, ?> animatableValue) {
diff --git a/lottie/src/main/java/com/airbnb/lottie/Layer.java b/lottie/src/main/java/com/airbnb/lottie/Layer.java
index 15ee77d..9ae24d5 100644
--- a/lottie/src/main/java/com/airbnb/lottie/Layer.java
+++ b/lottie/src/main/java/com/airbnb/lottie/Layer.java
@@ -31,116 +31,48 @@
Unknown
}
- private final List<Object> shapes = new ArrayList<>();
+ private final List<Object> shapes;
private final LottieComposition composition;
-
private final String layerName;
private final long layerId;
private final LayerType layerType;
private final long parentId;
@Nullable private final String refId;
-
- private final List<Mask> masks = new ArrayList<>();
-
+ private final List<Mask> masks;
private final AnimatableTransform transform;
- private int solidWidth;
- private int solidHeight;
- private int solidColor;
-
+ private final int solidWidth;
+ private final int solidHeight;
+ private final int solidColor;
private final float timeStretch;
private final float startProgress;
- private float preCompStartProgress;
- private int preCompWidth;
- private int preCompHeight;
- @Nullable private ImageAsset imageAsset;
+ private final int preCompWidth;
+ private final int preCompHeight;
+ private final List<Keyframe<Float>> inOutKeyframes;
+ private final MatteType matteType;
- private final List<Keyframe<Float>> inOutKeyframes = new ArrayList<>();
-
- private MatteType matteType;
-
- Layer(JSONObject json, LottieComposition composition) {
+ private Layer(List<Object> shapes, LottieComposition composition, String layerName, long layerId,
+ LayerType layerType, long parentId, @Nullable String refId, List<Mask> masks,
+ AnimatableTransform transform, int solidWidth, int solidHeight, int solidColor,
+ float timeStretch, float startProgress, int preCompWidth, int preCompHeight,
+ List<Keyframe<Float>> inOutKeyframes, MatteType matteType) {
+ this.shapes = shapes;
this.composition = composition;
- layerName = json.optString("nm");
- layerId = json.optLong("ind");
- refId = json.optString("refId");
-
- int layerTypeInt = json.optInt("ty", -1);
- if (layerTypeInt < LayerType.Unknown.ordinal()) {
- layerType = LayerType.values()[layerTypeInt];
- } else {
- layerType = LayerType.Unknown;
- }
-
- parentId = json.optLong("parent", -1);
-
- if (layerType == LayerType.Solid) {
- solidWidth = (int) (json.optInt("sw") * composition.getScale());
- solidHeight = (int) (json.optInt("sh") * composition.getScale());
- solidColor = Color.parseColor(json.optString("sc"));
- if (L.DBG) {
- Log.d(TAG, "\tSolid=" + Integer.toHexString(solidColor) + " " +
- solidWidth + "x" + solidHeight + " " + composition.getBounds());
- }
- }
-
- transform = new AnimatableTransform(json.optJSONObject("ks"), composition);
-
- matteType = MatteType.values()[json.optInt("tt")];
-
- JSONArray jsonMasks = json.optJSONArray("masksProperties");
- if (jsonMasks != null) {
- for (int i = 0; i < jsonMasks.length(); i++) {
- Mask mask = new Mask(jsonMasks.optJSONObject(i), composition);
- masks.add(mask);
- }
- }
-
- JSONArray shapesJson = json.optJSONArray("shapes");
- if (shapesJson != null) {
- for (int i = 0; i < shapesJson.length(); i++) {
- Object shape = ShapeGroup.shapeItemWithJson(shapesJson.optJSONObject(i), composition);
- if (shape != null) {
- shapes.add(shape);
- }
- }
- }
-
- timeStretch = (float) json.optDouble("sr", 1.0);
- float startFrame = (float) json.optDouble("st");
- float frames = composition.getDurationFrames();
- startProgress = startFrame / frames;
-
- if (layerType == LayerType.PreComp) {
- preCompWidth = (int) (json.optInt("w") * composition.getScale());
- preCompHeight = (int) (json.optInt("h") * composition.getScale());
- }
-
- long inFrame = json.optLong("ip");
- long outFrame = json.optLong("op");
-
- // Before the in frame
- if (inFrame > 0) {
- Keyframe<Float> preKeyframe = new Keyframe<>(composition, 0, inFrame);
- preKeyframe.startValue = 0f;
- preKeyframe.endValue = 0f;
- inOutKeyframes.add(preKeyframe);
- }
-
- // The + 1 is because the animation should be visible on the out frame itself.
- outFrame = (outFrame > 0 ? outFrame : composition.getEndFrame() + 1);
- Keyframe<Float> visibleKeyframe =
- new Keyframe<>(composition, inFrame, outFrame);
- visibleKeyframe.startValue = 1f;
- visibleKeyframe.endValue = 1f;
- inOutKeyframes.add(visibleKeyframe);
-
- if (outFrame <= composition.getDurationFrames()) {
- Keyframe<Float> outKeyframe =
- new Keyframe<>(composition, outFrame, composition.getEndFrame());
- outKeyframe.startValue = 0f;
- outKeyframe.endValue = 0f;
- inOutKeyframes.add(outKeyframe);
- }
+ this.layerName = layerName;
+ this.layerId = layerId;
+ this.layerType = layerType;
+ this.parentId = parentId;
+ this.refId = refId;
+ this.masks = masks;
+ this.transform = transform;
+ this.solidWidth = solidWidth;
+ this.solidHeight = solidHeight;
+ this.solidColor = solidColor;
+ this.timeStretch = timeStretch;
+ this.startProgress = startProgress;
+ this.preCompWidth = preCompWidth;
+ this.preCompHeight = preCompHeight;
+ this.inOutKeyframes = inOutKeyframes;
+ this.matteType = matteType;
}
LottieComposition getComposition() {
@@ -247,4 +179,101 @@
}
return sb.toString();
}
+
+ static class Factory {
+ static Layer newInstance(JSONObject json, LottieComposition composition) {
+ String layerName = json.optString("nm");
+ String refId = json.optString("refId");
+ long layerId = json.optLong("ind");
+ int solidWidth = 0;
+ int solidHeight = 0;
+ int solidColor = 0;
+ int preCompWidth = 0;
+ int preCompHeight = 0;
+ LayerType layerType;
+ int layerTypeInt = json.optInt("ty", -1);
+ if (layerTypeInt < LayerType.Unknown.ordinal()) {
+ layerType = LayerType.values()[layerTypeInt];
+ } else {
+ layerType = LayerType.Unknown;
+ }
+
+ long parentId = json.optLong("parent", -1);
+
+ if (layerType == LayerType.Solid) {
+ solidWidth = (int) (json.optInt("sw") * composition.getScale());
+ solidHeight = (int) (json.optInt("sh") * composition.getScale());
+ solidColor = Color.parseColor(json.optString("sc"));
+ if (L.DBG) {
+ Log.d(TAG, "\tSolid=" + Integer.toHexString(solidColor) + " " +
+ solidWidth + "x" + solidHeight + " " + composition.getBounds());
+ }
+ }
+
+ AnimatableTransform transform = AnimatableTransform.Factory.newInstance(json.optJSONObject("ks"),
+ composition);
+ MatteType matteType = MatteType.values()[json.optInt("tt")];
+ List<Object> shapes = new ArrayList<>();
+ List<Mask> masks = new ArrayList<>();
+ List<Keyframe<Float>> inOutKeyframes = new ArrayList<>();
+ JSONArray jsonMasks = json.optJSONArray("masksProperties");
+ if (jsonMasks != null) {
+ for (int i = 0; i < jsonMasks.length(); i++) {
+ Mask mask = Mask.Factory.newMask(jsonMasks.optJSONObject(i), composition);
+ masks.add(mask);
+ }
+ }
+
+ JSONArray shapesJson = json.optJSONArray("shapes");
+ if (shapesJson != null) {
+ for (int i = 0; i < shapesJson.length(); i++) {
+ Object shape = ShapeGroup.shapeItemWithJson(shapesJson.optJSONObject(i), composition);
+ if (shape != null) {
+ shapes.add(shape);
+ }
+ }
+ }
+
+ float timeStretch = (float) json.optDouble("sr", 1.0);
+ float startFrame = (float) json.optDouble("st");
+ float frames = composition.getDurationFrames();
+ float startProgress = startFrame / frames;
+
+ if (layerType == LayerType.PreComp) {
+ preCompWidth = (int) (json.optInt("w") * composition.getScale());
+ preCompHeight = (int) (json.optInt("h") * composition.getScale());
+ }
+
+ long inFrame = json.optLong("ip");
+ long outFrame = json.optLong("op");
+
+ // Before the in frame
+ if (inFrame > 0) {
+ Keyframe<Float> preKeyframe = new Keyframe<>(composition, 0, inFrame);
+ preKeyframe.startValue = 0f;
+ preKeyframe.endValue = 0f;
+ inOutKeyframes.add(preKeyframe);
+ }
+
+ // The + 1 is because the animation should be visible on the out frame itself.
+ outFrame = (outFrame > 0 ? outFrame : composition.getEndFrame() + 1);
+ Keyframe<Float> visibleKeyframe =
+ new Keyframe<>(composition, inFrame, outFrame);
+ visibleKeyframe.startValue = 1f;
+ visibleKeyframe.endValue = 1f;
+ inOutKeyframes.add(visibleKeyframe);
+
+ if (outFrame <= composition.getDurationFrames()) {
+ Keyframe<Float> outKeyframe =
+ new Keyframe<>(composition, outFrame, composition.getEndFrame());
+ outKeyframe.startValue = 0f;
+ outKeyframe.endValue = 0f;
+ inOutKeyframes.add(outKeyframe);
+ }
+
+ return new Layer(shapes, composition, layerName, layerId, layerType, parentId, refId,
+ masks, transform, solidWidth, solidHeight, solidColor, timeStretch, startProgress,
+ preCompWidth, preCompHeight, inOutKeyframes, matteType);
+ }
+ }
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/LayerView.java b/lottie/src/main/java/com/airbnb/lottie/LayerView.java
index 082551b..e2b7c90 100644
--- a/lottie/src/main/java/com/airbnb/lottie/LayerView.java
+++ b/lottie/src/main/java/com/airbnb/lottie/LayerView.java
@@ -138,24 +138,24 @@
ShapePath shapePath = (ShapePath) item;
ShapeLayerView shapeLayer =
new ShapeLayerView(shapePath, currentFill, currentStroke, currentTrim,
- new AnimatableTransform(composition), getCallback());
+ AnimatableTransform.Factory.newInstance(composition), getCallback());
addLayer(shapeLayer);
} else if (item instanceof RectangleShape) {
RectangleShape shapeRect = (RectangleShape) item;
RectLayer shapeLayer =
new RectLayer(shapeRect, currentFill, currentStroke, currentTrim,
- new AnimatableTransform(composition), getCallback());
+ AnimatableTransform.Factory.newInstance(composition), getCallback());
addLayer(shapeLayer);
} else if (item instanceof CircleShape) {
CircleShape shapeCircle = (CircleShape) item;
EllipseLayer shapeLayer =
new EllipseLayer(shapeCircle, currentFill, currentStroke, currentTrim,
- new AnimatableTransform(composition), getCallback());
+ AnimatableTransform.Factory.newInstance(composition), getCallback());
addLayer(shapeLayer);
} else if (item instanceof PolystarShape) {
PolystarShape polystarShape = (PolystarShape) item;
PolystarLayer shapeLayer = new PolystarLayer(polystarShape, currentFill, currentStroke,
- currentTrim, new AnimatableTransform(composition), getCallback());
+ currentTrim, AnimatableTransform.Factory.newInstance(composition), getCallback());
addLayer(shapeLayer);
}
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/LottieComposition.java b/lottie/src/main/java/com/airbnb/lottie/LottieComposition.java
index 1497774..4105b9f 100644
--- a/lottie/src/main/java/com/airbnb/lottie/LottieComposition.java
+++ b/lottie/src/main/java/com/airbnb/lottie/LottieComposition.java
@@ -134,13 +134,13 @@
}
static LottieComposition fromFileSync(Context context, String fileName) {
- InputStream file;
+ InputStream stream;
try {
- file = context.getAssets().open(fileName);
+ stream = context.getAssets().open(fileName);
} catch (IOException e) {
throw new IllegalStateException("Unable to find file " + fileName, e);
}
- return fromInputStream(context.getResources(), file);
+ return fromInputStream(context.getResources(), stream);
}
/**
@@ -209,7 +209,7 @@
JSONArray jsonLayers = json.optJSONArray("layers");
int length = jsonLayers.length();
for (int i = 0; i < length; i++) {
- Layer layer = new Layer(jsonLayers.optJSONObject(i), composition);
+ Layer layer = Layer.Factory.newInstance(jsonLayers.optJSONObject(i), composition);
addLayer(composition.layers, composition.layerMap, layer);
}
}
@@ -225,7 +225,7 @@
List<Layer> layers = new ArrayList<>(layersJson.length());
LongSparseArray<Layer> layerMap = new LongSparseArray<>();
for (int j = 0; j < layersJson.length(); j++) {
- Layer layer = new Layer(layersJson.optJSONObject(j), composition);
+ Layer layer = Layer.Factory.newInstance(layersJson.optJSONObject(j), composition);
layerMap.put(layer.getId(), layer);
layers.add(layer);
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/Mask.java b/lottie/src/main/java/com/airbnb/lottie/Mask.java
index 0feb6b3..8eb8ba8 100644
--- a/lottie/src/main/java/com/airbnb/lottie/Mask.java
+++ b/lottie/src/main/java/com/airbnb/lottie/Mask.java
@@ -13,31 +13,40 @@
private final MaskMode maskMode;
private final AnimatableShapeValue maskPath;
- Mask(JSONObject json, LottieComposition composition) {
- String mode = json.optString("mode");
- switch (mode) {
- case "a":
- maskMode = MaskMode.MaskModeAdd;
- break;
- case "s":
- maskMode = MaskMode.MaskModeSubtract;
- break;
- case "i":
- maskMode = MaskMode.MaskModeIntersect;
- break;
- default:
- maskMode = MaskMode.MaskModeUnknown;
- }
-
- maskPath = new AnimatableShapeValue(json.optJSONObject("pt"), composition);
- // TODO: use this
- // JSONObject opacityJson = json.optJSONObject("o");
- // if (opacityJson != null) {
- // AnimatableIntegerValue opacity =
- // new AnimatableIntegerValue(opacityJson, composition, false, true);
- // }
+ private Mask(MaskMode maskMode, AnimatableShapeValue maskPath) {
+ this.maskMode = maskMode;
+ this.maskPath = maskPath;
}
+ static class Factory {
+ static Mask newMask(JSONObject json, LottieComposition composition) {
+ MaskMode maskMode;
+ switch (json.optString("mode")) {
+ case "a":
+ maskMode = MaskMode.MaskModeAdd;
+ break;
+ case "s":
+ maskMode = MaskMode.MaskModeSubtract;
+ break;
+ case "i":
+ maskMode = MaskMode.MaskModeIntersect;
+ break;
+ default:
+ maskMode = MaskMode.MaskModeUnknown;
+ }
+
+ AnimatableShapeValue maskPath = new AnimatableShapeValue(json.optJSONObject("pt"),
+ composition);
+ // TODO: use this
+ // JSONObject opacityJson = json.optJSONObject("o");
+ // if (opacityJson != null) {
+ // AnimatableIntegerValue opacity =
+ // new AnimatableIntegerValue(opacityJson, composition, false, true);
+ // }
+
+ return new Mask(maskMode, maskPath);
+ }
+ }
@SuppressWarnings("unused") MaskMode getMaskMode() {
return maskMode;
diff --git a/lottie/src/main/java/com/airbnb/lottie/PathKeyframe.java b/lottie/src/main/java/com/airbnb/lottie/PathKeyframe.java
index 4d2c617..a186a7b 100644
--- a/lottie/src/main/java/com/airbnb/lottie/PathKeyframe.java
+++ b/lottie/src/main/java/com/airbnb/lottie/PathKeyframe.java
@@ -8,10 +8,8 @@
import org.json.JSONObject;
class PathKeyframe extends Keyframe<PointF> {
-
@Nullable private Path path;
-
PathKeyframe(JSONObject json, LottieComposition composition,
AnimatableValue<PointF, ?> animatableValue) {
super(json, composition, composition.getScale(), animatableValue);
@@ -29,11 +27,8 @@
}
}
- /**
- * This will be null if the startValue and endValue are the same.
- */
- @Nullable
- Path getPath() {
+ /** This will be null if the startValue and endValue are the same. */
+ @Nullable Path getPath() {
return path;
}
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/PolystarShape.java b/lottie/src/main/java/com/airbnb/lottie/PolystarShape.java
index 98b1b91..c646d7f 100644
--- a/lottie/src/main/java/com/airbnb/lottie/PolystarShape.java
+++ b/lottie/src/main/java/com/airbnb/lottie/PolystarShape.java
@@ -32,22 +32,45 @@
private final AnimatableFloatValue innerRoundedness;
private final AnimatableFloatValue outerRoundedness;
- PolystarShape(JSONObject json, LottieComposition composition) {
- type = Type.forValue(json.optInt("sy"));
- points = new AnimatableFloatValue(json.optJSONObject("pt"), composition, false);
- position = AnimatablePathValue.createAnimatablePathOrSplitDimensionPath(
- json.optJSONObject("p"), composition);
- rotation = new AnimatableFloatValue(json.optJSONObject("r"), composition, false);
+ private PolystarShape(Type type, AnimatableFloatValue points, IAnimatablePathValue position,
+ AnimatableFloatValue rotation, AnimatableFloatValue innerRadius,
+ AnimatableFloatValue outerRadius, AnimatableFloatValue innerRoundedness,
+ AnimatableFloatValue outerRoundedness) {
+ this.type = type;
+ this.points = points;
+ this.position = position;
+ this.rotation = rotation;
+ this.innerRadius = innerRadius;
+ this.outerRadius = outerRadius;
+ this.innerRoundedness = innerRoundedness;
+ this.outerRoundedness = outerRoundedness;
+ }
- outerRadius = new AnimatableFloatValue(json.optJSONObject("or"), composition);
- outerRoundedness = new AnimatableFloatValue(json.optJSONObject("os"), composition, false);
+ static class Factory {
+ static PolystarShape newInstance(JSONObject json, LottieComposition composition) {
+ Type type = Type.forValue(json.optInt("sy"));
+ AnimatableFloatValue points =
+ new AnimatableFloatValue(json.optJSONObject("pt"), composition, false);
+ IAnimatablePathValue position = AnimatablePathValue.createAnimatablePathOrSplitDimensionPath(
+ json.optJSONObject("p"), composition);
+ AnimatableFloatValue rotation =
+ new AnimatableFloatValue(json.optJSONObject("r"), composition, false);
+ AnimatableFloatValue outerRadius =
+ new AnimatableFloatValue(json.optJSONObject("or"), composition);
+ AnimatableFloatValue outerRoundedness =
+ new AnimatableFloatValue(json.optJSONObject("os"), composition, false);
+ AnimatableFloatValue innerRadius;
+ AnimatableFloatValue innerRoundedness;
- if (type == Type.Star) {
- innerRadius = new AnimatableFloatValue(json.optJSONObject("ir"), composition);
- innerRoundedness = new AnimatableFloatValue(json.optJSONObject("is"), composition, false);
- } else {
- innerRadius = null;
- innerRoundedness = null;
+ if (type == Type.Star) {
+ innerRadius = new AnimatableFloatValue(json.optJSONObject("ir"), composition);
+ innerRoundedness = new AnimatableFloatValue(json.optJSONObject("is"), composition, false);
+ } else {
+ innerRadius = null;
+ innerRoundedness = null;
+ }
+ return new PolystarShape(type, points, position, rotation, innerRadius, outerRadius,
+ innerRoundedness, outerRoundedness);
}
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/RectangleShape.java b/lottie/src/main/java/com/airbnb/lottie/RectangleShape.java
index 060dc07..04f8dc6 100644
--- a/lottie/src/main/java/com/airbnb/lottie/RectangleShape.java
+++ b/lottie/src/main/java/com/airbnb/lottie/RectangleShape.java
@@ -3,17 +3,25 @@
import org.json.JSONObject;
class RectangleShape {
- private static final String TAG = RectangleShape.class.getSimpleName();
-
private final IAnimatablePathValue position;
private final AnimatablePointValue size;
private final AnimatableFloatValue cornerRadius;
- RectangleShape(JSONObject json, LottieComposition composition) {
- position = AnimatablePathValue.createAnimatablePathOrSplitDimensionPath(
- json.optJSONObject("p"), composition);
- cornerRadius = new AnimatableFloatValue(json.optJSONObject("r"), composition);
- size = new AnimatablePointValue(json.optJSONObject("s"), composition);
+ private RectangleShape(IAnimatablePathValue position, AnimatablePointValue size,
+ AnimatableFloatValue cornerRadius) {
+ this.position = position;
+ this.size = size;
+ this.cornerRadius = cornerRadius;
+ }
+
+ static class Factory {
+ static RectangleShape newInstance(JSONObject json, LottieComposition composition) {
+ return new RectangleShape(
+ AnimatablePathValue.createAnimatablePathOrSplitDimensionPath(
+ json.optJSONObject("p"), composition),
+ new AnimatablePointValue(json.optJSONObject("s"), composition),
+ new AnimatableFloatValue(json.optJSONObject("r"), composition));
+ }
}
AnimatableFloatValue getCornerRadius() {
diff --git a/lottie/src/main/java/com/airbnb/lottie/ShapeFill.java b/lottie/src/main/java/com/airbnb/lottie/ShapeFill.java
index ac80362..691a79c 100644
--- a/lottie/src/main/java/com/airbnb/lottie/ShapeFill.java
+++ b/lottie/src/main/java/com/airbnb/lottie/ShapeFill.java
@@ -1,36 +1,52 @@
package com.airbnb.lottie;
+import android.support.annotation.Nullable;
+
import org.json.JSONObject;
class ShapeFill {
- private boolean fillEnabled;
- private AnimatableColorValue color;
- private AnimatableIntegerValue opacity;
+ private final boolean fillEnabled;
+ @Nullable private final AnimatableColorValue color;
+ @Nullable private final AnimatableIntegerValue opacity;
- ShapeFill(JSONObject json, LottieComposition composition) {
- JSONObject jsonColor = json.optJSONObject("c");
- if (jsonColor != null) {
- color = new AnimatableColorValue(jsonColor, composition);
- }
-
- JSONObject jsonOpacity = json.optJSONObject("o");
- if (jsonOpacity != null) {
- opacity = new AnimatableIntegerValue(jsonOpacity, composition, false, true);
- }
- fillEnabled = json.optBoolean("fillEnabled");
+ private ShapeFill(boolean fillEnabled, @Nullable AnimatableColorValue color, @Nullable
+ AnimatableIntegerValue opacity) {
+ this.fillEnabled = fillEnabled;
+ this.color = color;
+ this.opacity = opacity;
}
- public AnimatableColorValue getColor() {
+ static class Factory {
+ static ShapeFill newInstance(JSONObject json, LottieComposition composition) {
+ AnimatableColorValue color = null;
+ boolean fillEnabled;
+ AnimatableIntegerValue opacity = null;
+
+ JSONObject jsonColor = json.optJSONObject("c");
+ if (jsonColor != null) {
+ color = new AnimatableColorValue(jsonColor, composition);
+ }
+
+ JSONObject jsonOpacity = json.optJSONObject("o");
+ if (jsonOpacity != null) {
+ opacity = new AnimatableIntegerValue(jsonOpacity, composition, false, true);
+ }
+ fillEnabled = json.optBoolean("fillEnabled");
+ return new ShapeFill(fillEnabled, color, opacity);
+ }
+ }
+
+ @Nullable public AnimatableColorValue getColor() {
return color;
}
- public AnimatableIntegerValue getOpacity() {
+ @Nullable public AnimatableIntegerValue getOpacity() {
return opacity;
}
@Override
public String toString() {
- return "ShapeFill{" + "color=" + Integer.toHexString(color.getInitialValue()) +
+ return "ShapeFill{" + "color=" + Integer.toHexString(color.getInitialValue()) +
", fillEnabled=" + fillEnabled +
", opacity=" + opacity.getInitialValue() +
'}';
diff --git a/lottie/src/main/java/com/airbnb/lottie/ShapeGroup.java b/lottie/src/main/java/com/airbnb/lottie/ShapeGroup.java
index bf13ba9..adc1d73 100644
--- a/lottie/src/main/java/com/airbnb/lottie/ShapeGroup.java
+++ b/lottie/src/main/java/com/airbnb/lottie/ShapeGroup.java
@@ -11,48 +11,55 @@
import java.util.List;
class ShapeGroup {
-
- @Nullable
- static Object shapeItemWithJson(JSONObject json, LottieComposition composition) {
+ @Nullable static Object shapeItemWithJson(JSONObject json, LottieComposition composition) {
String type = json.optString("ty");
switch (type) {
case "gr":
- return new ShapeGroup(json, composition);
+ return ShapeGroup.Factory.newInstance(json, composition);
case "st":
- return new ShapeStroke(json, composition);
+ return ShapeStroke.Factory.newInstance(json, composition);
case "fl":
- return new ShapeFill(json, composition);
+ return ShapeFill.Factory.newInstance(json, composition);
case "tr":
- return new AnimatableTransform(json, composition);
+ return AnimatableTransform.Factory.newInstance(json, composition);
case "sh":
- return new ShapePath(json, composition);
+ return ShapePath.Factory.newInstance(json, composition);
case "el":
- return new CircleShape(json, composition);
+ return CircleShape.Factory.newInstance(json, composition);
case "rc":
- return new RectangleShape(json, composition);
+ return RectangleShape.Factory.newInstance(json, composition);
case "tm":
- return new ShapeTrimPath(json, composition);
+ return ShapeTrimPath.Factory.newInstance(json, composition);
case "sr":
- return new PolystarShape(json, composition);
+ return PolystarShape.Factory.newInstance(json, composition);
default:
Log.w(L.TAG, "Unknown shape type " + type);
}
return null;
}
- private String name;
- private final List<Object> items = new ArrayList<>();
+ private final String name;
+ private final List<Object> items;
- private ShapeGroup(JSONObject json, LottieComposition composition) {
- JSONArray jsonItems = json.optJSONArray("it");
- name = json.optString("nm");
+ private ShapeGroup(String name, List<Object> items) {
+ this.name = name;
+ this.items = items;
+ }
- for (int i = 0; i < jsonItems.length(); i++) {
- Object newItem = shapeItemWithJson(jsonItems.optJSONObject(i), composition);
- if (newItem != null) {
- items.add(newItem);
+ static class Factory {
+ private static ShapeGroup newInstance(JSONObject json, LottieComposition composition) {
+ JSONArray jsonItems = json.optJSONArray("it");
+ String name = json.optString("nm");
+ List<Object> items = new ArrayList<>();
+
+ for (int i = 0; i < jsonItems.length(); i++) {
+ Object newItem = shapeItemWithJson(jsonItems.optJSONObject(i), composition);
+ if (newItem != null) {
+ items.add(newItem);
+ }
}
+ return new ShapeGroup(name, items);
}
}
@@ -60,8 +67,7 @@
return items;
}
- @Override
- public String toString() {
+ @Override public String toString() {
return "ShapeGroup{" + "name='" + name + "\' Shapes: " + Arrays.toString(items.toArray()) + '}';
}
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/ShapePath.java b/lottie/src/main/java/com/airbnb/lottie/ShapePath.java
index 7f1a5dc..96672bf 100644
--- a/lottie/src/main/java/com/airbnb/lottie/ShapePath.java
+++ b/lottie/src/main/java/com/airbnb/lottie/ShapePath.java
@@ -1,20 +1,23 @@
package com.airbnb.lottie;
-import android.util.Log;
-
import org.json.JSONObject;
class ShapePath {
- private static final String TAG = ShapePath.class.getSimpleName();
-
private final String name;
private final int index;
- private AnimatableShapeValue shapePath;
+ private final AnimatableShapeValue shapePath;
- ShapePath(JSONObject json, LottieComposition composition) {
- index = json.optInt("ind");
- name = json.optString("nm");
- shapePath = new AnimatableShapeValue(json.optJSONObject("ks"), composition);
+ private ShapePath(String name, int index, AnimatableShapeValue shapePath) {
+ this.name = name;
+ this.index = index;
+ this.shapePath = shapePath;
+ }
+
+ static class Factory {
+ static ShapePath newInstance(JSONObject json, LottieComposition composition) {
+ return new ShapePath(json.optString("nm"), json.optInt("ind"),
+ new AnimatableShapeValue(json.optJSONObject("ks"), composition));
+ }
}
AnimatableShapeValue getShapePath() {
diff --git a/lottie/src/main/java/com/airbnb/lottie/ShapeStroke.java b/lottie/src/main/java/com/airbnb/lottie/ShapeStroke.java
index f6ce682..7dfff4f 100644
--- a/lottie/src/main/java/com/airbnb/lottie/ShapeStroke.java
+++ b/lottie/src/main/java/com/airbnb/lottie/ShapeStroke.java
@@ -1,5 +1,7 @@
package com.airbnb.lottie;
+import android.support.annotation.Nullable;
+
import org.json.JSONArray;
import org.json.JSONObject;
@@ -19,42 +21,57 @@
Bevel
}
- private AnimatableFloatValue offset;
- private final List<AnimatableFloatValue> lineDashPattern = new ArrayList<>();
-
+ @Nullable private final AnimatableFloatValue offset;
+ private final List<AnimatableFloatValue> lineDashPattern;
private final AnimatableColorValue color;
private final AnimatableIntegerValue opacity;
private final AnimatableFloatValue width;
private final LineCapType capType;
private final LineJoinType joinType;
- ShapeStroke(JSONObject json, LottieComposition composition) {
- color = new AnimatableColorValue(json.optJSONObject("c"), composition);
+ private ShapeStroke(@Nullable AnimatableFloatValue offset,
+ List<AnimatableFloatValue> lineDashPattern, AnimatableColorValue color,
+ AnimatableIntegerValue opacity, AnimatableFloatValue width, LineCapType capType,
+ LineJoinType joinType) {
+ this.offset = offset;
+ this.lineDashPattern = lineDashPattern;
+ this.color = color;
+ this.opacity = opacity;
+ this.width = width;
+ this.capType = capType;
+ this.joinType = joinType;
+ }
- width = new AnimatableFloatValue(json.optJSONObject("w"), composition);
+ static class Factory {
+ static ShapeStroke newInstance(JSONObject json, LottieComposition composition) {
+ List<AnimatableFloatValue> lineDashPattern = new ArrayList<>();
+ AnimatableColorValue color = new AnimatableColorValue(json.optJSONObject("c"), composition);
+ AnimatableFloatValue width = new AnimatableFloatValue(json.optJSONObject("w"), composition);
+ AnimatableIntegerValue opacity = new AnimatableIntegerValue(json.optJSONObject("o"),
+ composition, false, true);
+ LineCapType capType = LineCapType.values()[json.optInt("lc") - 1];
+ LineJoinType joinType = LineJoinType.values()[json.optInt("lj") - 1];
+ AnimatableFloatValue offset = null;
- opacity = new AnimatableIntegerValue(json.optJSONObject("o"), composition, false, true);
-
- capType = LineCapType.values()[json.optInt("lc") - 1];
- joinType = LineJoinType.values()[json.optInt("lj") - 1];
-
- if (json.has("d")) {
- JSONArray dashesJson = json.optJSONArray("d");
- for (int i = 0; i < dashesJson.length(); i++) {
- JSONObject dashJson = dashesJson.optJSONObject(i);
- String n = dashJson.optString("n");
- if (n.equals("o")) {
- JSONObject value = dashJson.optJSONObject("v");
- offset = new AnimatableFloatValue(value, composition);
- } else if (n.equals("d") || n.equals("g")) {
- JSONObject value = dashJson.optJSONObject("v");
- lineDashPattern.add(new AnimatableFloatValue(value, composition));
+ if (json.has("d")) {
+ JSONArray dashesJson = json.optJSONArray("d");
+ for (int i = 0; i < dashesJson.length(); i++) {
+ JSONObject dashJson = dashesJson.optJSONObject(i);
+ String n = dashJson.optString("n");
+ if (n.equals("o")) {
+ JSONObject value = dashJson.optJSONObject("v");
+ offset = new AnimatableFloatValue(value, composition);
+ } else if (n.equals("d") || n.equals("g")) {
+ JSONObject value = dashJson.optJSONObject("v");
+ lineDashPattern.add(new AnimatableFloatValue(value, composition));
+ }
+ }
+ if (lineDashPattern.size() == 1) {
+ // If there is only 1 value then it is assumed to be equal parts on and off.
+ lineDashPattern.add(lineDashPattern.get(0));
}
}
- if (lineDashPattern.size() == 1) {
- // If there is only 1 value then it is assumed to be equal parts on and off.
- lineDashPattern.add(lineDashPattern.get(0));
- }
+ return new ShapeStroke(offset, lineDashPattern, color, opacity, width, capType, joinType);
}
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/ShapeTrimPath.java b/lottie/src/main/java/com/airbnb/lottie/ShapeTrimPath.java
index 19128e8..570684f 100644
--- a/lottie/src/main/java/com/airbnb/lottie/ShapeTrimPath.java
+++ b/lottie/src/main/java/com/airbnb/lottie/ShapeTrimPath.java
@@ -7,10 +7,20 @@
private final AnimatableFloatValue end;
private final AnimatableFloatValue offset;
- ShapeTrimPath(JSONObject json, LottieComposition composition) {
- start = new AnimatableFloatValue(json.optJSONObject("s"), composition, false);
- end = new AnimatableFloatValue(json.optJSONObject("e"), composition, false);
- offset = new AnimatableFloatValue(json.optJSONObject("o"), composition, false);
+ private ShapeTrimPath(AnimatableFloatValue start, AnimatableFloatValue end, AnimatableFloatValue
+ offset) {
+ this.start = start;
+ this.end = end;
+ this.offset = offset;
+ }
+
+ static class Factory {
+ static ShapeTrimPath newInstance(JSONObject json, LottieComposition composition) {
+ return new ShapeTrimPath(
+ new AnimatableFloatValue(json.optJSONObject("s"), composition, false),
+ new AnimatableFloatValue(json.optJSONObject("e"), composition, false),
+ new AnimatableFloatValue(json.optJSONObject("o"), composition, false));
+ }
}
AnimatableFloatValue getEnd() {