summaryrefslogtreecommitdiff
path: root/graphics/java
diff options
context:
space:
mode:
Diffstat (limited to 'graphics/java')
-rw-r--r--graphics/java/android/graphics/Bitmap.java7
-rw-r--r--graphics/java/android/graphics/Canvas.java10
-rw-r--r--graphics/java/android/graphics/Paint.java6
-rw-r--r--graphics/java/android/graphics/Path.java7
-rw-r--r--graphics/java/android/graphics/PathDashPathEffect.java2
-rw-r--r--graphics/java/android/graphics/PathMeasure.java7
-rw-r--r--graphics/java/android/graphics/Picture.java13
-rw-r--r--graphics/java/android/graphics/Region.java6
-rw-r--r--graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java280
-rw-r--r--graphics/java/android/graphics/drawable/GradientDrawable.java2
-rw-r--r--graphics/java/android/graphics/drawable/VectorDrawable.java333
11 files changed, 561 insertions, 112 deletions
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 1fdc1f575bd4..49721cf42666 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -1667,10 +1667,10 @@ public final class Bitmap implements Parcelable {
* and therefore is harmless.
*/
public void prepareToDraw() {
- // TODO: Consider having this start an async upload?
- // With inPurgeable no-op'd there's currently no use for this
- // method, but it could have interesting future uses.
checkRecycled("Can't prepareToDraw on a recycled bitmap!");
+ // Kick off an update/upload of the bitmap outside of the normal
+ // draw path.
+ nativePrepareToDraw(mNativePtr);
}
/**
@@ -1741,4 +1741,5 @@ public final class Bitmap implements Parcelable {
private static native void nativeSetHasMipMap(long nativeBitmap, boolean hasMipMap);
private static native boolean nativeSameAs(long nativeBitmap0, long nativeBitmap1);
private static native long nativeRefPixelRef(long nativeBitmap);
+ private static native void nativePrepareToDraw(long nativeBitmap);
}
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index cb6c92e87172..40b877de131c 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -788,7 +788,7 @@ public class Canvas {
* @return true if the resulting is non-empty
*/
public boolean clipPath(@NonNull Path path, @NonNull Region.Op op) {
- return native_clipPath(mNativeCanvasWrapper, path.ni(), op.nativeInt);
+ return native_clipPath(mNativeCanvasWrapper, path.readOnlyNI(), op.nativeInt);
}
/**
@@ -907,7 +907,7 @@ public class Canvas {
* does not intersect with the canvas' clip
*/
public boolean quickReject(@NonNull Path path, @NonNull EdgeType type) {
- return native_quickReject(mNativeCanvasWrapper, path.ni());
+ return native_quickReject(mNativeCanvasWrapper, path.readOnlyNI());
}
/**
@@ -1259,7 +1259,7 @@ public class Canvas {
if (path.isSimplePath && path.rects != null) {
native_drawRegion(mNativeCanvasWrapper, path.rects.mNativeRegion, paint.getNativeInstance());
} else {
- native_drawPath(mNativeCanvasWrapper, path.ni(), paint.getNativeInstance());
+ native_drawPath(mNativeCanvasWrapper, path.readOnlyNI(), paint.getNativeInstance());
}
}
@@ -1895,7 +1895,7 @@ public class Canvas {
throw new ArrayIndexOutOfBoundsException();
}
native_drawTextOnPath(mNativeCanvasWrapper, text, index, count,
- path.ni(), hOffset, vOffset,
+ path.readOnlyNI(), hOffset, vOffset,
paint.mBidiFlags, paint.getNativeInstance(), paint.mNativeTypeface);
}
@@ -1915,7 +1915,7 @@ public class Canvas {
public void drawTextOnPath(@NonNull String text, @NonNull Path path, float hOffset,
float vOffset, @NonNull Paint paint) {
if (text.length() > 0) {
- native_drawTextOnPath(mNativeCanvasWrapper, text, path.ni(), hOffset, vOffset,
+ native_drawTextOnPath(mNativeCanvasWrapper, text, path.readOnlyNI(), hOffset, vOffset,
paint.mBidiFlags, paint.getNativeInstance(), paint.mNativeTypeface);
}
}
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 054e29fd9ff4..cfbe672a4cd6 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -1021,7 +1021,7 @@ public class Paint {
* drawn with a hairline (width == 0)
*/
public boolean getFillPath(Path src, Path dst) {
- return nGetFillPath(mNativePaint, src.ni(), dst.ni());
+ return nGetFillPath(mNativePaint, src.readOnlyNI(), dst.mutateNI());
}
/**
@@ -2394,7 +2394,7 @@ public class Paint {
throw new ArrayIndexOutOfBoundsException();
}
nGetTextPath(mNativePaint, mNativeTypeface, mBidiFlags, text, index, count, x, y,
- path.ni());
+ path.mutateNI());
}
/**
@@ -2416,7 +2416,7 @@ public class Paint {
throw new IndexOutOfBoundsException();
}
nGetTextPath(mNativePaint, mNativeTypeface, mBidiFlags, text, start, end, x, y,
- path.ni());
+ path.mutateNI());
}
/**
diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java
index de391af5d4ce..be31bbe0c086 100644
--- a/graphics/java/android/graphics/Path.java
+++ b/graphics/java/android/graphics/Path.java
@@ -775,7 +775,12 @@ public class Path {
}
}
- final long ni() {
+ final long readOnlyNI() {
+ return mNativePath;
+ }
+
+ final long mutateNI() {
+ isSimplePath = false;
return mNativePath;
}
diff --git a/graphics/java/android/graphics/PathDashPathEffect.java b/graphics/java/android/graphics/PathDashPathEffect.java
index 4f43f680ff3e..2b6a6edcc266 100644
--- a/graphics/java/android/graphics/PathDashPathEffect.java
+++ b/graphics/java/android/graphics/PathDashPathEffect.java
@@ -41,7 +41,7 @@ public class PathDashPathEffect extends PathEffect {
*/
public PathDashPathEffect(Path shape, float advance, float phase,
Style style) {
- native_instance = nativeCreate(shape.ni(), advance, phase,
+ native_instance = nativeCreate(shape.readOnlyNI(), advance, phase,
style.native_style);
}
diff --git a/graphics/java/android/graphics/PathMeasure.java b/graphics/java/android/graphics/PathMeasure.java
index 2848949eb72f..78d892e94cf0 100644
--- a/graphics/java/android/graphics/PathMeasure.java
+++ b/graphics/java/android/graphics/PathMeasure.java
@@ -50,7 +50,7 @@ public class PathMeasure {
public PathMeasure(Path path, boolean forceClosed) {
// The native implementation does not copy the path, prevent it from being GC'd
mPath = path;
- native_instance = native_create(path != null ? path.ni() : 0,
+ native_instance = native_create(path != null ? path.readOnlyNI() : 0,
forceClosed);
}
@@ -60,7 +60,7 @@ public class PathMeasure {
public void setPath(Path path, boolean forceClosed) {
mPath = path;
native_setPath(native_instance,
- path != null ? path.ni() : 0,
+ path != null ? path.readOnlyNI() : 0,
forceClosed);
}
@@ -134,8 +134,7 @@ public class PathMeasure {
return false;
}
- dst.isSimplePath = false;
- return native_getSegment(native_instance, startD, stopD, dst.ni(), startWithMoveTo);
+ return native_getSegment(native_instance, startD, stopD, dst.mutateNI(), startWithMoveTo);
}
/**
diff --git a/graphics/java/android/graphics/Picture.java b/graphics/java/android/graphics/Picture.java
index 28d869063225..08eeaff69f9b 100644
--- a/graphics/java/android/graphics/Picture.java
+++ b/graphics/java/android/graphics/Picture.java
@@ -21,11 +21,14 @@ import java.io.OutputStream;
/**
* A Picture records drawing calls (via the canvas returned by beginRecording)
- * and can then play them back into Canvas (via {@link Picture#draw(Canvas)} or
+ * and can then play them back into Canvas (via {@link Picture#draw(Canvas)} or
* {@link Canvas#drawPicture(Picture)}).For most content (e.g. text, lines, rectangles),
* drawing a sequence from a picture can be faster than the equivalent API
* calls, since the picture performs its playback without incurring any
* method-call overhead.
+ *
+ * <p class="note"><strong>Note:</strong> Prior to API level 23 a picture cannot
+ * be replayed on a hardware accelerated canvas.</p>
*/
public class Picture {
private Canvas mRecordingCanvas;
@@ -135,10 +138,6 @@ public class Picture {
* have been persisted across device restarts are not guaranteed to decode
* properly and are highly discouraged.
*
- * <p>
- * <strong>Note:</strong> Prior to API level 23 a picture created from an
- * input stream cannot be replayed on a hardware accelerated canvas.
- *
* @see #writeToStream(java.io.OutputStream)
* @deprecated The recommended alternative is to not use writeToStream and
* instead draw the picture into a Bitmap from which you can persist it as
@@ -155,10 +154,6 @@ public class Picture {
* The resulting stream is NOT to be persisted across device restarts as
* there is no guarantee that the Picture can be successfully reconstructed.
*
- * <p>
- * <strong>Note:</strong> Prior to API level 23 a picture created from an
- * input stream cannot be replayed on a hardware accelerated canvas.
- *
* @see #createFromStream(java.io.InputStream)
* @deprecated The recommended alternative is to draw the picture into a
* Bitmap from which you can persist it as raw or compressed pixels.
diff --git a/graphics/java/android/graphics/Region.java b/graphics/java/android/graphics/Region.java
index de89ad07d873..dca6d9ed3b2f 100644
--- a/graphics/java/android/graphics/Region.java
+++ b/graphics/java/android/graphics/Region.java
@@ -110,7 +110,7 @@ public class Region implements Parcelable {
* (with no antialiasing).
*/
public boolean setPath(Path path, Region clip) {
- return nativeSetPath(mNativeRegion, path.ni(), clip.mNativeRegion);
+ return nativeSetPath(mNativeRegion, path.readOnlyNI(), clip.mNativeRegion);
}
/**
@@ -155,7 +155,7 @@ public class Region implements Parcelable {
*/
public Path getBoundaryPath() {
Path path = new Path();
- nativeGetBoundaryPath(mNativeRegion, path.ni());
+ nativeGetBoundaryPath(mNativeRegion, path.mutateNI());
return path;
}
@@ -164,7 +164,7 @@ public class Region implements Parcelable {
* path will also be empty.
*/
public boolean getBoundaryPath(Path path) {
- return nativeGetBoundaryPath(mNativeRegion, path.ni());
+ return nativeGetBoundaryPath(mNativeRegion, path.mutateNI());
}
/**
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index ee6780722f0c..c836204486b0 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -46,6 +46,7 @@ import android.util.IntArray;
import android.util.Log;
import android.util.LongArray;
import android.util.PathParser;
+import android.util.Property;
import android.util.TimeUtils;
import android.view.Choreographer;
import android.view.DisplayListCanvas;
@@ -157,7 +158,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
private static final boolean DBG_ANIMATION_VECTOR_DRAWABLE = false;
/** Local, mutable animator set. */
- private VectorDrawableAnimator mAnimatorSet = new VectorDrawableAnimatorUI(this);
+ private VectorDrawableAnimator mAnimatorSet;
/**
* The resources against which this drawable was created. Used to attempt
@@ -182,6 +183,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
private AnimatedVectorDrawable(AnimatedVectorDrawableState state, Resources res) {
mAnimatedVectorState = new AnimatedVectorDrawableState(state, mCallback, res);
+ mAnimatorSet = new VectorDrawableAnimatorRT(this);
mRes = res;
}
@@ -237,6 +239,14 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
@Override
public void draw(Canvas canvas) {
+ if (!canvas.isHardwareAccelerated() && mAnimatorSet instanceof VectorDrawableAnimatorRT) {
+ // If we have SW canvas and the RT animation is waiting to start, We need to fallback
+ // to UI thread animation for AVD.
+ if (!mAnimatorSet.isRunning() &&
+ ((VectorDrawableAnimatorRT) mAnimatorSet).mPendingAnimationActions.size() > 0) {
+ fallbackOntoUI();
+ }
+ }
mAnimatorSet.onDraw(canvas);
mAnimatedVectorState.mVectorDrawable.draw(canvas);
}
@@ -390,9 +400,12 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
R.styleable.AnimatedVectorDrawableTarget_animation, 0);
if (animResId != 0) {
if (theme != null) {
- final Animator objectAnimator = AnimatorInflater.loadAnimator(
+ // The animator here could be ObjectAnimator or AnimatorSet.
+ final Animator animator = AnimatorInflater.loadAnimator(
res, theme, animResId, pathErrorScale);
- state.addTargetAnimator(target, objectAnimator);
+ updateAnimatorProperty(animator, target, state.mVectorDrawable,
+ state.mShouldIgnoreInvalidAnim);
+ state.addTargetAnimator(target, animator);
} else {
// The animation may be theme-dependent. As a
// workaround until Animator has full support for
@@ -414,6 +427,55 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
mRes = state.mPendingAnims == null ? null : res;
}
+ private static void updateAnimatorProperty(Animator animator, String targetName,
+ VectorDrawable vectorDrawable, boolean ignoreInvalidAnim) {
+ if (animator instanceof ObjectAnimator) {
+ // Change the property of the Animator from using reflection based on the property
+ // name to a Property object that wraps the setter and getter for modifying that
+ // specific property for a given object. By replacing the reflection with a direct call,
+ // we can largely reduce the time it takes for a animator to modify a VD property.
+ PropertyValuesHolder[] holders = ((ObjectAnimator) animator).getValues();
+ for (int i = 0; i < holders.length; i++) {
+ PropertyValuesHolder pvh = holders[i];
+ String propertyName = pvh.getPropertyName();
+ Object targetNameObj = vectorDrawable.getTargetByName(targetName);
+ Property property = null;
+ if (targetNameObj instanceof VectorDrawable.VObject) {
+ property = ((VectorDrawable.VObject) targetNameObj).getProperty(propertyName);
+ } else if (targetNameObj instanceof VectorDrawable.VectorDrawableState) {
+ property = ((VectorDrawable.VectorDrawableState) targetNameObj)
+ .getProperty(propertyName);
+ }
+ if (property != null) {
+ if (containsSameValueType(pvh, property)) {
+ pvh.setProperty(property);
+ } else if (!ignoreInvalidAnim) {
+ throw new RuntimeException("Wrong valueType for Property: " + propertyName
+ + ". Expected type: " + property.getType().toString() + ". Actual "
+ + "type defined in resources: " + pvh.getValueType().toString());
+
+ }
+ }
+ }
+ } else if (animator instanceof AnimatorSet) {
+ for (Animator anim : ((AnimatorSet) animator).getChildAnimations()) {
+ updateAnimatorProperty(anim, targetName, vectorDrawable, ignoreInvalidAnim);
+ }
+ }
+ }
+
+ private static boolean containsSameValueType(PropertyValuesHolder holder, Property property) {
+ Class type1 = holder.getValueType();
+ Class type2 = property.getType();
+ if (type1 == float.class || type1 == Float.class) {
+ return type2 == float.class || type2 == Float.class;
+ } else if (type1 == int.class || type1 == Integer.class) {
+ return type2 == int.class || type2 == Integer.class;
+ } else {
+ return type1 == type2;
+ }
+ }
+
/**
* Force to animate on UI thread.
* @hide
@@ -425,10 +487,22 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
throw new UnsupportedOperationException("Cannot force Animated Vector Drawable to" +
" run on UI thread when the animation has started on RenderThread.");
}
+ fallbackOntoUI();
+ }
+ }
+
+ private void fallbackOntoUI() {
+ if (mAnimatorSet instanceof VectorDrawableAnimatorRT) {
+ VectorDrawableAnimatorRT oldAnim = (VectorDrawableAnimatorRT) mAnimatorSet;
mAnimatorSet = new VectorDrawableAnimatorUI(this);
if (mAnimatorSetFromXml != null) {
mAnimatorSet.init(mAnimatorSetFromXml);
}
+ // Transfer the listener from RT animator to UI animator
+ if (oldAnim.mListener != null) {
+ mAnimatorSet.setListener(oldAnim.mListener);
+ }
+ oldAnim.transferPendingActions(mAnimatorSet);
}
}
@@ -462,6 +536,8 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
@Config int mChangingConfigurations;
VectorDrawable mVectorDrawable;
+ private final boolean mShouldIgnoreInvalidAnim;
+
/** Animators that require a theme before inflation. */
ArrayList<PendingAnimator> mPendingAnims;
@@ -473,6 +549,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
public AnimatedVectorDrawableState(AnimatedVectorDrawableState copy,
Callback owner, Resources res) {
+ mShouldIgnoreInvalidAnim = shouldIgnoreInvalidAnimation();
if (copy != null) {
mChangingConfigurations = copy.mChangingConfigurations;
@@ -616,8 +693,10 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
for (int i = 0, count = pendingAnims.size(); i < count; i++) {
final PendingAnimator pendingAnimator = pendingAnims.get(i);
- final Animator objectAnimator = pendingAnimator.newInstance(res, t);
- addTargetAnimator(pendingAnimator.target, objectAnimator);
+ final Animator animator = pendingAnimator.newInstance(res, t);
+ updateAnimatorProperty(animator, pendingAnimator.target, mVectorDrawable,
+ mShouldIgnoreInvalidAnim);
+ addTargetAnimator(pendingAnimator.target, animator);
}
}
}
@@ -982,6 +1061,9 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
private static final int REVERSE_ANIMATION = 2;
private static final int RESET_ANIMATION = 3;
private static final int END_ANIMATION = 4;
+
+ // If the duration of an animation is more than 300 frames, we cap the sample size to 300.
+ private static final int MAX_SAMPLE_POINTS = 300;
private AnimatorListener mListener = null;
private final LongArray mStartDelays = new LongArray();
private PropertyValuesHolder.PropertyValues mTmpValues =
@@ -992,14 +1074,12 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
private boolean mInitialized = false;
private boolean mIsReversible = false;
private boolean mIsInfinite = false;
- // This needs to be set before parsing starts.
- private boolean mShouldIgnoreInvalidAnim;
// TODO: Consider using NativeAllocationRegistery to track native allocation
private final VirtualRefBasePtr mSetRefBasePtr;
private WeakReference<RenderNode> mLastSeenTarget = null;
private int mLastListenerId = 0;
private final IntArray mPendingAnimationActions = new IntArray();
- private final Drawable mDrawable;
+ private final AnimatedVectorDrawable mDrawable;
VectorDrawableAnimatorRT(AnimatedVectorDrawable drawable) {
mDrawable = drawable;
@@ -1016,8 +1096,10 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
throw new UnsupportedOperationException("VectorDrawableAnimator cannot be " +
"re-initialized");
}
- mShouldIgnoreInvalidAnim = shouldIgnoreInvalidAnimation();
parseAnimatorSet(set, 0);
+ long vectorDrawableTreePtr = mDrawable.mAnimatedVectorState.mVectorDrawable
+ .getNativeTree();
+ nSetVectorDrawableTarget(mSetPtr, vectorDrawableTreePtr);
mInitialized = true;
mIsInfinite = set.getTotalDuration() == Animator.DURATION_INFINITE;
@@ -1077,7 +1159,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
} else if (target instanceof VectorDrawable.VFullPath) {
createRTAnimatorForFullPath(animator, (VectorDrawable.VFullPath) target,
startTime);
- } else if (!mShouldIgnoreInvalidAnim) {
+ } else if (!mDrawable.mAnimatedVectorState.mShouldIgnoreInvalidAnim) {
throw new IllegalArgumentException("ClipPath only supports PathData " +
"property");
}
@@ -1086,7 +1168,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
} else if (target instanceof VectorDrawable.VectorDrawableState) {
createRTAnimatorForRootGroup(values, animator,
(VectorDrawable.VectorDrawableState) target, startTime);
- } else if (!mShouldIgnoreInvalidAnim) {
+ } else if (!mDrawable.mAnimatedVectorState.mShouldIgnoreInvalidAnim) {
// Should never get here
throw new UnsupportedOperationException("Target should be either VGroup, VPath, " +
"or ConstantState, " + target == null ? "Null target" : target.getClass() +
@@ -1121,8 +1203,8 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
long propertyPtr = nCreateGroupPropertyHolder(nativePtr, propertyId,
(Float) mTmpValues.startValue, (Float) mTmpValues.endValue);
if (mTmpValues.dataSource != null) {
- float[] dataPoints = createDataPoints(mTmpValues.dataSource, animator
- .getDuration());
+ float[] dataPoints = createFloatDataPoints(mTmpValues.dataSource,
+ animator.getDuration());
nSetPropertyHolderData(propertyPtr, dataPoints, dataPoints.length);
}
createNativeChildAnimator(propertyPtr, startTime, animator);
@@ -1149,7 +1231,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
long nativePtr = target.getNativePtr();
if (mTmpValues.type == Float.class || mTmpValues.type == float.class) {
if (propertyId < 0) {
- if (mShouldIgnoreInvalidAnim) {
+ if (mDrawable.mAnimatedVectorState.mShouldIgnoreInvalidAnim) {
return;
} else {
throw new IllegalArgumentException("Property: " + mTmpValues.propertyName
@@ -1158,12 +1240,24 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
}
propertyPtr = nCreatePathPropertyHolder(nativePtr, propertyId,
(Float) mTmpValues.startValue, (Float) mTmpValues.endValue);
+ if (mTmpValues.dataSource != null) {
+ // Pass keyframe data to native, if any.
+ float[] dataPoints = createFloatDataPoints(mTmpValues.dataSource,
+ animator.getDuration());
+ nSetPropertyHolderData(propertyPtr, dataPoints, dataPoints.length);
+ }
} else if (mTmpValues.type == Integer.class || mTmpValues.type == int.class) {
propertyPtr = nCreatePathColorPropertyHolder(nativePtr, propertyId,
(Integer) mTmpValues.startValue, (Integer) mTmpValues.endValue);
+ if (mTmpValues.dataSource != null) {
+ // Pass keyframe data to native, if any.
+ int[] dataPoints = createIntDataPoints(mTmpValues.dataSource,
+ animator.getDuration());
+ nSetPropertyHolderData(propertyPtr, dataPoints, dataPoints.length);
+ }
} else {
- if (mShouldIgnoreInvalidAnim) {
+ if (mDrawable.mAnimatedVectorState.mShouldIgnoreInvalidAnim) {
return;
} else {
throw new UnsupportedOperationException("Unsupported type: " +
@@ -1171,45 +1265,63 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
"supported for Paths.");
}
}
- if (mTmpValues.dataSource != null) {
- float[] dataPoints = createDataPoints(mTmpValues.dataSource, animator
- .getDuration());
- nSetPropertyHolderData(propertyPtr, dataPoints, dataPoints.length);
- }
createNativeChildAnimator(propertyPtr, startTime, animator);
}
private void createRTAnimatorForRootGroup(PropertyValuesHolder[] values,
ObjectAnimator animator, VectorDrawable.VectorDrawableState target,
long startTime) {
- long nativePtr = target.getNativeRenderer();
- if (!animator.getPropertyName().equals("alpha")) {
- if (mShouldIgnoreInvalidAnim) {
- return;
- } else {
- throw new UnsupportedOperationException("Only alpha is supported for root "
- + "group");
- }
+ long nativePtr = target.getNativeRenderer();
+ if (!animator.getPropertyName().equals("alpha")) {
+ if (mDrawable.mAnimatedVectorState.mShouldIgnoreInvalidAnim) {
+ return;
+ } else {
+ throw new UnsupportedOperationException("Only alpha is supported for root "
+ + "group");
}
- Float startValue = null;
- Float endValue = null;
- for (int i = 0; i < values.length; i++) {
- values[i].getPropertyValues(mTmpValues);
- if (mTmpValues.propertyName.equals("alpha")) {
- startValue = (Float) mTmpValues.startValue;
- endValue = (Float) mTmpValues.endValue;
- break;
- }
+ }
+ Float startValue = null;
+ Float endValue = null;
+ for (int i = 0; i < values.length; i++) {
+ values[i].getPropertyValues(mTmpValues);
+ if (mTmpValues.propertyName.equals("alpha")) {
+ startValue = (Float) mTmpValues.startValue;
+ endValue = (Float) mTmpValues.endValue;
+ break;
}
- if (startValue == null && endValue == null) {
- if (mShouldIgnoreInvalidAnim) {
- return;
- } else {
- throw new UnsupportedOperationException("No alpha values are specified");
- }
+ }
+ if (startValue == null && endValue == null) {
+ if (mDrawable.mAnimatedVectorState.mShouldIgnoreInvalidAnim) {
+ return;
+ } else {
+ throw new UnsupportedOperationException("No alpha values are specified");
}
- long propertyPtr = nCreateRootAlphaPropertyHolder(nativePtr, startValue, endValue);
- createNativeChildAnimator(propertyPtr, startTime, animator);
+ }
+ long propertyPtr = nCreateRootAlphaPropertyHolder(nativePtr, startValue, endValue);
+ if (mTmpValues.dataSource != null) {
+ // Pass keyframe data to native, if any.
+ float[] dataPoints = createFloatDataPoints(mTmpValues.dataSource,
+ animator.getDuration());
+ nSetPropertyHolderData(propertyPtr, dataPoints, dataPoints.length);
+ }
+ createNativeChildAnimator(propertyPtr, startTime, animator);
+ }
+
+ /**
+ * Calculate the amount of frames an animation will run based on duration.
+ */
+ private static int getFrameCount(long duration) {
+ long frameIntervalNanos = Choreographer.getInstance().getFrameIntervalNanos();
+ int animIntervalMs = (int) (frameIntervalNanos / TimeUtils.NANOS_PER_MS);
+ int numAnimFrames = (int) Math.ceil(((double) duration) / animIntervalMs);
+ // We need 2 frames of data minimum.
+ numAnimFrames = Math.max(2, numAnimFrames);
+ if (numAnimFrames > MAX_SAMPLE_POINTS) {
+ Log.w("AnimatedVectorDrawable", "Duration for the animation is too long :" +
+ duration + ", the animation will subsample the keyframe or path data.");
+ numAnimFrames = MAX_SAMPLE_POINTS;
+ }
+ return numAnimFrames;
}
// These are the data points that define the value of the animating properties.
@@ -1217,11 +1329,9 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
// a point on the path corresponds to the values of translateX and translateY.
// TODO: (Optimization) We should pass the path down in native and chop it into segments
// in native.
- private static float[] createDataPoints(
+ private static float[] createFloatDataPoints(
PropertyValuesHolder.PropertyValues.DataSource dataSource, long duration) {
- long frameIntervalNanos = Choreographer.getInstance().getFrameIntervalNanos();
- int animIntervalMs = (int) (frameIntervalNanos / TimeUtils.NANOS_PER_MS);
- int numAnimFrames = (int) Math.ceil(((double) duration) / animIntervalMs);
+ int numAnimFrames = getFrameCount(duration);
float values[] = new float[numAnimFrames];
float lastFrame = numAnimFrames - 1;
for (int i = 0; i < numAnimFrames; i++) {
@@ -1231,6 +1341,18 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
return values;
}
+ private static int[] createIntDataPoints(
+ PropertyValuesHolder.PropertyValues.DataSource dataSource, long duration) {
+ int numAnimFrames = getFrameCount(duration);
+ int values[] = new int[numAnimFrames];
+ float lastFrame = numAnimFrames - 1;
+ for (int i = 0; i < numAnimFrames; i++) {
+ float fraction = i / lastFrame;
+ values[i] = (Integer) dataSource.getValueAtFraction(fraction);
+ }
+ return values;
+ }
+
private void createNativeChildAnimator(long propertyPtr, long extraDelay,
ObjectAnimator animator) {
long duration = animator.getDuration();
@@ -1245,7 +1367,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
mStartDelays.add(startDelay);
nAddAnimator(mSetPtr, propertyPtr, nativeInterpolator, startDelay, duration,
- repeatCount);
+ repeatCount, animator.getRepeatMode());
}
/**
@@ -1254,16 +1376,19 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
* to the last seen RenderNode target and start right away.
*/
protected void recordLastSeenTarget(DisplayListCanvas canvas) {
- mLastSeenTarget = new WeakReference<RenderNode>(
- RenderNodeAnimatorSetHelper.getTarget(canvas));
- if (mPendingAnimationActions.size() > 0 && useLastSeenTarget()) {
- if (DBG_ANIMATION_VECTOR_DRAWABLE) {
- Log.d(LOGTAG, "Target is set in the next frame");
- }
- for (int i = 0; i < mPendingAnimationActions.size(); i++) {
- handlePendingAction(mPendingAnimationActions.get(i));
+ final RenderNode node = RenderNodeAnimatorSetHelper.getTarget(canvas);
+ mLastSeenTarget = new WeakReference<RenderNode>(node);
+ // Add the animator to the list of animators on every draw
+ if (mInitialized || mPendingAnimationActions.size() > 0) {
+ if (useTarget(node)) {
+ if (DBG_ANIMATION_VECTOR_DRAWABLE) {
+ Log.d(LOGTAG, "Target is set in the next frame");
+ }
+ for (int i = 0; i < mPendingAnimationActions.size(); i++) {
+ handlePendingAction(mPendingAnimationActions.get(i));
+ }
+ mPendingAnimationActions.clear();
}
- mPendingAnimationActions.clear();
}
}
@@ -1285,10 +1410,15 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
private boolean useLastSeenTarget() {
if (mLastSeenTarget != null) {
final RenderNode target = mLastSeenTarget.get();
- if (target != null && target.isAttached()) {
- target.addAnimator(this);
- return true;
- }
+ return useTarget(target);
+ }
+ return false;
+ }
+
+ private boolean useTarget(RenderNode target) {
+ if (target != null && target.isAttached()) {
+ target.registerVectorDrawableAnimator(this);
+ return true;
}
return false;
}
@@ -1480,11 +1610,32 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
private static void callOnFinished(VectorDrawableAnimatorRT set, int id) {
set.onAnimationEnd(id);
}
+
+ private void transferPendingActions(VectorDrawableAnimator animatorSet) {
+ for (int i = 0; i < mPendingAnimationActions.size(); i++) {
+ int pendingAction = mPendingAnimationActions.get(i);
+ if (pendingAction == START_ANIMATION) {
+ animatorSet.start();
+ } else if (pendingAction == END_ANIMATION) {
+ animatorSet.end();
+ } else if (pendingAction == REVERSE_ANIMATION) {
+ animatorSet.reverse();
+ } else if (pendingAction == RESET_ANIMATION) {
+ animatorSet.reset();
+ } else {
+ throw new UnsupportedOperationException("Animation action " +
+ pendingAction + "is not supported");
+ }
+ }
+ mPendingAnimationActions.clear();
+ }
}
private static native long nCreateAnimatorSet();
+ private static native void nSetVectorDrawableTarget(long animatorPtr, long vectorDrawablePtr);
private static native void nAddAnimator(long setPtr, long propertyValuesHolder,
- long nativeInterpolator, long startDelay, long duration, int repeatCount);
+ long nativeInterpolator, long startDelay, long duration, int repeatCount,
+ int repeatMode);
private static native long nCreateGroupPropertyHolder(long nativePtr, int propertyId,
float startValue, float endValue);
@@ -1498,6 +1649,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
private static native long nCreateRootAlphaPropertyHolder(long nativePtr, float startValue,
float endValue);
private static native void nSetPropertyHolderData(long nativePtr, float[] data, int length);
+ private static native void nSetPropertyHolderData(long nativePtr, int[] data, int length);
private static native void nStart(long animatorSetPtr, VectorDrawableAnimatorRT set, int id);
private static native void nReverse(long animatorSetPtr, VectorDrawableAnimatorRT set, int id);
private static native void nEnd(long animatorSetPtr);
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index bcc354c5b736..3dbd2a96b00a 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -699,7 +699,7 @@ public class GradientDrawable extends Drawable {
float rad = mStrokePaint.getStrokeWidth();
canvas.saveLayer(mRect.left - rad, mRect.top - rad,
mRect.right + rad, mRect.bottom + rad,
- mLayerPaint, Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
+ mLayerPaint);
// don't perform the filter in our individual paints
// since the layer will do it for us
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index c855c4c6d17b..dc1d18f3b2bd 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -34,9 +34,12 @@ import android.graphics.Shader;
import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
+import android.util.FloatProperty;
+import android.util.IntProperty;
import android.util.LayoutDirection;
import android.util.Log;
import android.util.PathParser;
+import android.util.Property;
import android.util.Xml;
import com.android.internal.R;
@@ -232,7 +235,7 @@ public class VectorDrawable extends Drawable {
private final Rect mTmpBounds = new Rect();
public VectorDrawable() {
- this(new VectorDrawableState(), null);
+ this(new VectorDrawableState(null), null);
}
/**
@@ -763,6 +766,13 @@ public class VectorDrawable extends Drawable {
return mVectorState.mAutoMirrored;
}
+ /**
+ * @hide
+ */
+ public long getNativeTree() {
+ return mVectorState.getNativeRenderer();
+ }
+
static class VectorDrawableState extends ConstantState {
// Variables below need to be copied (deep copy if applicable) for mutation.
int[] mThemeAttrs;
@@ -795,12 +805,33 @@ public class VectorDrawable extends Drawable {
int mLastSWCachePixelCount = 0;
int mLastHWCachePixelCount = 0;
+ final static Property<VectorDrawableState, Float> ALPHA =
+ new FloatProperty<VectorDrawableState>("alpha") {
+ @Override
+ public void setValue(VectorDrawableState state, float value) {
+ state.setAlpha(value);
+ }
+
+ @Override
+ public Float get(VectorDrawableState state) {
+ return state.getAlpha();
+ }
+ };
+
+ Property getProperty(String propertyName) {
+ if (ALPHA.getName().equals(propertyName)) {
+ return ALPHA;
+ }
+ return null;
+ }
+
// This tracks the total native allocation for all the nodes.
private int mAllocationOfAllNodes = 0;
private static final int NATIVE_ALLOCATION_SIZE = 316;
- // Deep copy for mutate() or implicitly mutate.
+ // If copy is not null, deep copy the given VectorDrawableState. Otherwise, create a
+ // native vector drawable tree with an empty root group.
public VectorDrawableState(VectorDrawableState copy) {
if (copy != null) {
mThemeAttrs = copy.mThemeAttrs;
@@ -821,8 +852,11 @@ public class VectorDrawable extends Drawable {
if (copy.mRootName != null) {
mVGTargetsMap.put(copy.mRootName, this);
}
- onTreeConstructionFinished();
+ } else {
+ mRootGroup = new VGroup();
+ createNativeTree(mRootGroup);
}
+ onTreeConstructionFinished();
}
private void createNativeTree(VGroup rootGroup) {
@@ -840,7 +874,8 @@ public class VectorDrawable extends Drawable {
VMRuntime.getRuntime().registerNativeAllocation(NATIVE_ALLOCATION_SIZE);
}
-
+ // This should be called every time after a new RootGroup and all its subtrees are created
+ // (i.e. in constructors of VectorDrawableState and in inflate).
void onTreeConstructionFinished() {
mRootGroup.setTree(mNativeTree);
mAllocationOfAllNodes = mRootGroup.getNativeSize();
@@ -888,11 +923,6 @@ public class VectorDrawable extends Drawable {
|| super.canApplyTheme();
}
- public VectorDrawableState() {
- mRootGroup = new VGroup();
- createNativeTree(mRootGroup);
- }
-
@Override
public Drawable newDrawable() {
return new VectorDrawable(this, null);
@@ -972,7 +1002,7 @@ public class VectorDrawable extends Drawable {
}
static class VGroup extends VObject {
- private static final int ROTATE_INDEX = 0;
+ private static final int ROTATION_INDEX = 0;
private static final int PIVOT_X_INDEX = 1;
private static final int PIVOT_Y_INDEX = 2;
private static final int SCALE_X_INDEX = 3;
@@ -983,7 +1013,7 @@ public class VectorDrawable extends Drawable {
private static final int NATIVE_ALLOCATION_SIZE = 100;
- private static final HashMap<String, Integer> sPropertyMap =
+ private static final HashMap<String, Integer> sPropertyIndexMap =
new HashMap<String, Integer>() {
{
put("translateX", TRANSLATE_X_INDEX);
@@ -992,19 +1022,123 @@ public class VectorDrawable extends Drawable {
put("scaleY", SCALE_Y_INDEX);
put("pivotX", PIVOT_X_INDEX);
put("pivotY", PIVOT_Y_INDEX);
- put("rotation", ROTATE_INDEX);
+ put("rotation", ROTATION_INDEX);
}
};
static int getPropertyIndex(String propertyName) {
- if (sPropertyMap.containsKey(propertyName)) {
- return sPropertyMap.get(propertyName);
+ if (sPropertyIndexMap.containsKey(propertyName)) {
+ return sPropertyIndexMap.get(propertyName);
} else {
// property not found
return -1;
}
}
+ // Below are the Properties that wrap the setters to avoid reflection overhead in animations
+ private static final Property<VGroup, Float> TRANSLATE_X =
+ new FloatProperty<VGroup> ("translateX") {
+ @Override
+ public void setValue(VGroup object, float value) {
+ object.setTranslateX(value);
+ }
+
+ @Override
+ public Float get(VGroup object) {
+ return object.getTranslateX();
+ }
+ };
+
+ private static final Property<VGroup, Float> TRANSLATE_Y =
+ new FloatProperty<VGroup> ("translateY") {
+ @Override
+ public void setValue(VGroup object, float value) {
+ object.setTranslateY(value);
+ }
+
+ @Override
+ public Float get(VGroup object) {
+ return object.getTranslateY();
+ }
+ };
+
+ private static final Property<VGroup, Float> SCALE_X =
+ new FloatProperty<VGroup> ("scaleX") {
+ @Override
+ public void setValue(VGroup object, float value) {
+ object.setScaleX(value);
+ }
+
+ @Override
+ public Float get(VGroup object) {
+ return object.getScaleX();
+ }
+ };
+
+ private static final Property<VGroup, Float> SCALE_Y =
+ new FloatProperty<VGroup> ("scaleY") {
+ @Override
+ public void setValue(VGroup object, float value) {
+ object.setScaleY(value);
+ }
+
+ @Override
+ public Float get(VGroup object) {
+ return object.getScaleY();
+ }
+ };
+
+ private static final Property<VGroup, Float> PIVOT_X =
+ new FloatProperty<VGroup> ("pivotX") {
+ @Override
+ public void setValue(VGroup object, float value) {
+ object.setPivotX(value);
+ }
+
+ @Override
+ public Float get(VGroup object) {
+ return object.getPivotX();
+ }
+ };
+
+ private static final Property<VGroup, Float> PIVOT_Y =
+ new FloatProperty<VGroup> ("pivotY") {
+ @Override
+ public void setValue(VGroup object, float value) {
+ object.setPivotY(value);
+ }
+
+ @Override
+ public Float get(VGroup object) {
+ return object.getPivotY();
+ }
+ };
+
+ private static final Property<VGroup, Float> ROTATION =
+ new FloatProperty<VGroup> ("rotation") {
+ @Override
+ public void setValue(VGroup object, float value) {
+ object.setRotation(value);
+ }
+
+ @Override
+ public Float get(VGroup object) {
+ return object.getRotation();
+ }
+ };
+
+ private static final HashMap<String, Property> sPropertyMap =
+ new HashMap<String, Property>() {
+ {
+ put("translateX", TRANSLATE_X);
+ put("translateY", TRANSLATE_Y);
+ put("scaleX", SCALE_X);
+ put("scaleY", SCALE_Y);
+ put("pivotX", PIVOT_X);
+ put("pivotY", PIVOT_Y);
+ put("rotation", ROTATION);
+ }
+ };
// Temp array to store transform values obtained from native.
private float[] mTransform;
/////////////////////////////////////////////////////
@@ -1060,6 +1194,15 @@ public class VectorDrawable extends Drawable {
mNativePtr = nCreateGroup();
}
+ Property getProperty(String propertyName) {
+ if (sPropertyMap.containsKey(propertyName)) {
+ return sPropertyMap.get(propertyName);
+ } else {
+ // property not found
+ return null;
+ }
+ }
+
public String getGroupName() {
return mGroupName;
}
@@ -1107,7 +1250,7 @@ public class VectorDrawable extends Drawable {
throw new RuntimeException("Error: inconsistent property count");
}
float rotate = a.getFloat(R.styleable.VectorDrawableGroup_rotation,
- mTransform[ROTATE_INDEX]);
+ mTransform[ROTATION_INDEX]);
float pivotX = a.getFloat(R.styleable.VectorDrawableGroup_pivotX,
mTransform[PIVOT_X_INDEX]);
float pivotY = a.getFloat(R.styleable.VectorDrawableGroup_pivotY,
@@ -1293,6 +1436,27 @@ public class VectorDrawable extends Drawable {
String mPathName;
@Config int mChangingConfigurations;
+ private static final Property<VPath, PathParser.PathData> PATH_DATA =
+ new Property<VPath, PathParser.PathData>(PathParser.PathData.class, "pathData") {
+ @Override
+ public void set(VPath object, PathParser.PathData data) {
+ object.setPathData(data);
+ }
+
+ @Override
+ public PathParser.PathData get(VPath object) {
+ return object.getPathData();
+ }
+ };
+
+ Property getProperty(String propertyName) {
+ if (PATH_DATA.getName().equals(propertyName)) {
+ return PATH_DATA;
+ }
+ // property not found
+ return null;
+ }
+
public VPath() {
// Empty constructor.
}
@@ -1415,7 +1579,7 @@ public class VectorDrawable extends Drawable {
private static final int NATIVE_ALLOCATION_SIZE = 264;
// Property map for animatable attributes.
- private final static HashMap<String, Integer> sPropertyMap
+ private final static HashMap<String, Integer> sPropertyIndexMap
= new HashMap<String, Integer> () {
{
put("strokeWidth", STROKE_WIDTH_INDEX);
@@ -1429,6 +1593,125 @@ public class VectorDrawable extends Drawable {
}
};
+ // Below are the Properties that wrap the setters to avoid reflection overhead in animations
+ private static final Property<VFullPath, Float> STROKE_WIDTH =
+ new FloatProperty<VFullPath> ("strokeWidth") {
+ @Override
+ public void setValue(VFullPath object, float value) {
+ object.setStrokeWidth(value);
+ }
+
+ @Override
+ public Float get(VFullPath object) {
+ return object.getStrokeWidth();
+ }
+ };
+
+ private static final Property<VFullPath, Integer> STROKE_COLOR =
+ new IntProperty<VFullPath> ("strokeColor") {
+ @Override
+ public void setValue(VFullPath object, int value) {
+ object.setStrokeColor(value);
+ }
+
+ @Override
+ public Integer get(VFullPath object) {
+ return object.getStrokeColor();
+ }
+ };
+
+ private static final Property<VFullPath, Float> STROKE_ALPHA =
+ new FloatProperty<VFullPath> ("strokeAlpha") {
+ @Override
+ public void setValue(VFullPath object, float value) {
+ object.setStrokeAlpha(value);
+ }
+
+ @Override
+ public Float get(VFullPath object) {
+ return object.getStrokeAlpha();
+ }
+ };
+
+ private static final Property<VFullPath, Integer> FILL_COLOR =
+ new IntProperty<VFullPath>("fillColor") {
+ @Override
+ public void setValue(VFullPath object, int value) {
+ object.setFillColor(value);
+ }
+
+ @Override
+ public Integer get(VFullPath object) {
+ return object.getFillColor();
+ }
+ };
+
+ private static final Property<VFullPath, Float> FILL_ALPHA =
+ new FloatProperty<VFullPath> ("fillAlpha") {
+ @Override
+ public void setValue(VFullPath object, float value) {
+ object.setFillAlpha(value);
+ }
+
+ @Override
+ public Float get(VFullPath object) {
+ return object.getFillAlpha();
+ }
+ };
+
+ private static final Property<VFullPath, Float> TRIM_PATH_START =
+ new FloatProperty<VFullPath> ("trimPathStart") {
+ @Override
+ public void setValue(VFullPath object, float value) {
+ object.setTrimPathStart(value);
+ }
+
+ @Override
+ public Float get(VFullPath object) {
+ return object.getTrimPathStart();
+ }
+ };
+
+ private static final Property<VFullPath, Float> TRIM_PATH_END =
+ new FloatProperty<VFullPath> ("trimPathEnd") {
+ @Override
+ public void setValue(VFullPath object, float value) {
+ object.setTrimPathEnd(value);
+ }
+
+ @Override
+ public Float get(VFullPath object) {
+ return object.getTrimPathEnd();
+ }
+ };
+
+ private static final Property<VFullPath, Float> TRIM_PATH_OFFSET =
+ new FloatProperty<VFullPath> ("trimPathOffset") {
+ @Override
+ public void setValue(VFullPath object, float value) {
+ object.setTrimPathOffset(value);
+ }
+
+ @Override
+ public Float get(VFullPath object) {
+ return object.getTrimPathOffset();
+ }
+ };
+
+ private final static HashMap<String, Property> sPropertyMap
+ = new HashMap<String, Property> () {
+ {
+ put("strokeWidth", STROKE_WIDTH);
+ put("strokeColor", STROKE_COLOR);
+ put("strokeAlpha", STROKE_ALPHA);
+ put("fillColor", FILL_COLOR);
+ put("fillAlpha", FILL_ALPHA);
+ put("trimPathStart", TRIM_PATH_START);
+ put("trimPathEnd", TRIM_PATH_END);
+ put("trimPathOffset", TRIM_PATH_OFFSET);
+ }
+ };
+
// Temp array to store property data obtained from native getter.
private byte[] mPropertyData;
/////////////////////////////////////////////////////
@@ -1451,11 +1734,24 @@ public class VectorDrawable extends Drawable {
mFillColors = copy.mFillColors;
}
+ Property getProperty(String propertyName) {
+ Property p = super.getProperty(propertyName);
+ if (p != null) {
+ return p;
+ }
+ if (sPropertyMap.containsKey(propertyName)) {
+ return sPropertyMap.get(propertyName);
+ } else {
+ // property not found
+ return null;
+ }
+ }
+
int getPropertyIndex(String propertyName) {
- if (!sPropertyMap.containsKey(propertyName)) {
+ if (!sPropertyIndexMap.containsKey(propertyName)) {
return -1;
} else {
- return sPropertyMap.get(propertyName);
+ return sPropertyIndexMap.get(propertyName);
}
}
@@ -1789,6 +2085,7 @@ public class VectorDrawable extends Drawable {
abstract boolean onStateChange(int[] state);
abstract boolean isStateful();
abstract int getNativeSize();
+ abstract Property getProperty(String propertyName);
}
private static native long nCreateTree(long rootGroupPtr);