Loading core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java +0 −13 Original line number Diff line number Diff line Loading @@ -204,19 +204,6 @@ public class AdaptiveIconDrawableTest extends AndroidTestCase { 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 // Loading graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java +132 −45 Original line number Diff line number Diff line Loading @@ -26,6 +26,8 @@ import android.content.res.ColorStateList; 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; Loading @@ -37,6 +39,8 @@ import android.graphics.Path; 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; Loading Loading @@ -102,8 +106,7 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback 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; Loading @@ -111,10 +114,9 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback * 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 Loading @@ -129,10 +131,19 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback */ 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. */ Loading @@ -146,16 +157,19 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback */ 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) { Loading Loading @@ -266,7 +280,7 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback * @return the mask path object used to clip the drawable */ public Path getIconMask() { return mMaskTransformed; return mMask; } /** Loading Loading @@ -308,47 +322,92 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback 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); } } // Update the clipping mask mMaskMatrix.setScale(bounds.width() / MASK_SIZE, bounds.height() / MASK_SIZE); mMaskMatrix.postTranslate(bounds.left, bounds.top); mMask.transform(mMaskMatrix, mMaskTransformed); 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); } // 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 (mLayersBitmap == null) { return; } if (mLayersShader == null) { mCanvas.setBitmap(mLayersBitmap); mCanvas.drawColor(Color.BLACK); if (mLayerState.mChildren[BACKGROUND_ID].mDrawable != null) { mLayerState.mChildren[BACKGROUND_ID].mDrawable.draw(canvas); mLayerState.mChildren[BACKGROUND_ID].mDrawable.draw(mCanvas); } if (mLayerState.mChildren[FOREGROUND_ID].mDrawable != null) { mLayerState.mChildren[FOREGROUND_ID].mDrawable.draw(canvas); 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 Loading Loading @@ -541,10 +600,38 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback 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) { if (mSuspendChildInvalidation) { mChildRequestedInvalidation = true; } else { invalidateSelf(); } } @Override public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) { Loading Loading @@ -627,13 +714,6 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback @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 Loading Loading @@ -736,6 +816,10 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback } } if (changed) { updateLayerBounds(getBounds()); } return changed; } Loading @@ -751,6 +835,10 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback } } if (changed) { updateLayerBounds(getBounds()); } return changed; } Loading Loading @@ -891,7 +979,6 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback 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; Loading Loading
core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java +0 −13 Original line number Diff line number Diff line Loading @@ -204,19 +204,6 @@ public class AdaptiveIconDrawableTest extends AndroidTestCase { 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 // Loading
graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java +132 −45 Original line number Diff line number Diff line Loading @@ -26,6 +26,8 @@ import android.content.res.ColorStateList; 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; Loading @@ -37,6 +39,8 @@ import android.graphics.Path; 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; Loading Loading @@ -102,8 +106,7 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback 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; Loading @@ -111,10 +114,9 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback * 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 Loading @@ -129,10 +131,19 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback */ 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. */ Loading @@ -146,16 +157,19 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback */ 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) { Loading Loading @@ -266,7 +280,7 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback * @return the mask path object used to clip the drawable */ public Path getIconMask() { return mMaskTransformed; return mMask; } /** Loading Loading @@ -308,47 +322,92 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback 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); } } // Update the clipping mask mMaskMatrix.setScale(bounds.width() / MASK_SIZE, bounds.height() / MASK_SIZE); mMaskMatrix.postTranslate(bounds.left, bounds.top); mMask.transform(mMaskMatrix, mMaskTransformed); 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); } // 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 (mLayersBitmap == null) { return; } if (mLayersShader == null) { mCanvas.setBitmap(mLayersBitmap); mCanvas.drawColor(Color.BLACK); if (mLayerState.mChildren[BACKGROUND_ID].mDrawable != null) { mLayerState.mChildren[BACKGROUND_ID].mDrawable.draw(canvas); mLayerState.mChildren[BACKGROUND_ID].mDrawable.draw(mCanvas); } if (mLayerState.mChildren[FOREGROUND_ID].mDrawable != null) { mLayerState.mChildren[FOREGROUND_ID].mDrawable.draw(canvas); 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 Loading Loading @@ -541,10 +600,38 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback 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) { if (mSuspendChildInvalidation) { mChildRequestedInvalidation = true; } else { invalidateSelf(); } } @Override public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) { Loading Loading @@ -627,13 +714,6 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback @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 Loading Loading @@ -736,6 +816,10 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback } } if (changed) { updateLayerBounds(getBounds()); } return changed; } Loading @@ -751,6 +835,10 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback } } if (changed) { updateLayerBounds(getBounds()); } return changed; } Loading Loading @@ -891,7 +979,6 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback 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; Loading