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 3f46fee26510c..ae6797bcf27e9 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 @@ -70,6 +70,7 @@ public class SplashscreenContentDrawer { private int mIconNormalExitDistance; private int mIconEarlyExitDistance; private final TransactionPool mTransactionPool; + private final SplashScreenWindowAttrs mTmpAttrs = new SplashScreenWindowAttrs(); SplashscreenContentDrawer(Context context, int maxAnimatableIconDuration, int iconExitAnimDuration, int appRevealAnimDuration, TransactionPool pool) { @@ -109,49 +110,79 @@ public class SplashscreenContentDrawer { return new ColorDrawable(getSystemBGColor()); } - SplashScreenView makeSplashScreenContentView(Context context, int iconRes, - int splashscreenContentResId) { - updateDensity(); - // splash screen content will be deprecated after S. - final SplashScreenView ssc = - makeSplashscreenContentDrawable(context, splashscreenContentResId); - - if (ssc != null) { - return ssc; - } - - final SplashScreenWindowAttrs attrs = - SplashScreenWindowAttrs.createWindowAttrs(context); - final StartingWindowViewBuilder builder = new StartingWindowViewBuilder(); + @ColorInt int peekWindowBGColor(Context context) { final Drawable themeBGDrawable; - - if (attrs.mWindowBgColor != 0) { - themeBGDrawable = new ColorDrawable(attrs.mWindowBgColor); - } else if (attrs.mWindowBgResId != 0) { - themeBGDrawable = context.getDrawable(attrs.mWindowBgResId); + if (mTmpAttrs.mWindowBgColor != 0) { + themeBGDrawable = new ColorDrawable(mTmpAttrs.mWindowBgColor); + } else if (mTmpAttrs.mWindowBgResId != 0) { + themeBGDrawable = context.getDrawable(mTmpAttrs.mWindowBgResId); } else { Slog.w(TAG, "Window background not exist!"); themeBGDrawable = createDefaultBackgroundDrawable(); } + return estimateWindowBGColor(themeBGDrawable); + } + + private int estimateWindowBGColor(Drawable themeBGDrawable) { + final DrawableColorTester themeBGTester = + new DrawableColorTester(themeBGDrawable, true /* filterTransparent */); + if (themeBGTester.nonTransparentRatio() == 0) { + // the window background is transparent, unable to draw + Slog.w(TAG, "Window background is transparent, fill background with black color"); + return getSystemBGColor(); + } else { + return themeBGTester.getDominateColor(); + } + } + + SplashScreenView makeSplashScreenContentView(Context context, int iconRes) { + updateDensity(); + + getWindowAttrs(context, mTmpAttrs); + final StartingWindowViewBuilder builder = new StartingWindowViewBuilder(); final int animationDuration; final Drawable iconDrawable; - if (attrs.mReplaceIcon != null) { - iconDrawable = attrs.mReplaceIcon; + if (mTmpAttrs.mReplaceIcon != null) { + iconDrawable = mTmpAttrs.mReplaceIcon; animationDuration = Math.max(0, - Math.min(attrs.mAnimationDuration, mMaxAnimatableIconDuration)); + Math.min(mTmpAttrs.mAnimationDuration, mMaxAnimatableIconDuration)); } else { iconDrawable = iconRes != 0 ? context.getDrawable(iconRes) : context.getPackageManager().getDefaultActivityIcon(); animationDuration = 0; } + final int themeBGColor = peekWindowBGColor(context); // TODO (b/173975965) Tracking the performance on improved splash screen. return builder .setContext(context) - .setThemeDrawable(themeBGDrawable) + .setWindowBGColor(themeBGColor) .setIconDrawable(iconDrawable) .setIconAnimationDuration(animationDuration) - .setBrandingDrawable(attrs.mBrandingImage) - .setIconBackground(attrs.mIconBgColor).build(); + .setBrandingDrawable(mTmpAttrs.mBrandingImage) + .setIconBackground(mTmpAttrs.mIconBgColor).build(); + } + + private static void getWindowAttrs(Context context, SplashScreenWindowAttrs attrs) { + final TypedArray typedArray = context.obtainStyledAttributes( + com.android.internal.R.styleable.Window); + attrs.mWindowBgResId = typedArray.getResourceId(R.styleable.Window_windowBackground, 0); + attrs.mWindowBgColor = typedArray.getColor( + R.styleable.Window_windowSplashScreenBackground, Color.TRANSPARENT); + attrs.mReplaceIcon = typedArray.getDrawable( + R.styleable.Window_windowSplashScreenAnimatedIcon); + attrs.mAnimationDuration = typedArray.getInt( + 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: " + + Integer.toHexString(attrs.mWindowBgColor) + + " icon " + attrs.mReplaceIcon + " duration " + attrs.mAnimationDuration + + " brandImage " + attrs.mBrandingImage); + } } static class SplashScreenWindowAttrs { @@ -161,35 +192,9 @@ public class SplashscreenContentDrawer { private Drawable mBrandingImage = null; private int mIconBgColor = Color.TRANSPARENT; private int mAnimationDuration = 0; - - static SplashScreenWindowAttrs createWindowAttrs(Context context) { - final SplashScreenWindowAttrs attrs = new SplashScreenWindowAttrs(); - final TypedArray typedArray = context.obtainStyledAttributes( - com.android.internal.R.styleable.Window); - attrs.mWindowBgResId = typedArray.getResourceId(R.styleable.Window_windowBackground, 0); - attrs.mWindowBgColor = typedArray.getColor( - R.styleable.Window_windowSplashScreenBackground, Color.TRANSPARENT); - attrs.mReplaceIcon = typedArray.getDrawable( - R.styleable.Window_windowSplashScreenAnimatedIcon); - attrs.mAnimationDuration = typedArray.getInt( - 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: " - + Integer.toHexString(attrs.mWindowBgColor) - + " icon " + attrs.mReplaceIcon + " duration " + attrs.mAnimationDuration - + " brandImage " + attrs.mBrandingImage); - } - return attrs; - } } private class StartingWindowViewBuilder { - private Drawable mThemeBGDrawable; private Drawable mIconDrawable; private int mIconAnimationDuration; private Context mContext; @@ -203,8 +208,8 @@ public class SplashscreenContentDrawer { private Drawable mFinalIconDrawable; private float mScale = 1f; - StartingWindowViewBuilder setThemeDrawable(Drawable background) { - mThemeBGDrawable = background; + StartingWindowViewBuilder setWindowBGColor(@ColorInt int background) { + mThemeColor = background; mBuildComplete = false; return this; } @@ -247,18 +252,14 @@ public class SplashscreenContentDrawer { Slog.e(TAG, "Unable to create StartingWindowView, lack of materials!"); return null; } - if (mThemeBGDrawable == null) { - Slog.w(TAG, "Theme Background Drawable is null, forget to set Theme Drawable?"); - mThemeBGDrawable = createDefaultBackgroundDrawable(); - } - processThemeColor(); + if (!processAdaptiveIcon() && mIconDrawable != null) { if (DEBUG) { Slog.d(TAG, "The icon is not an AdaptiveIconDrawable"); } mFinalIconDrawable = SplashscreenIconDrawableFactory.makeIconDrawable( mIconBackground != Color.TRANSPARENT - ? mIconBackground : mThemeColor, mIconDrawable); + ? mIconBackground : mThemeColor, mIconDrawable, mIconSize); } final int iconSize = mFinalIconDrawable != null ? (int) (mIconSize * mScale) : 0; mCachedResult = fillViewWithIcon(mContext, iconSize, mFinalIconDrawable); @@ -266,22 +267,10 @@ public class SplashscreenContentDrawer { return mCachedResult; } - private void createIconDrawable(Drawable iconDrawable) { + private void createIconDrawable(Drawable iconDrawable, int iconSize) { mFinalIconDrawable = SplashscreenIconDrawableFactory.makeIconDrawable( mIconBackground != Color.TRANSPARENT - ? mIconBackground : mThemeColor, iconDrawable); - } - - private void processThemeColor() { - final DrawableColorTester themeBGTester = - new DrawableColorTester(mThemeBGDrawable, true /* filterTransparent */); - if (themeBGTester.nonTransparentRatio() == 0) { - // the window background is transparent, unable to draw - Slog.w(TAG, "Window background is transparent, fill background with black color"); - mThemeColor = getSystemBGColor(); - } else { - mThemeColor = themeBGTester.getDominateColor(); - } + ? mIconBackground : mThemeColor, iconDrawable, iconSize); } private boolean processAdaptiveIcon() { @@ -325,23 +314,20 @@ public class SplashscreenContentDrawer { if (DEBUG) { Slog.d(TAG, "makeSplashScreenContentView: choose fg icon"); } - // Using AdaptiveIconDrawable here can help keep the shape consistent with the - // current settings. - 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) { mScale = 1.5f; } + // Using AdaptiveIconDrawable here can help keep the shape consistent with the + // current settings. + final int iconSize = (int) (0.5f + mIconSize * mScale); + createIconDrawable(iconForeground, iconSize); } else { if (DEBUG) { Slog.d(TAG, "makeSplashScreenContentView: draw whole icon"); } - if (mIconBackground != Color.TRANSPARENT) { - createIconDrawable(adaptiveIconDrawable); - } else { - mFinalIconDrawable = adaptiveIconDrawable; - } + createIconDrawable(adaptiveIconDrawable, mIconSize); } return true; } @@ -395,7 +381,7 @@ public class SplashscreenContentDrawer { return root < 0.1; } - private static SplashScreenView makeSplashscreenContentDrawable(Context ctx, + static SplashScreenView makeSplashscreenContent(Context ctx, int splashscreenContentResId) { // doesn't support windowSplashscreenContent after S // TODO add an allowlist to skip some packages if needed 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 8626dbc6d7243..a4a83eb87b946 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 @@ -16,11 +16,15 @@ package com.android.wm.shell.startingsurface; +import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; + import android.animation.Animator; import android.animation.ValueAnimator; import android.annotation.ColorInt; import android.annotation.NonNull; import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapShader; import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.Matrix; @@ -28,11 +32,13 @@ import android.graphics.Paint; import android.graphics.Path; import android.graphics.PixelFormat; import android.graphics.Rect; +import android.graphics.Shader; 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.os.Trace; import android.util.PathParser; import android.window.SplashScreenView; @@ -47,12 +53,57 @@ import java.util.function.Consumer; public class SplashscreenIconDrawableFactory { static Drawable makeIconDrawable(@ColorInt int backgroundColor, - @NonNull Drawable foregroundDrawable) { + @NonNull Drawable foregroundDrawable, int iconSize) { if (foregroundDrawable instanceof Animatable) { return new AnimatableIconDrawable(backgroundColor, foregroundDrawable); + } else if (foregroundDrawable instanceof AdaptiveIconDrawable) { + return new ImmobileIconDrawable((AdaptiveIconDrawable) foregroundDrawable, iconSize); } else { - // TODO make a light weight drawable instead of AdaptiveIconDrawable - return new AdaptiveIconDrawable(new ColorDrawable(backgroundColor), foregroundDrawable); + return new ImmobileIconDrawable(new AdaptiveIconDrawable( + new ColorDrawable(backgroundColor), foregroundDrawable), iconSize); + } + } + + private static class ImmobileIconDrawable extends Drawable { + private Shader mLayersShader; + private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG + | Paint.FILTER_BITMAP_FLAG); + + ImmobileIconDrawable(AdaptiveIconDrawable drawable, int iconSize) { + cachePaint(drawable, iconSize, iconSize); + } + + private void cachePaint(AdaptiveIconDrawable drawable, int width, int height) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "cachePaint"); + final Bitmap layersBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + final Canvas canvas = new Canvas(layersBitmap); + drawable.setBounds(0, 0, width, height); + drawable.draw(canvas); + mLayersShader = new BitmapShader(layersBitmap, Shader.TileMode.CLAMP, + Shader.TileMode.CLAMP); + mPaint.setShader(mLayersShader); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + } + + @Override + public void draw(Canvas canvas) { + final Rect bounds = getBounds(); + canvas.drawRect(bounds, mPaint); + } + + @Override + public void setAlpha(int alpha) { + + } + + @Override + public void setColorFilter(ColorFilter colorFilter) { + + } + + @Override + public int getOpacity() { + return 1; } } 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 b592121e98c0c..c9533fed0bc8e 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 @@ -267,21 +267,31 @@ public class StartingSurfaceDrawer { final int taskId = taskInfo.taskId; SplashScreenView sView = null; try { - sView = mSplashscreenContentDrawer.makeSplashScreenContentView(context, iconRes, - splashscreenContentResId[0]); final View view = win.getDecorView(); final WindowManager wm = mContext.getSystemService(WindowManager.class); - if (postAddWindow(taskId, appToken, view, wm, params)) { + // splash screen content will be deprecated after S. + sView = SplashscreenContentDrawer.makeSplashscreenContent( + context, splashscreenContentResId[0]); + final boolean splashscreenContentCompatible = sView != null; + if (splashscreenContentCompatible) { + win.setContentView(sView); + } else { + sView = mSplashscreenContentDrawer.makeSplashScreenContentView(context, iconRes); win.setContentView(sView); sView.cacheRootWindow(win); } + postAddWindow(taskId, appToken, view, wm, params); } catch (RuntimeException e) { // don't crash if something else bad happens, for example a // failure loading resources because we are loading from an app // on external storage that has been unmounted. - Slog.w(TAG, " failed creating starting window", e); + Slog.w(TAG, " failed creating starting window at taskId: " + taskId, e); + sView = null; } finally { - setSplashScreenRecord(taskId, sView); + final StartingWindowRecord record = mStartingWindowRecords.get(taskId); + if (record != null) { + record.setSplashScreenView(sView); + } } } @@ -328,7 +338,7 @@ public class StartingSurfaceDrawer { ActivityTaskManager.getInstance().onSplashScreenViewCopyFinished(taskId, parcelable); } - protected boolean postAddWindow(int taskId, IBinder appToken, View view, WindowManager wm, + protected void postAddWindow(int taskId, IBinder appToken, View view, WindowManager wm, WindowManager.LayoutParams params) { boolean shouldSaveView = true; try { @@ -349,7 +359,6 @@ public class StartingSurfaceDrawer { removeWindowNoAnimate(taskId); saveSplashScreenRecord(taskId, view); } - return shouldSaveView; } private void saveSplashScreenRecord(int taskId, View view) { @@ -358,13 +367,6 @@ public class StartingSurfaceDrawer { mStartingWindowRecords.put(taskId, tView); } - private void setSplashScreenRecord(int taskId, SplashScreenView splashScreenView) { - final StartingWindowRecord record = mStartingWindowRecords.get(taskId); - if (record != null) { - record.setSplashScreenView(splashScreenView); - } - } - private void removeWindowNoAnimate(int taskId) { removeWindowSynced(taskId, null, null, false); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java index 207db9e805117..8ae0a73565747 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java @@ -83,12 +83,11 @@ public class StartingSurfaceDrawerTests { } @Override - protected boolean postAddWindow(int taskId, IBinder appToken, + protected void postAddWindow(int taskId, IBinder appToken, View view, WindowManager wm, WindowManager.LayoutParams params) { // listen for addView mAddWindowForTask = taskId; mViewThemeResId = view.getContext().getThemeResId(); - return true; } @Override