Merge "Merge "Revert "Using clippath for adaptive icon drawable instead of bit..."" into tm-qpr-dev am: 132b386fc5" into tm-qpr-dev-plus-aosp
diff --git a/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java b/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java
index ddc05e0..7338c3a 100644
--- a/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java
+++ b/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java
@@ -204,19 +204,6 @@
assertEquals(100, Color.alpha(bitmap.getPixel(50, 50)));
}
- @Test
- public void testSetBounds() throws Exception {
- mIconDrawable = new AdaptiveIconDrawable(mBackgroundDrawable, mForegroundDrawable);
- mIconDrawable.setBounds(0, 0, 100, 100);
-
- assertEquals(new Rect(-25, -25, 125, 125), mBackgroundDrawable.getBounds());
- assertEquals(new Rect(-25, -25, 125, 125), mForegroundDrawable.getBounds());
-
- mIconDrawable.setBounds(10, 10, 110, 110);
- assertEquals(new Rect(-15, -15, 135, 135), mBackgroundDrawable.getBounds());
- assertEquals(new Rect(-15, -15, 135, 135), mForegroundDrawable.getBounds());
- }
-
//
// Utils
//
diff --git a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
index 47de37d..688425a 100644
--- a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
+++ b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
@@ -26,6 +26,8 @@
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
import android.graphics.BlendMode;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -37,6 +39,8 @@
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.Region;
+import android.graphics.Shader;
+import android.graphics.Shader.TileMode;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.PathParser;
@@ -102,8 +106,7 @@
private static final float DEFAULT_VIEW_PORT_SCALE = 1f / (1 + 2 * EXTRA_INSET_PERCENTAGE);
/**
- * Unused path.
- * TODO: Remove once the layoutLib is updated
+ * Clip path defined in R.string.config_icon_mask.
*/
private static Path sMask;
@@ -111,10 +114,9 @@
* Scaled mask based on the view bounds.
*/
private final Path mMask;
- private final Path mMaskTransformed;
+ private final Path mMaskScaleOnly;
private final Matrix mMaskMatrix;
private final Region mTransparentRegion;
- private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
/**
* Indices used to access {@link #mLayerState.mChildDrawable} array for foreground and
@@ -129,10 +131,19 @@
*/
LayerState mLayerState;
+ private Shader mLayersShader;
+ private Bitmap mLayersBitmap;
+
private final Rect mTmpOutRect = new Rect();
private Rect mHotspotBounds;
private boolean mMutated;
+ private boolean mSuspendChildInvalidation;
+ private boolean mChildRequestedInvalidation;
+ private final Canvas mCanvas;
+ private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG |
+ Paint.FILTER_BITMAP_FLAG);
+
/**
* Constructor used for xml inflation.
*/
@@ -146,16 +157,19 @@
*/
AdaptiveIconDrawable(@Nullable LayerState state, @Nullable Resources res) {
mLayerState = createConstantState(state, res);
- // config_icon_mask from context bound resource may have been changed using
+ // config_icon_mask from context bound resource may have been chaged using
// OverlayManager. Read that one first.
Resources r = ActivityThread.currentActivityThread() == null
? Resources.getSystem()
: ActivityThread.currentActivityThread().getApplication().getResources();
- mMask = PathParser.createPathFromPathData(r.getString(R.string.config_icon_mask));
- mMaskTransformed = new Path();
+ // TODO: either make sMask update only when config_icon_mask changes OR
+ // get rid of it all-together in layoutlib
+ sMask = PathParser.createPathFromPathData(r.getString(R.string.config_icon_mask));
+ mMask = new Path(sMask);
+ mMaskScaleOnly = new Path(mMask);
mMaskMatrix = new Matrix();
+ mCanvas = new Canvas();
mTransparentRegion = new Region();
- mPaint.setColor(Color.BLACK);
}
private ChildDrawable createChildDrawable(Drawable drawable) {
@@ -266,7 +280,7 @@
* @return the mask path object used to clip the drawable
*/
public Path getIconMask() {
- return mMaskTransformed;
+ return mMask;
}
/**
@@ -308,47 +322,92 @@
if (bounds.isEmpty()) {
return;
}
- // Set the child layer bounds bigger than the view port size
- // by {@link #DEFAULT_VIEW_PORT_SCALE}
- float cX = bounds.exactCenterX();
- float cY = bounds.exactCenterY();
- float insetWidth = bounds.width() / (DEFAULT_VIEW_PORT_SCALE * 2);
- float insetHeight = bounds.height() / (DEFAULT_VIEW_PORT_SCALE * 2);
- final Rect outRect = mTmpOutRect;
- outRect.set(
- (int) (cX - insetWidth),
- (int) (cY - insetHeight),
- (int) (cX + insetWidth),
- (int) (cY + insetHeight));
+ updateLayerBounds(bounds);
+ }
+
+ private void updateLayerBounds(Rect bounds) {
+ if (bounds.isEmpty()) {
+ return;
+ }
+ try {
+ suspendChildInvalidation();
+ updateLayerBoundsInternal(bounds);
+ updateMaskBoundsInternal(bounds);
+ } finally {
+ resumeChildInvalidation();
+ }
+ }
+
+ /**
+ * Set the child layer bounds bigger than the view port size by {@link #DEFAULT_VIEW_PORT_SCALE}
+ */
+ private void updateLayerBoundsInternal(Rect bounds) {
+ int cX = bounds.width() / 2;
+ int cY = bounds.height() / 2;
for (int i = 0, count = mLayerState.N_CHILDREN; i < count; i++) {
final ChildDrawable r = mLayerState.mChildren[i];
- if (r.mDrawable != null) {
- r.mDrawable.setBounds(outRect);
+ final Drawable d = r.mDrawable;
+ if (d == null) {
+ continue;
}
+
+ int insetWidth = (int) (bounds.width() / (DEFAULT_VIEW_PORT_SCALE * 2));
+ int insetHeight = (int) (bounds.height() / (DEFAULT_VIEW_PORT_SCALE * 2));
+ final Rect outRect = mTmpOutRect;
+ outRect.set(cX - insetWidth, cY - insetHeight, cX + insetWidth, cY + insetHeight);
+
+ d.setBounds(outRect);
+ }
+ }
+
+ private void updateMaskBoundsInternal(Rect b) {
+ // reset everything that depends on the view bounds
+ mMaskMatrix.setScale(b.width() / MASK_SIZE, b.height() / MASK_SIZE);
+ sMask.transform(mMaskMatrix, mMaskScaleOnly);
+
+ mMaskMatrix.postTranslate(b.left, b.top);
+ sMask.transform(mMaskMatrix, mMask);
+
+ if (mLayersBitmap == null || mLayersBitmap.getWidth() != b.width()
+ || mLayersBitmap.getHeight() != b.height()) {
+ mLayersBitmap = Bitmap.createBitmap(b.width(), b.height(), Bitmap.Config.ARGB_8888);
}
- // Update the clipping mask
- mMaskMatrix.setScale(bounds.width() / MASK_SIZE, bounds.height() / MASK_SIZE);
- mMaskMatrix.postTranslate(bounds.left, bounds.top);
- mMask.transform(mMaskMatrix, mMaskTransformed);
-
- // Clear the transparent region, it is calculated lazily
+ mPaint.setShader(null);
mTransparentRegion.setEmpty();
+ mLayersShader = null;
}
@Override
public void draw(Canvas canvas) {
- int saveCount = canvas.save();
- canvas.clipPath(mMaskTransformed);
- canvas.drawPaint(mPaint);
- if (mLayerState.mChildren[BACKGROUND_ID].mDrawable != null) {
- mLayerState.mChildren[BACKGROUND_ID].mDrawable.draw(canvas);
+ if (mLayersBitmap == null) {
+ return;
}
- if (mLayerState.mChildren[FOREGROUND_ID].mDrawable != null) {
- mLayerState.mChildren[FOREGROUND_ID].mDrawable.draw(canvas);
+ if (mLayersShader == null) {
+ mCanvas.setBitmap(mLayersBitmap);
+ mCanvas.drawColor(Color.BLACK);
+ if (mLayerState.mChildren[BACKGROUND_ID].mDrawable != null) {
+ mLayerState.mChildren[BACKGROUND_ID].mDrawable.draw(mCanvas);
+ }
+ if (mLayerState.mChildren[FOREGROUND_ID].mDrawable != null) {
+ mLayerState.mChildren[FOREGROUND_ID].mDrawable.draw(mCanvas);
+ }
+ mLayersShader = new BitmapShader(mLayersBitmap, TileMode.CLAMP, TileMode.CLAMP);
+ mPaint.setShader(mLayersShader);
}
- canvas.restoreToCount(saveCount);
+ if (mMaskScaleOnly != null) {
+ Rect bounds = getBounds();
+ canvas.translate(bounds.left, bounds.top);
+ canvas.drawPath(mMaskScaleOnly, mPaint);
+ canvas.translate(-bounds.left, -bounds.top);
+ }
+ }
+
+ @Override
+ public void invalidateSelf() {
+ mLayersShader = null;
+ super.invalidateSelf();
}
@Override
@@ -541,9 +600,37 @@
return false;
}
+ /**
+ * Temporarily suspends child invalidation.
+ *
+ * @see #resumeChildInvalidation()
+ */
+ private void suspendChildInvalidation() {
+ mSuspendChildInvalidation = true;
+ }
+
+ /**
+ * Resumes child invalidation after suspension, immediately performing an
+ * invalidation if one was requested by a child during suspension.
+ *
+ * @see #suspendChildInvalidation()
+ */
+ private void resumeChildInvalidation() {
+ mSuspendChildInvalidation = false;
+
+ if (mChildRequestedInvalidation) {
+ mChildRequestedInvalidation = false;
+ invalidateSelf();
+ }
+ }
+
@Override
public void invalidateDrawable(@NonNull Drawable who) {
- invalidateSelf();
+ if (mSuspendChildInvalidation) {
+ mChildRequestedInvalidation = true;
+ } else {
+ invalidateSelf();
+ }
}
@Override
@@ -627,13 +714,6 @@
@Override
public void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
- final ChildDrawable[] array = mLayerState.mChildren;
- for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
- final Drawable dr = array[i].mDrawable;
- if (dr != null) {
- dr.setAlpha(alpha);
- }
- }
}
@Override
@@ -736,6 +816,10 @@
}
}
+ if (changed) {
+ updateLayerBounds(getBounds());
+ }
+
return changed;
}
@@ -751,6 +835,10 @@
}
}
+ if (changed) {
+ updateLayerBounds(getBounds());
+ }
+
return changed;
}
@@ -891,7 +979,6 @@
int mDensity;
// The density to use when inflating/looking up the children drawables. A value of 0 means
-
// use the system's density.
int mSrcDensityOverride = 0;