diff --git a/core/java/android/window/SplashScreenView.java b/core/java/android/window/SplashScreenView.java index d67fb3233a3b9..6d7163909492c 100644 --- a/core/java/android/window/SplashScreenView.java +++ b/core/java/android/window/SplashScreenView.java @@ -317,10 +317,10 @@ public final class SplashScreenView extends FrameLayout { } void initIconAnimation(Drawable iconDrawable, long duration) { - if (!(iconDrawable instanceof SplashscreenIconDrawable)) { + if (!(iconDrawable instanceof IconAnimateListener)) { return; } - SplashscreenIconDrawable aniDrawable = (SplashscreenIconDrawable) iconDrawable; + IconAnimateListener aniDrawable = (IconAnimateListener) iconDrawable; aniDrawable.prepareAnimate(duration, this::animationStartCallback); } @@ -455,20 +455,17 @@ public final class SplashScreenView extends FrameLayout { } /** - * A lightweight Drawable object to make the view drawing faster and keep this - * drawable masked by config_icon_mask. + * An interface for an animatable drawable object to register a callback when animation start. * @hide */ - public abstract static class SplashscreenIconDrawable extends Drawable { + public interface IconAnimateListener { /** * Prepare the animation if this drawable also be animatable. * @param duration The animation duration. * @param startListener The callback listener used to receive the start of the animation. * @return true if this drawable object can also be animated and it can be played now. */ - protected boolean prepareAnimate(long duration, Runnable startListener) { - return false; - } + boolean prepareAnimate(long duration, Runnable startListener); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java index a646231d175bf..093c272193a3e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java @@ -280,7 +280,7 @@ public class SplashscreenContentDrawer { // replaced icon, don't process iconDrawable = mTmpAttrs.mReplaceIcon; animationDuration = mTmpAttrs.mAnimationDuration; - createIconDrawable(iconDrawable); + createIconDrawable(iconDrawable, false); } else { final float iconScale = (float) mIconSize / (float) mDefaultIconSize; final int densityDpi = mContext.getResources().getDisplayMetrics().densityDpi; @@ -295,7 +295,7 @@ public class SplashscreenContentDrawer { Slog.d(TAG, "The icon is not an AdaptiveIconDrawable"); } // TODO process legacy icon(bitmap) - createIconDrawable(iconDrawable); + createIconDrawable(iconDrawable, true); } animationDuration = 0; } @@ -305,11 +305,18 @@ public class SplashscreenContentDrawer { return mCachedResult; } - private void createIconDrawable(Drawable iconDrawable) { - mFinalIconDrawable = SplashscreenIconDrawableFactory.makeIconDrawable( - mTmpAttrs.mIconBgColor != Color.TRANSPARENT - ? mTmpAttrs.mIconBgColor : mThemeColor, - iconDrawable, mDefaultIconSize, mFinalIconSize, mSplashscreenWorkerHandler); + private void createIconDrawable(Drawable iconDrawable, boolean legacy) { + if (legacy) { + mFinalIconDrawable = SplashscreenIconDrawableFactory.makeLegacyIconDrawable( + mTmpAttrs.mIconBgColor != Color.TRANSPARENT + ? mTmpAttrs.mIconBgColor : Color.WHITE, + iconDrawable, mDefaultIconSize, mFinalIconSize, mSplashscreenWorkerHandler); + } else { + mFinalIconDrawable = SplashscreenIconDrawableFactory.makeIconDrawable( + mTmpAttrs.mIconBgColor != Color.TRANSPARENT + ? mTmpAttrs.mIconBgColor : mThemeColor, + iconDrawable, mDefaultIconSize, mFinalIconSize, mSplashscreenWorkerHandler); + } } private boolean processAdaptiveIcon(Drawable iconDrawable) { @@ -365,12 +372,12 @@ public class SplashscreenContentDrawer { // Using AdaptiveIconDrawable here can help keep the shape consistent with the // current settings. mFinalIconSize = (int) (0.5f + mIconSize * noBgScale); - createIconDrawable(iconForeground); + createIconDrawable(iconForeground, false); } else { if (DEBUG) { Slog.d(TAG, "makeSplashScreenContentView: draw whole icon"); } - createIconDrawable(iconDrawable); + createIconDrawable(iconDrawable, false); } Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); return true; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java index e8d95ab72ff1d..dae7055ede536 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java @@ -52,32 +52,39 @@ public class SplashscreenIconDrawableFactory { @NonNull Drawable foregroundDrawable, int srcIconSize, int iconSize, Handler splashscreenWorkerHandler) { if (foregroundDrawable instanceof Animatable) { - return new AnimatableIconDrawable(backgroundColor, foregroundDrawable); + return new AnimatableIconAnimateListener(backgroundColor, foregroundDrawable); } else if (foregroundDrawable instanceof AdaptiveIconDrawable) { - return new ImmobileIconDrawable((AdaptiveIconDrawable) foregroundDrawable, + return new ImmobileIconDrawable(foregroundDrawable, srcIconSize, iconSize, splashscreenWorkerHandler); } else { - // TODO for legacy icon don't use adaptive icon drawable to wrapper it + // single layer icon return new ImmobileIconDrawable(new AdaptiveIconDrawable( new ColorDrawable(backgroundColor), foregroundDrawable), srcIconSize, iconSize, splashscreenWorkerHandler); } } + static Drawable makeLegacyIconDrawable(@ColorInt int backgroundColor, + @NonNull Drawable foregroundDrawable, int srcIconSize, int iconSize, + Handler splashscreenWorkerHandler) { + return new ImmobileIconDrawable(new LegacyIconDrawable(backgroundColor, + foregroundDrawable), srcIconSize, iconSize, splashscreenWorkerHandler); + } + private static class ImmobileIconDrawable extends Drawable { private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG | Paint.FILTER_BITMAP_FLAG); private final Matrix mMatrix = new Matrix(); private Bitmap mIconBitmap; - ImmobileIconDrawable(AdaptiveIconDrawable drawable, int srcIconSize, int iconSize, + ImmobileIconDrawable(Drawable drawable, int srcIconSize, int iconSize, Handler splashscreenWorkerHandler) { final float scale = (float) iconSize / srcIconSize; mMatrix.setScale(scale, scale); splashscreenWorkerHandler.post(() -> preDrawIcon(drawable, srcIconSize)); } - private void preDrawIcon(AdaptiveIconDrawable drawable, int size) { + private void preDrawIcon(Drawable drawable, int size) { synchronized (mPaint) { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "preDrawIcon"); mIconBitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); @@ -115,49 +122,143 @@ public class SplashscreenIconDrawableFactory { } } - /** - * A lightweight AdaptiveIconDrawable which support foreground to be Animatable, and keep this - * drawable masked by config_icon_mask. - * @hide - */ - private static class AnimatableIconDrawable extends SplashScreenView.SplashscreenIconDrawable { + // Base class the draw the circle background + private abstract static class MaskBackgroundDrawable extends Drawable { private static final float MASK_SIZE = AdaptiveIconDrawable.MASK_SIZE; private static final float EXTRA_INSET_PERCENTAGE = 1 / 4f; - private static final float DEFAULT_VIEW_PORT_SCALE = 1f / (1 + 2 * EXTRA_INSET_PERCENTAGE); - private final Rect mTmpOutRect = new Rect(); + static final float DEFAULT_VIEW_PORT_SCALE = 1f / (1 + 2 * EXTRA_INSET_PERCENTAGE); /** * Clip path defined in R.string.config_icon_mask. */ private static Path sMask; - - /** - * Scaled mask based on the view bounds. - */ - private final Path mMask; private final Path mMaskScaleOnly; private final Matrix mMaskMatrix; - private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG + | Paint.FILTER_BITMAP_FLAG); + + MaskBackgroundDrawable(@ColorInt int backgroundColor) { + final Resources r = Resources.getSystem(); + sMask = PathParser.createPathFromPathData(r.getString(R.string.config_icon_mask)); + Path mask = new Path(sMask); + mMaskScaleOnly = new Path(mask); + mMaskMatrix = new Matrix(); + mPaint.setColor(backgroundColor); + mPaint.setStyle(Paint.Style.FILL); + } + + @Override + protected void onBoundsChange(Rect bounds) { + if (bounds.isEmpty()) { + return; + } + updateLayerBounds(bounds); + } + + protected void updateLayerBounds(Rect bounds) { + // reset everything that depends on the view bounds + mMaskMatrix.setScale(bounds.width() / MASK_SIZE, bounds.height() / MASK_SIZE); + sMask.transform(mMaskMatrix, mMaskScaleOnly); + } + + @Override + public void draw(Canvas canvas) { + if (mMaskScaleOnly != null) { + canvas.drawPath(mMaskScaleOnly, mPaint); + } + } + + @Override + public void setAlpha(int alpha) { + mPaint.setAlpha(alpha); + } + + @Override + public int getOpacity() { + return PixelFormat.RGBA_8888; + } + } + + private static class LegacyIconDrawable extends MaskBackgroundDrawable { + // reference FixedScaleDrawable + // iconBounds = 0.7 * X * outerBounds, X is the scale of diagonal + private static final float LEGACY_ICON_SCALE = .7f * .8f; + private final Drawable mForegroundDrawable; + private float mScaleX, mScaleY, mTransX, mTransY; + + LegacyIconDrawable(@ColorInt int backgroundColor, Drawable foregroundDrawable) { + super(backgroundColor); + mForegroundDrawable = foregroundDrawable; + mScaleX = LEGACY_ICON_SCALE; + mScaleY = LEGACY_ICON_SCALE; + } + + @Override + protected void updateLayerBounds(Rect bounds) { + super.updateLayerBounds(bounds); + + if (mForegroundDrawable == null) { + return; + } + float outerBoundsWidth = bounds.width(); + float outerBoundsHeight = bounds.height(); + float h = mForegroundDrawable.getIntrinsicHeight(); + float w = mForegroundDrawable.getIntrinsicWidth(); + mScaleX = LEGACY_ICON_SCALE; + mScaleY = LEGACY_ICON_SCALE; + if (h > w && w > 0) { + mScaleX *= w / h; + } else if (w > h && h > 0) { + mScaleY *= h / w; + } + int innerBoundsWidth = (int) (0.5 + outerBoundsWidth * mScaleX); + int innerBoundsHeight = (int) (0.5 + outerBoundsHeight * mScaleY); + final Rect rect = new Rect(0, 0, innerBoundsWidth, innerBoundsHeight); + mForegroundDrawable.setBounds(rect); + mTransX = (outerBoundsWidth - innerBoundsWidth) / 2; + mTransY = (outerBoundsHeight - innerBoundsHeight) / 2; + invalidateSelf(); + } + + @Override + public void draw(Canvas canvas) { + super.draw(canvas); + int saveCount = canvas.save(); + canvas.translate(mTransX, mTransY); + if (mForegroundDrawable != null) { + mForegroundDrawable.draw(canvas); + } + canvas.restoreToCount(saveCount); + } + + @Override + public void setColorFilter(ColorFilter colorFilter) { + if (mForegroundDrawable != null) { + mForegroundDrawable.setColorFilter(colorFilter); + } + } + } + /** + * A lightweight AdaptiveIconDrawable which support foreground to be Animatable, and keep this + * drawable masked by config_icon_mask. + */ + private static class AnimatableIconAnimateListener extends MaskBackgroundDrawable + implements SplashScreenView.IconAnimateListener { private final Drawable mForegroundDrawable; private Animatable mAnimatableIcon; private Animator mIconAnimator; private boolean mAnimationTriggered; + private final Rect mTmpOutRect = new Rect(); - AnimatableIconDrawable(@ColorInt int backgroundColor, Drawable foregroundDrawable) { + AnimatableIconAnimateListener(@ColorInt int backgroundColor, Drawable foregroundDrawable) { + super(backgroundColor); mForegroundDrawable = foregroundDrawable; - final Resources r = Resources.getSystem(); - sMask = PathParser.createPathFromPathData(r.getString(R.string.config_icon_mask)); - mMask = new Path(sMask); - mMaskScaleOnly = new Path(mMask); - mMaskMatrix = new Matrix(); - mPaint.setColor(backgroundColor); - mPaint.setStyle(Paint.Style.FILL); if (mForegroundDrawable != null) { mForegroundDrawable.setCallback(mCallback); } } @Override - protected boolean prepareAnimate(long duration, Runnable startListener) { + public boolean prepareAnimate(long duration, Runnable startListener) { mAnimatableIcon = (Animatable) mForegroundDrawable; mIconAnimator = ValueAnimator.ofInt(0, 1); mIconAnimator.setDuration(duration); @@ -190,11 +291,19 @@ public class SplashscreenIconDrawableFactory { } @Override - protected void onBoundsChange(Rect bounds) { - if (bounds.isEmpty()) { - return; + protected void updateLayerBounds(Rect bounds) { + super.updateLayerBounds(bounds); + int cX = bounds.width() / 2; + int cY = bounds.height() / 2; + + 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); + if (mForegroundDrawable != null) { + mForegroundDrawable.setBounds(outRect); } - updateLayerBounds(bounds); + invalidateSelf(); } private final Callback mCallback = new Callback() { @@ -214,24 +323,6 @@ public class SplashscreenIconDrawableFactory { } }; - private void updateLayerBounds(Rect bounds) { - int cX = bounds.width() / 2; - int cY = bounds.height() / 2; - - 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); - - if (mForegroundDrawable != null) { - mForegroundDrawable.setBounds(outRect); - } - // reset everything that depends on the view bounds - mMaskMatrix.setScale(bounds.width() / MASK_SIZE, bounds.height() / MASK_SIZE); - sMask.transform(mMaskMatrix, mMaskScaleOnly); - invalidateSelf(); - } - private void ensureAnimationStarted() { if (mAnimationTriggered) { return; @@ -244,31 +335,19 @@ public class SplashscreenIconDrawableFactory { @Override public void draw(Canvas canvas) { - if (mMaskScaleOnly != null) { - canvas.drawPath(mMaskScaleOnly, mPaint); - } + super.draw(canvas); if (mForegroundDrawable != null) { ensureAnimationStarted(); mForegroundDrawable.draw(canvas); } } - @Override - public void setAlpha(int alpha) { - mPaint.setAlpha(alpha); - } - @Override public void setColorFilter(ColorFilter colorFilter) { if (mForegroundDrawable != null) { mForegroundDrawable.setColorFilter(colorFilter); } } - - @Override - public int getOpacity() { - return PixelFormat.TRANSLUCENT; - } } }