Drawing the legacy icon similar to the Launcher.
The legacy icon on the splash screen should looks similar to it shows on Launcher, a circle background wrap the icon bitmap in the center. Bug: 182883560 Test: atest SplashscreenTests Change-Id: I994f516bb7b5b1231ff9e5bb02a3a92aa5113a84
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user