summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
author wilsonshih <wilsonshih@google.com> 2021-02-25 14:32:10 +0800
committer wilsonshih <wilsonshih@google.com> 2021-03-19 11:23:56 +0800
commita4c6d8f82897cc03cc71a5301874d3e6fee63eab (patch)
tree42337e0132aa8d91393ce64e61398d0fa8fa031e /libs
parentdf8f1012446cb8f34731b944745c93fd69f392b8 (diff)
Add API windowSplashScreenIconBackground(12/N)
Add new API for developer to customize starting window, set a background color behind the icon. Also masking the AVD and icon background like AdaptiveIconDrawable. Ref doc: go/improved_app_launch_animations Bug: 73289295 Test: atest SplashscreenTests Change-Id: Ibba49ecc3ca020022cf775fed302e383908be327
Diffstat (limited to 'libs')
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java38
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java217
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java1
3 files changed, 248 insertions, 8 deletions
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 3f9c2717731a..3f46fee26510 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
@@ -16,6 +16,7 @@
package com.android.wm.shell.startingsurface;
+import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.app.ActivityThread;
import android.content.Context;
@@ -149,14 +150,16 @@ public class SplashscreenContentDrawer {
.setThemeDrawable(themeBGDrawable)
.setIconDrawable(iconDrawable)
.setIconAnimationDuration(animationDuration)
- .setBrandingDrawable(attrs.mBrandingImage).build();
+ .setBrandingDrawable(attrs.mBrandingImage)
+ .setIconBackground(attrs.mIconBgColor).build();
}
- private static class SplashScreenWindowAttrs {
+ static class SplashScreenWindowAttrs {
private int mWindowBgResId = 0;
private int mWindowBgColor = Color.TRANSPARENT;
private Drawable mReplaceIcon = null;
private Drawable mBrandingImage = null;
+ private int mIconBgColor = Color.TRANSPARENT;
private int mAnimationDuration = 0;
static SplashScreenWindowAttrs createWindowAttrs(Context context) {
@@ -172,6 +175,8 @@ public class SplashscreenContentDrawer {
R.styleable.Window_windowSplashScreenAnimationDuration, 0);
attrs.mBrandingImage = typedArray.getDrawable(
R.styleable.Window_windowSplashScreenBrandingImage);
+ attrs.mIconBgColor = typedArray.getColor(
+ R.styleable.Window_windowSplashScreenIconBackgroundColor, Color.TRANSPARENT);
typedArray.recycle();
if (DEBUG) {
Slog.d(TAG, "window attributes color: "
@@ -189,6 +194,7 @@ public class SplashscreenContentDrawer {
private int mIconAnimationDuration;
private Context mContext;
private Drawable mBrandingDrawable;
+ private @ColorInt int mIconBackground;
// result
private boolean mBuildComplete = false;
@@ -221,6 +227,12 @@ public class SplashscreenContentDrawer {
return this;
}
+ StartingWindowViewBuilder setIconBackground(int color) {
+ mIconBackground = color;
+ mBuildComplete = false;
+ return this;
+ }
+
StartingWindowViewBuilder setContext(Context context) {
mContext = context;
mBuildComplete = false;
@@ -244,7 +256,9 @@ public class SplashscreenContentDrawer {
if (DEBUG) {
Slog.d(TAG, "The icon is not an AdaptiveIconDrawable");
}
- mFinalIconDrawable = mIconDrawable;
+ mFinalIconDrawable = SplashscreenIconDrawableFactory.makeIconDrawable(
+ mIconBackground != Color.TRANSPARENT
+ ? mIconBackground : mThemeColor, mIconDrawable);
}
final int iconSize = mFinalIconDrawable != null ? (int) (mIconSize * mScale) : 0;
mCachedResult = fillViewWithIcon(mContext, iconSize, mFinalIconDrawable);
@@ -252,6 +266,12 @@ public class SplashscreenContentDrawer {
return mCachedResult;
}
+ private void createIconDrawable(Drawable iconDrawable) {
+ mFinalIconDrawable = SplashscreenIconDrawableFactory.makeIconDrawable(
+ mIconBackground != Color.TRANSPARENT
+ ? mIconBackground : mThemeColor, iconDrawable);
+ }
+
private void processThemeColor() {
final DrawableColorTester themeBGTester =
new DrawableColorTester(mThemeBGDrawable, true /* filterTransparent */);
@@ -307,8 +327,7 @@ public class SplashscreenContentDrawer {
}
// Using AdaptiveIconDrawable here can help keep the shape consistent with the
// current settings.
- mFinalIconDrawable = new AdaptiveIconDrawable(
- new ColorDrawable(mThemeColor), iconForeground);
+ createIconDrawable(iconForeground);
// Reference AdaptiveIcon description, outer is 108 and inner is 72, so we
// should enlarge the size 108/72 if we only draw adaptiveIcon's foreground.
if (foreIconTester.nonTransparentRatio() < ENLARGE_FOREGROUND_ICON_THRESHOLD) {
@@ -318,7 +337,11 @@ public class SplashscreenContentDrawer {
if (DEBUG) {
Slog.d(TAG, "makeSplashScreenContentView: draw whole icon");
}
- mFinalIconDrawable = adaptiveIconDrawable;
+ if (mIconBackground != Color.TRANSPARENT) {
+ createIconDrawable(adaptiveIconDrawable);
+ } else {
+ mFinalIconDrawable = adaptiveIconDrawable;
+ }
}
return true;
}
@@ -326,7 +349,8 @@ public class SplashscreenContentDrawer {
private SplashScreenView fillViewWithIcon(Context context,
int iconSize, Drawable iconDrawable) {
final SplashScreenView.Builder builder = new SplashScreenView.Builder(context);
- builder.setIconSize(iconSize).setBackgroundColor(mThemeColor);
+ builder.setIconSize(iconSize).setBackgroundColor(mThemeColor)
+ .setIconBackground(mIconBackground);
if (iconDrawable != null) {
builder.setCenterViewDrawable(iconDrawable);
}
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
new file mode 100644
index 000000000000..8626dbc6d724
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.startingsurface;
+
+import android.animation.Animator;
+import android.animation.ValueAnimator;
+import android.annotation.ColorInt;
+import android.annotation.NonNull;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.Animatable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.SystemClock;
+import android.util.PathParser;
+import android.window.SplashScreenView;
+
+import com.android.internal.R;
+
+import java.util.function.Consumer;
+
+/**
+ * Creating a lightweight Drawable object used for splash screen.
+ * @hide
+ */
+public class SplashscreenIconDrawableFactory {
+
+ static Drawable makeIconDrawable(@ColorInt int backgroundColor,
+ @NonNull Drawable foregroundDrawable) {
+ if (foregroundDrawable instanceof Animatable) {
+ return new AnimatableIconDrawable(backgroundColor, foregroundDrawable);
+ } else {
+ // TODO make a light weight drawable instead of AdaptiveIconDrawable
+ return new AdaptiveIconDrawable(new ColorDrawable(backgroundColor), foregroundDrawable);
+ }
+ }
+
+ /**
+ * 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 {
+ 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();
+ /**
+ * 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 Drawable mForegroundDrawable;
+ private Animatable mAnimatableIcon;
+ private Animator mIconAnimator;
+ private boolean mAnimationTriggered;
+ private long mIconAnimationStart;
+
+ AnimatableIconDrawable(@ColorInt int backgroundColor, Drawable foregroundDrawable) {
+ 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, Consumer<Long> startListener) {
+ mAnimatableIcon = (Animatable) mForegroundDrawable;
+ mIconAnimator = ValueAnimator.ofInt(0, 1);
+ mIconAnimator.setDuration(duration);
+ mIconAnimator.addListener(new Animator.AnimatorListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mIconAnimationStart = SystemClock.uptimeMillis();
+ if (startListener != null) {
+ startListener.accept(mIconAnimationStart);
+ }
+ mAnimatableIcon.start();
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mAnimatableIcon.stop();
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mAnimatableIcon.stop();
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ // do not repeat
+ mAnimatableIcon.stop();
+ }
+ });
+ return true;
+ }
+
+ @Override
+ protected void onBoundsChange(Rect bounds) {
+ if (bounds.isEmpty()) {
+ return;
+ }
+ updateLayerBounds(bounds);
+ }
+
+ private final Callback mCallback = new Callback() {
+ @Override
+ public void invalidateDrawable(@NonNull Drawable who) {
+ invalidateSelf();
+ }
+
+ @Override
+ public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
+ scheduleSelf(what, when);
+ }
+
+ @Override
+ public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
+ unscheduleSelf(what);
+ }
+ };
+
+ 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;
+ }
+ if (mIconAnimator != null && !mIconAnimator.isRunning()) {
+ mIconAnimator.start();
+ }
+ mAnimationTriggered = true;
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ if (mMaskScaleOnly != null) {
+ canvas.drawPath(mMaskScaleOnly, mPaint);
+ }
+ 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;
+ }
+ }
+
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index 9212c4b86105..b592121e98c0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -362,7 +362,6 @@ public class StartingSurfaceDrawer {
final StartingWindowRecord record = mStartingWindowRecords.get(taskId);
if (record != null) {
record.setSplashScreenView(splashScreenView);
- splashScreenView.startIntroAnimation();
}
}