Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit da8fc0ee authored by Wei Sheng Shih's avatar Wei Sheng Shih Committed by Automerger Merge Worker
Browse files

Merge "Drawing the legacy icon similar to the Launcher." into sc-dev am: fffb1502

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/14234712

Change-Id: I433043bdbb252676eef71801e9891248ad508f8c
parents dfa430c1 fffb1502
Loading
Loading
Loading
Loading
+5 −8
Original line number Diff line number Diff line
@@ -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);
    }

    /**
+16 −9
Original line number Diff line number Diff line
@@ -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,12 +305,19 @@ public class SplashscreenContentDrawer {
            return mCachedResult;
        }

        private void createIconDrawable(Drawable iconDrawable) {
        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) {
            if (!(iconDrawable instanceof AdaptiveIconDrawable)) {
@@ -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;
+142 −63
Original line number Diff line number Diff line
@@ -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;
        private final Path mMaskScaleOnly;
        private final Matrix mMaskMatrix;
        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);
            }
        }
    }
    /**
         * Scaled mask based on the view bounds.
     * A lightweight AdaptiveIconDrawable which support foreground to be Animatable, and keep this
     * drawable masked by config_icon_mask.
     */
        private final Path mMask;
        private final Path mMaskScaleOnly;
        private final Matrix mMaskMatrix;
        private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    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;
        }
    }

}