Merge "Extract the drawing logic from StartingSurfaceDrawer."
This commit is contained in:
committed by
Android (Google) Code Review
commit
941c58dee9
508
core/java/android/window/SnapshotDrawerUtils.java
Normal file
508
core/java/android/window/SnapshotDrawerUtils.java
Normal file
@@ -0,0 +1,508 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 android.window;
|
||||
|
||||
import static android.graphics.Color.WHITE;
|
||||
import static android.graphics.Color.alpha;
|
||||
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
|
||||
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
|
||||
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
|
||||
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
|
||||
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST;
|
||||
|
||||
import static com.android.internal.policy.DecorView.NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES;
|
||||
import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES;
|
||||
import static com.android.internal.policy.DecorView.getNavigationBarRect;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityThread;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.GraphicBuffer;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.hardware.HardwareBuffer;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
import android.view.InsetsState;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.SurfaceSession;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowInsets;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.policy.DecorView;
|
||||
|
||||
/**
|
||||
* Utils class to help draw a snapshot on a surface.
|
||||
* @hide
|
||||
*/
|
||||
public class SnapshotDrawerUtils {
|
||||
private static final String TAG = "SnapshotDrawerUtils";
|
||||
|
||||
/**
|
||||
* When creating the starting window, we use the exact same layout flags such that we end up
|
||||
* with a window with the exact same dimensions etc. However, these flags are not used in layout
|
||||
* and might cause other side effects so we exclude them.
|
||||
*/
|
||||
static final int FLAG_INHERIT_EXCLUDES = FLAG_NOT_FOCUSABLE
|
||||
| FLAG_NOT_TOUCHABLE
|
||||
| FLAG_NOT_TOUCH_MODAL
|
||||
| FLAG_ALT_FOCUSABLE_IM
|
||||
| FLAG_NOT_FOCUSABLE
|
||||
| FLAG_HARDWARE_ACCELERATED
|
||||
| FLAG_IGNORE_CHEEK_PRESSES
|
||||
| FLAG_LOCAL_FOCUS_MODE
|
||||
| FLAG_SLIPPERY
|
||||
| FLAG_WATCH_OUTSIDE_TOUCH
|
||||
| FLAG_SPLIT_TOUCH
|
||||
| FLAG_SCALED
|
||||
| FLAG_SECURE;
|
||||
|
||||
private static final RectF sTmpSnapshotSize = new RectF();
|
||||
private static final RectF sTmpDstFrame = new RectF();
|
||||
|
||||
private static final Matrix sSnapshotMatrix = new Matrix();
|
||||
private static final float[] sTmpFloat9 = new float[9];
|
||||
private static final Paint sBackgroundPaint = new Paint();
|
||||
|
||||
/**
|
||||
* The internal object to hold the surface and drawing on it.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public static class SnapshotSurface {
|
||||
private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
|
||||
private final SurfaceControl mRootSurface;
|
||||
private final TaskSnapshot mSnapshot;
|
||||
private final CharSequence mTitle;
|
||||
|
||||
private SystemBarBackgroundPainter mSystemBarBackgroundPainter;
|
||||
private final Rect mTaskBounds;
|
||||
private final Rect mFrame = new Rect();
|
||||
private final Rect mSystemBarInsets = new Rect();
|
||||
private boolean mSizeMismatch;
|
||||
|
||||
public SnapshotSurface(SurfaceControl rootSurface, TaskSnapshot snapshot,
|
||||
CharSequence title,
|
||||
Rect taskBounds) {
|
||||
mRootSurface = rootSurface;
|
||||
mSnapshot = snapshot;
|
||||
mTitle = title;
|
||||
mTaskBounds = taskBounds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate system bar painter to draw the system bar background.
|
||||
*/
|
||||
void initiateSystemBarPainter(int windowFlags, int windowPrivateFlags,
|
||||
int appearance, ActivityManager.TaskDescription taskDescription,
|
||||
@WindowInsets.Type.InsetsType int requestedVisibleTypes) {
|
||||
mSystemBarBackgroundPainter = new SystemBarBackgroundPainter(windowFlags,
|
||||
windowPrivateFlags, appearance, taskDescription, 1f, requestedVisibleTypes);
|
||||
int backgroundColor = taskDescription.getBackgroundColor();
|
||||
sBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set frame size.
|
||||
*/
|
||||
void setFrames(Rect frame, Rect systemBarInsets) {
|
||||
mFrame.set(frame);
|
||||
mSystemBarInsets.set(systemBarInsets);
|
||||
final HardwareBuffer snapshot = mSnapshot.getHardwareBuffer();
|
||||
mSizeMismatch = (mFrame.width() != snapshot.getWidth()
|
||||
|| mFrame.height() != snapshot.getHeight());
|
||||
mSystemBarBackgroundPainter.setInsets(systemBarInsets);
|
||||
}
|
||||
|
||||
private void drawSnapshot(boolean releaseAfterDraw) {
|
||||
Log.v(TAG, "Drawing snapshot surface sizeMismatch=" + mSizeMismatch);
|
||||
if (mSizeMismatch) {
|
||||
// The dimensions of the buffer and the window don't match, so attaching the buffer
|
||||
// will fail. Better create a child window with the exact dimensions and fill the
|
||||
// parent window with the background color!
|
||||
drawSizeMismatchSnapshot();
|
||||
} else {
|
||||
drawSizeMatchSnapshot();
|
||||
}
|
||||
|
||||
// In case window manager leaks us, make sure we don't retain the snapshot.
|
||||
if (mSnapshot.getHardwareBuffer() != null) {
|
||||
mSnapshot.getHardwareBuffer().close();
|
||||
}
|
||||
if (releaseAfterDraw) {
|
||||
mRootSurface.release();
|
||||
}
|
||||
}
|
||||
|
||||
private void drawSizeMatchSnapshot() {
|
||||
mTransaction.setBuffer(mRootSurface, mSnapshot.getHardwareBuffer())
|
||||
.setColorSpace(mRootSurface, mSnapshot.getColorSpace())
|
||||
.apply();
|
||||
}
|
||||
|
||||
private void drawSizeMismatchSnapshot() {
|
||||
final HardwareBuffer buffer = mSnapshot.getHardwareBuffer();
|
||||
final SurfaceSession session = new SurfaceSession();
|
||||
|
||||
// We consider nearly matched dimensions as there can be rounding errors and the user
|
||||
// won't notice very minute differences from scaling one dimension more than the other
|
||||
final boolean aspectRatioMismatch = !isAspectRatioMatch(mFrame, mSnapshot);
|
||||
|
||||
// Keep a reference to it such that it doesn't get destroyed when finalized.
|
||||
SurfaceControl childSurfaceControl = new SurfaceControl.Builder(session)
|
||||
.setName(mTitle + " - task-snapshot-surface")
|
||||
.setBLASTLayer()
|
||||
.setFormat(buffer.getFormat())
|
||||
.setParent(mRootSurface)
|
||||
.setCallsite("TaskSnapshotWindow.drawSizeMismatchSnapshot")
|
||||
.build();
|
||||
|
||||
final Rect frame;
|
||||
// We can just show the surface here as it will still be hidden as the parent is
|
||||
// still hidden.
|
||||
mTransaction.show(childSurfaceControl);
|
||||
if (aspectRatioMismatch) {
|
||||
// Clip off ugly navigation bar.
|
||||
final Rect crop = calculateSnapshotCrop();
|
||||
frame = calculateSnapshotFrame(crop);
|
||||
mTransaction.setWindowCrop(childSurfaceControl, crop);
|
||||
mTransaction.setPosition(childSurfaceControl, frame.left, frame.top);
|
||||
sTmpSnapshotSize.set(crop);
|
||||
sTmpDstFrame.set(frame);
|
||||
} else {
|
||||
frame = null;
|
||||
sTmpSnapshotSize.set(0, 0, buffer.getWidth(), buffer.getHeight());
|
||||
sTmpDstFrame.set(mFrame);
|
||||
sTmpDstFrame.offsetTo(0, 0);
|
||||
}
|
||||
|
||||
// Scale the mismatch dimensions to fill the task bounds
|
||||
sSnapshotMatrix.setRectToRect(sTmpSnapshotSize, sTmpDstFrame, Matrix.ScaleToFit.FILL);
|
||||
mTransaction.setMatrix(childSurfaceControl, sSnapshotMatrix, sTmpFloat9);
|
||||
mTransaction.setColorSpace(childSurfaceControl, mSnapshot.getColorSpace());
|
||||
mTransaction.setBuffer(childSurfaceControl, mSnapshot.getHardwareBuffer());
|
||||
|
||||
if (aspectRatioMismatch) {
|
||||
GraphicBuffer background = GraphicBuffer.create(mFrame.width(), mFrame.height(),
|
||||
PixelFormat.RGBA_8888,
|
||||
GraphicBuffer.USAGE_HW_TEXTURE | GraphicBuffer.USAGE_HW_COMPOSER
|
||||
| GraphicBuffer.USAGE_SW_WRITE_RARELY);
|
||||
// TODO: Support this on HardwareBuffer
|
||||
final Canvas c = background.lockCanvas();
|
||||
drawBackgroundAndBars(c, frame);
|
||||
background.unlockCanvasAndPost(c);
|
||||
mTransaction.setBuffer(mRootSurface,
|
||||
HardwareBuffer.createFromGraphicBuffer(background));
|
||||
}
|
||||
mTransaction.apply();
|
||||
childSurfaceControl.release();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the snapshot crop in snapshot coordinate space.
|
||||
*
|
||||
* @return crop rect in snapshot coordinate space.
|
||||
*/
|
||||
Rect calculateSnapshotCrop() {
|
||||
final Rect rect = new Rect();
|
||||
final HardwareBuffer snapshot = mSnapshot.getHardwareBuffer();
|
||||
rect.set(0, 0, snapshot.getWidth(), snapshot.getHeight());
|
||||
final Rect insets = mSnapshot.getContentInsets();
|
||||
|
||||
final float scaleX = (float) snapshot.getWidth() / mSnapshot.getTaskSize().x;
|
||||
final float scaleY = (float) snapshot.getHeight() / mSnapshot.getTaskSize().y;
|
||||
|
||||
// Let's remove all system decorations except the status bar, but only if the task is at
|
||||
// the very top of the screen.
|
||||
final boolean isTop = mTaskBounds.top == 0 && mFrame.top == 0;
|
||||
rect.inset((int) (insets.left * scaleX),
|
||||
isTop ? 0 : (int) (insets.top * scaleY),
|
||||
(int) (insets.right * scaleX),
|
||||
(int) (insets.bottom * scaleY));
|
||||
return rect;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the snapshot frame in window coordinate space from crop.
|
||||
*
|
||||
* @param crop rect that is in snapshot coordinate space.
|
||||
*/
|
||||
Rect calculateSnapshotFrame(Rect crop) {
|
||||
final HardwareBuffer snapshot = mSnapshot.getHardwareBuffer();
|
||||
final float scaleX = (float) snapshot.getWidth() / mSnapshot.getTaskSize().x;
|
||||
final float scaleY = (float) snapshot.getHeight() / mSnapshot.getTaskSize().y;
|
||||
|
||||
// Rescale the frame from snapshot to window coordinate space
|
||||
final Rect frame = new Rect(0, 0,
|
||||
(int) (crop.width() / scaleX + 0.5f),
|
||||
(int) (crop.height() / scaleY + 0.5f)
|
||||
);
|
||||
|
||||
// However, we also need to make space for the navigation bar on the left side.
|
||||
frame.offset(mSystemBarInsets.left, 0);
|
||||
return frame;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw status bar and navigation bar background.
|
||||
*/
|
||||
void drawBackgroundAndBars(Canvas c, Rect frame) {
|
||||
final int statusBarHeight = mSystemBarBackgroundPainter.getStatusBarColorViewHeight();
|
||||
final boolean fillHorizontally = c.getWidth() > frame.right;
|
||||
final boolean fillVertically = c.getHeight() > frame.bottom;
|
||||
if (fillHorizontally) {
|
||||
c.drawRect(frame.right, alpha(mSystemBarBackgroundPainter.mStatusBarColor) == 0xFF
|
||||
? statusBarHeight : 0, c.getWidth(), fillVertically
|
||||
? frame.bottom : c.getHeight(), sBackgroundPaint);
|
||||
}
|
||||
if (fillVertically) {
|
||||
c.drawRect(0, frame.bottom, c.getWidth(), c.getHeight(), sBackgroundPaint);
|
||||
}
|
||||
mSystemBarBackgroundPainter.drawDecors(c, frame);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask system bar background painter to draw status bar background.
|
||||
*
|
||||
*/
|
||||
void drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame) {
|
||||
mSystemBarBackgroundPainter.drawStatusBarBackground(c, alreadyDrawnFrame,
|
||||
mSystemBarBackgroundPainter.getStatusBarColorViewHeight());
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask system bar background painter to draw navigation bar background.
|
||||
*
|
||||
*/
|
||||
void drawNavigationBarBackground(Canvas c) {
|
||||
mSystemBarBackgroundPainter.drawNavigationBarBackground(c);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the aspect ratio match between a frame and a snapshot buffer.
|
||||
*/
|
||||
public static boolean isAspectRatioMatch(Rect frame, TaskSnapshot snapshot) {
|
||||
if (frame.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
final HardwareBuffer buffer = snapshot.getHardwareBuffer();
|
||||
return Math.abs(
|
||||
((float) buffer.getWidth() / buffer.getHeight())
|
||||
- ((float) frame.width() / frame.height())) <= 0.01f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Help method to draw the snapshot on a surface.
|
||||
*/
|
||||
public static void drawSnapshotOnSurface(StartingWindowInfo info, WindowManager.LayoutParams lp,
|
||||
SurfaceControl rootSurface, TaskSnapshot snapshot,
|
||||
Rect configBounds, Rect windowBounds, InsetsState topWindowInsetsState,
|
||||
boolean releaseAfterDraw) {
|
||||
if (windowBounds.isEmpty()) {
|
||||
Log.e(TAG, "Unable to draw snapshot on an empty windowBounds");
|
||||
return;
|
||||
}
|
||||
final SnapshotSurface drawSurface = new SnapshotSurface(
|
||||
rootSurface, snapshot, lp.getTitle(), configBounds);
|
||||
|
||||
final WindowManager.LayoutParams attrs = info.topOpaqueWindowLayoutParams;
|
||||
final ActivityManager.RunningTaskInfo runningTaskInfo = info.taskInfo;
|
||||
final ActivityManager.TaskDescription taskDescription;
|
||||
if (runningTaskInfo.taskDescription != null) {
|
||||
taskDescription = runningTaskInfo.taskDescription;
|
||||
} else {
|
||||
taskDescription = new ActivityManager.TaskDescription();
|
||||
taskDescription.setBackgroundColor(WHITE);
|
||||
}
|
||||
drawSurface.initiateSystemBarPainter(lp.flags, lp.privateFlags,
|
||||
attrs.insetsFlags.appearance, taskDescription, info.requestedVisibleTypes);
|
||||
final Rect systemBarInsets = getSystemBarInsets(windowBounds, topWindowInsetsState);
|
||||
drawSurface.setFrames(windowBounds, systemBarInsets);
|
||||
drawSurface.drawSnapshot(releaseAfterDraw);
|
||||
}
|
||||
|
||||
/**
|
||||
* Help method to create a layout parameters for a window.
|
||||
*/
|
||||
public static WindowManager.LayoutParams createLayoutParameters(StartingWindowInfo info,
|
||||
CharSequence title, @WindowManager.LayoutParams.WindowType int windowType,
|
||||
int pixelFormat, IBinder token) {
|
||||
final WindowManager.LayoutParams attrs = info.topOpaqueWindowLayoutParams;
|
||||
final WindowManager.LayoutParams mainWindowParams = info.mainWindowLayoutParams;
|
||||
final InsetsState topWindowInsetsState = info.topOpaqueWindowInsetsState;
|
||||
if (attrs == null || mainWindowParams == null || topWindowInsetsState == null) {
|
||||
Log.w(TAG, "unable to create taskSnapshot surface ");
|
||||
return null;
|
||||
}
|
||||
final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
|
||||
|
||||
final int appearance = attrs.insetsFlags.appearance;
|
||||
final int windowFlags = attrs.flags;
|
||||
final int windowPrivateFlags = attrs.privateFlags;
|
||||
|
||||
layoutParams.packageName = mainWindowParams.packageName;
|
||||
layoutParams.windowAnimations = mainWindowParams.windowAnimations;
|
||||
layoutParams.dimAmount = mainWindowParams.dimAmount;
|
||||
layoutParams.type = windowType;
|
||||
layoutParams.format = pixelFormat;
|
||||
layoutParams.flags = (windowFlags & ~FLAG_INHERIT_EXCLUDES)
|
||||
| FLAG_NOT_FOCUSABLE
|
||||
| FLAG_NOT_TOUCHABLE;
|
||||
// Setting as trusted overlay to let touches pass through. This is safe because this
|
||||
// window is controlled by the system.
|
||||
layoutParams.privateFlags = (windowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS)
|
||||
| PRIVATE_FLAG_TRUSTED_OVERLAY | PRIVATE_FLAG_USE_BLAST;
|
||||
layoutParams.token = token;
|
||||
layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
layoutParams.insetsFlags.appearance = appearance;
|
||||
layoutParams.insetsFlags.behavior = attrs.insetsFlags.behavior;
|
||||
layoutParams.layoutInDisplayCutoutMode = attrs.layoutInDisplayCutoutMode;
|
||||
layoutParams.setFitInsetsTypes(attrs.getFitInsetsTypes());
|
||||
layoutParams.setFitInsetsSides(attrs.getFitInsetsSides());
|
||||
layoutParams.setFitInsetsIgnoringVisibility(attrs.isFitInsetsIgnoringVisibility());
|
||||
|
||||
layoutParams.setTitle(title);
|
||||
return layoutParams;
|
||||
}
|
||||
|
||||
static Rect getSystemBarInsets(Rect frame, InsetsState state) {
|
||||
return state.calculateInsets(frame, WindowInsets.Type.systemBars(),
|
||||
false /* ignoreVisibility */).toRect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class to draw the background of the system bars in regions the task snapshot isn't
|
||||
* filling the window.
|
||||
*/
|
||||
public static class SystemBarBackgroundPainter {
|
||||
private final Paint mStatusBarPaint = new Paint();
|
||||
private final Paint mNavigationBarPaint = new Paint();
|
||||
private final int mStatusBarColor;
|
||||
private final int mNavigationBarColor;
|
||||
private final int mWindowFlags;
|
||||
private final int mWindowPrivateFlags;
|
||||
private final float mScale;
|
||||
private final @WindowInsets.Type.InsetsType int mRequestedVisibleTypes;
|
||||
private final Rect mSystemBarInsets = new Rect();
|
||||
|
||||
public SystemBarBackgroundPainter(int windowFlags, int windowPrivateFlags, int appearance,
|
||||
ActivityManager.TaskDescription taskDescription, float scale,
|
||||
@WindowInsets.Type.InsetsType int requestedVisibleTypes) {
|
||||
mWindowFlags = windowFlags;
|
||||
mWindowPrivateFlags = windowPrivateFlags;
|
||||
mScale = scale;
|
||||
final Context context = ActivityThread.currentActivityThread().getSystemUiContext();
|
||||
final int semiTransparent = context.getColor(
|
||||
R.color.system_bar_background_semi_transparent);
|
||||
mStatusBarColor = DecorView.calculateBarColor(windowFlags, FLAG_TRANSLUCENT_STATUS,
|
||||
semiTransparent, taskDescription.getStatusBarColor(), appearance,
|
||||
APPEARANCE_LIGHT_STATUS_BARS,
|
||||
taskDescription.getEnsureStatusBarContrastWhenTransparent());
|
||||
mNavigationBarColor = DecorView.calculateBarColor(windowFlags,
|
||||
FLAG_TRANSLUCENT_NAVIGATION, semiTransparent,
|
||||
taskDescription.getNavigationBarColor(), appearance,
|
||||
APPEARANCE_LIGHT_NAVIGATION_BARS,
|
||||
taskDescription.getEnsureNavigationBarContrastWhenTransparent()
|
||||
&& context.getResources().getBoolean(
|
||||
R.bool.config_navBarNeedsScrim));
|
||||
mStatusBarPaint.setColor(mStatusBarColor);
|
||||
mNavigationBarPaint.setColor(mNavigationBarColor);
|
||||
mRequestedVisibleTypes = requestedVisibleTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set system bar insets.
|
||||
*/
|
||||
public void setInsets(Rect systemBarInsets) {
|
||||
mSystemBarInsets.set(systemBarInsets);
|
||||
}
|
||||
|
||||
int getStatusBarColorViewHeight() {
|
||||
final boolean forceBarBackground =
|
||||
(mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0;
|
||||
if (STATUS_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
|
||||
mRequestedVisibleTypes, mStatusBarColor, mWindowFlags,
|
||||
forceBarBackground)) {
|
||||
return (int) (mSystemBarInsets.top * mScale);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isNavigationBarColorViewVisible() {
|
||||
final boolean forceBarBackground =
|
||||
(mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0;
|
||||
return NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
|
||||
mRequestedVisibleTypes, mNavigationBarColor, mWindowFlags,
|
||||
forceBarBackground);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw bar colors to a canvas.
|
||||
*/
|
||||
public void drawDecors(Canvas c, @Nullable Rect alreadyDrawnFrame) {
|
||||
drawStatusBarBackground(c, alreadyDrawnFrame, getStatusBarColorViewHeight());
|
||||
drawNavigationBarBackground(c);
|
||||
}
|
||||
|
||||
void drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame,
|
||||
int statusBarHeight) {
|
||||
if (statusBarHeight > 0 && Color.alpha(mStatusBarColor) != 0
|
||||
&& (alreadyDrawnFrame == null || c.getWidth() > alreadyDrawnFrame.right)) {
|
||||
final int rightInset = (int) (mSystemBarInsets.right * mScale);
|
||||
final int left = alreadyDrawnFrame != null ? alreadyDrawnFrame.right : 0;
|
||||
c.drawRect(left, 0, c.getWidth() - rightInset, statusBarHeight,
|
||||
mStatusBarPaint);
|
||||
}
|
||||
}
|
||||
|
||||
void drawNavigationBarBackground(Canvas c) {
|
||||
final Rect navigationBarRect = new Rect();
|
||||
getNavigationBarRect(c.getWidth(), c.getHeight(), mSystemBarInsets, navigationBarRect,
|
||||
mScale);
|
||||
final boolean visible = isNavigationBarColorViewVisible();
|
||||
if (visible && Color.alpha(mNavigationBarColor) != 0
|
||||
&& !navigationBarRect.isEmpty()) {
|
||||
c.drawRect(navigationBarRect, mNavigationBarPaint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
* Copyright (C) 2022 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.
|
||||
@@ -14,22 +14,20 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.wm.shell.startingsurface;
|
||||
package android.window;
|
||||
|
||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
||||
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
|
||||
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.ActivityManager.TaskDescription;
|
||||
import android.content.ComponentName;
|
||||
@@ -42,33 +40,28 @@ import android.hardware.HardwareBuffer;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.WindowInsets;
|
||||
import android.window.TaskSnapshot;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.wm.shell.ShellTestCase;
|
||||
import com.android.wm.shell.TestShellExecutor;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* Test class for {@link TaskSnapshotWindow}.
|
||||
*
|
||||
* Test class for {@link SnapshotDrawerUtils}.
|
||||
*/
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class TaskSnapshotWindowTest extends ShellTestCase {
|
||||
public class SnapshotDrawerUtilsTest {
|
||||
|
||||
private TaskSnapshotWindow mWindow;
|
||||
private SnapshotDrawerUtils.SnapshotSurface mSnapshotSurface;
|
||||
|
||||
private void setupSurface(int width, int height) {
|
||||
setupSurface(width, height, new Rect(), 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
|
||||
setupSurface(width, height, new Rect(), FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
|
||||
new Rect(0, 0, width, height));
|
||||
}
|
||||
|
||||
private void setupSurface(int width, int height, Rect contentInsets, int sysuiVis,
|
||||
private void setupSurface(int width, int height, Rect contentInsets,
|
||||
int windowFlags, Rect taskBounds) {
|
||||
// Previously when constructing TaskSnapshots for this test, scale was 1.0f, so to mimic
|
||||
// this behavior set the taskSize to be the same as the taskBounds width and height. The
|
||||
@@ -80,12 +73,13 @@ public class TaskSnapshotWindowTest extends ShellTestCase {
|
||||
Point taskSize = new Point(taskBounds.width(), taskBounds.height());
|
||||
|
||||
final TaskSnapshot snapshot = createTaskSnapshot(width, height, taskSize, contentInsets);
|
||||
mWindow = new TaskSnapshotWindow(new SurfaceControl(), snapshot, "Test",
|
||||
createTaskDescription(Color.WHITE, Color.RED, Color.BLUE),
|
||||
0 /* appearance */, windowFlags /* windowFlags */, 0 /* privateWindowFlags */,
|
||||
taskBounds, ORIENTATION_PORTRAIT, ACTIVITY_TYPE_STANDARD,
|
||||
WindowInsets.Type.defaultVisible(), null /* clearWindow */,
|
||||
new TestShellExecutor());
|
||||
TaskDescription taskDescription = createTaskDescription(Color.WHITE,
|
||||
Color.RED, Color.BLUE);
|
||||
|
||||
mSnapshotSurface = new SnapshotDrawerUtils.SnapshotSurface(
|
||||
new SurfaceControl(), snapshot, "Test", taskBounds);
|
||||
mSnapshotSurface.initiateSystemBarPainter(windowFlags, 0, 0,
|
||||
taskDescription, WindowInsets.Type.defaultVisible());
|
||||
}
|
||||
|
||||
private TaskSnapshot createTaskSnapshot(int width, int height, Point taskSize,
|
||||
@@ -101,8 +95,8 @@ public class TaskSnapshotWindowTest extends ShellTestCase {
|
||||
0 /* systemUiVisibility */, false /* isTranslucent */, false /* hasImeSurface */);
|
||||
}
|
||||
|
||||
private static TaskDescription createTaskDescription(int background, int statusBar,
|
||||
int navigationBar) {
|
||||
private static TaskDescription createTaskDescription(int background,
|
||||
int statusBar, int navigationBar) {
|
||||
final TaskDescription td = new TaskDescription();
|
||||
td.setBackgroundColor(background);
|
||||
td.setStatusBarColor(statusBar);
|
||||
@@ -116,7 +110,7 @@ public class TaskSnapshotWindowTest extends ShellTestCase {
|
||||
final Canvas mockCanvas = mock(Canvas.class);
|
||||
when(mockCanvas.getWidth()).thenReturn(200);
|
||||
when(mockCanvas.getHeight()).thenReturn(100);
|
||||
mWindow.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 200));
|
||||
mSnapshotSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 200));
|
||||
verify(mockCanvas).drawRect(eq(100.0f), eq(0.0f), eq(200.0f), eq(100.0f), any());
|
||||
}
|
||||
|
||||
@@ -126,7 +120,7 @@ public class TaskSnapshotWindowTest extends ShellTestCase {
|
||||
final Canvas mockCanvas = mock(Canvas.class);
|
||||
when(mockCanvas.getWidth()).thenReturn(100);
|
||||
when(mockCanvas.getHeight()).thenReturn(200);
|
||||
mWindow.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 200, 100));
|
||||
mSnapshotSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 200, 100));
|
||||
verify(mockCanvas).drawRect(eq(0.0f), eq(100.0f), eq(100.0f), eq(200.0f), any());
|
||||
}
|
||||
|
||||
@@ -136,7 +130,7 @@ public class TaskSnapshotWindowTest extends ShellTestCase {
|
||||
final Canvas mockCanvas = mock(Canvas.class);
|
||||
when(mockCanvas.getWidth()).thenReturn(200);
|
||||
when(mockCanvas.getHeight()).thenReturn(200);
|
||||
mWindow.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 100));
|
||||
mSnapshotSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 100));
|
||||
verify(mockCanvas).drawRect(eq(100.0f), eq(0.0f), eq(200.0f), eq(100.0f), any());
|
||||
verify(mockCanvas).drawRect(eq(0.0f), eq(100.0f), eq(200.0f), eq(200.0f), any());
|
||||
}
|
||||
@@ -147,7 +141,7 @@ public class TaskSnapshotWindowTest extends ShellTestCase {
|
||||
final Canvas mockCanvas = mock(Canvas.class);
|
||||
when(mockCanvas.getWidth()).thenReturn(100);
|
||||
when(mockCanvas.getHeight()).thenReturn(100);
|
||||
mWindow.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 100));
|
||||
mSnapshotSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 100));
|
||||
verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any());
|
||||
}
|
||||
|
||||
@@ -157,76 +151,76 @@ public class TaskSnapshotWindowTest extends ShellTestCase {
|
||||
final Canvas mockCanvas = mock(Canvas.class);
|
||||
when(mockCanvas.getWidth()).thenReturn(100);
|
||||
when(mockCanvas.getHeight()).thenReturn(100);
|
||||
mWindow.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 200, 200));
|
||||
mSnapshotSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 200, 200));
|
||||
verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculateSnapshotCrop() {
|
||||
setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, 0, new Rect(0, 0, 100, 100));
|
||||
assertEquals(new Rect(0, 0, 100, 90), mWindow.calculateSnapshotCrop());
|
||||
setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, new Rect(0, 0, 100, 100));
|
||||
assertEquals(new Rect(0, 0, 100, 90), mSnapshotSurface.calculateSnapshotCrop());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculateSnapshotCrop_taskNotOnTop() {
|
||||
setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, 0, new Rect(0, 50, 100, 150));
|
||||
assertEquals(new Rect(0, 10, 100, 90), mWindow.calculateSnapshotCrop());
|
||||
setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, new Rect(0, 50, 100, 150));
|
||||
assertEquals(new Rect(0, 10, 100, 90), mSnapshotSurface.calculateSnapshotCrop());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculateSnapshotCrop_navBarLeft() {
|
||||
setupSurface(100, 100, new Rect(10, 10, 0, 0), 0, 0, new Rect(0, 0, 100, 100));
|
||||
assertEquals(new Rect(10, 0, 100, 100), mWindow.calculateSnapshotCrop());
|
||||
setupSurface(100, 100, new Rect(10, 10, 0, 0), 0, new Rect(0, 0, 100, 100));
|
||||
assertEquals(new Rect(10, 0, 100, 100), mSnapshotSurface.calculateSnapshotCrop());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculateSnapshotCrop_navBarRight() {
|
||||
setupSurface(100, 100, new Rect(0, 10, 10, 0), 0, 0, new Rect(0, 0, 100, 100));
|
||||
assertEquals(new Rect(0, 0, 90, 100), mWindow.calculateSnapshotCrop());
|
||||
setupSurface(100, 100, new Rect(0, 10, 10, 0), 0, new Rect(0, 0, 100, 100));
|
||||
assertEquals(new Rect(0, 0, 90, 100), mSnapshotSurface.calculateSnapshotCrop());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculateSnapshotCrop_waterfall() {
|
||||
setupSurface(100, 100, new Rect(5, 10, 5, 10), 0, 0, new Rect(0, 0, 100, 100));
|
||||
assertEquals(new Rect(5, 0, 95, 90), mWindow.calculateSnapshotCrop());
|
||||
setupSurface(100, 100, new Rect(5, 10, 5, 10), 0, new Rect(0, 0, 100, 100));
|
||||
assertEquals(new Rect(5, 0, 95, 90), mSnapshotSurface.calculateSnapshotCrop());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculateSnapshotFrame() {
|
||||
setupSurface(100, 100);
|
||||
final Rect insets = new Rect(0, 10, 0, 10);
|
||||
mWindow.setFrames(new Rect(0, 0, 100, 100), insets);
|
||||
mSnapshotSurface.setFrames(new Rect(0, 0, 100, 100), insets);
|
||||
assertEquals(new Rect(0, 0, 100, 80),
|
||||
mWindow.calculateSnapshotFrame(new Rect(0, 10, 100, 90)));
|
||||
mSnapshotSurface.calculateSnapshotFrame(new Rect(0, 10, 100, 90)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculateSnapshotFrame_navBarLeft() {
|
||||
setupSurface(100, 100);
|
||||
final Rect insets = new Rect(10, 10, 0, 0);
|
||||
mWindow.setFrames(new Rect(0, 0, 100, 100), insets);
|
||||
mSnapshotSurface.setFrames(new Rect(0, 0, 100, 100), insets);
|
||||
assertEquals(new Rect(10, 0, 100, 90),
|
||||
mWindow.calculateSnapshotFrame(new Rect(10, 10, 100, 100)));
|
||||
mSnapshotSurface.calculateSnapshotFrame(new Rect(10, 10, 100, 100)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculateSnapshotFrame_waterfall() {
|
||||
setupSurface(100, 100, new Rect(5, 10, 5, 10), 0, 0, new Rect(0, 0, 100, 100));
|
||||
setupSurface(100, 100, new Rect(5, 10, 5, 10), 0, new Rect(0, 0, 100, 100));
|
||||
final Rect insets = new Rect(0, 10, 0, 10);
|
||||
mWindow.setFrames(new Rect(5, 0, 95, 100), insets);
|
||||
mSnapshotSurface.setFrames(new Rect(5, 0, 95, 100), insets);
|
||||
assertEquals(new Rect(0, 0, 90, 90),
|
||||
mWindow.calculateSnapshotFrame(new Rect(5, 0, 95, 90)));
|
||||
mSnapshotSurface.calculateSnapshotFrame(new Rect(5, 0, 95, 90)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDrawStatusBarBackground() {
|
||||
setupSurface(100, 100);
|
||||
final Rect insets = new Rect(0, 10, 10, 0);
|
||||
mWindow.setFrames(new Rect(0, 0, 100, 100), insets);
|
||||
mSnapshotSurface.setFrames(new Rect(0, 0, 100, 100), insets);
|
||||
final Canvas mockCanvas = mock(Canvas.class);
|
||||
when(mockCanvas.getWidth()).thenReturn(100);
|
||||
when(mockCanvas.getHeight()).thenReturn(100);
|
||||
mWindow.drawStatusBarBackground(mockCanvas, new Rect(0, 0, 50, 100));
|
||||
mSnapshotSurface.drawStatusBarBackground(mockCanvas, new Rect(0, 0, 50, 100));
|
||||
verify(mockCanvas).drawRect(eq(50.0f), eq(0.0f), eq(90.0f), eq(10.0f), any());
|
||||
}
|
||||
|
||||
@@ -234,11 +228,11 @@ public class TaskSnapshotWindowTest extends ShellTestCase {
|
||||
public void testDrawStatusBarBackground_nullFrame() {
|
||||
setupSurface(100, 100);
|
||||
final Rect insets = new Rect(0, 10, 10, 0);
|
||||
mWindow.setFrames(new Rect(0, 0, 100, 100), insets);
|
||||
mSnapshotSurface.setFrames(new Rect(0, 0, 100, 100), insets);
|
||||
final Canvas mockCanvas = mock(Canvas.class);
|
||||
when(mockCanvas.getWidth()).thenReturn(100);
|
||||
when(mockCanvas.getHeight()).thenReturn(100);
|
||||
mWindow.drawStatusBarBackground(mockCanvas, null);
|
||||
mSnapshotSurface.drawStatusBarBackground(mockCanvas, null);
|
||||
verify(mockCanvas).drawRect(eq(0.0f), eq(0.0f), eq(90.0f), eq(10.0f), any());
|
||||
}
|
||||
|
||||
@@ -246,50 +240,50 @@ public class TaskSnapshotWindowTest extends ShellTestCase {
|
||||
public void testDrawStatusBarBackground_nope() {
|
||||
setupSurface(100, 100);
|
||||
final Rect insets = new Rect(0, 10, 10, 0);
|
||||
mWindow.setFrames(new Rect(0, 0, 100, 100), insets);
|
||||
mSnapshotSurface.setFrames(new Rect(0, 0, 100, 100), insets);
|
||||
final Canvas mockCanvas = mock(Canvas.class);
|
||||
when(mockCanvas.getWidth()).thenReturn(100);
|
||||
when(mockCanvas.getHeight()).thenReturn(100);
|
||||
mWindow.drawStatusBarBackground(mockCanvas, new Rect(0, 0, 100, 100));
|
||||
mSnapshotSurface.drawStatusBarBackground(mockCanvas, new Rect(0, 0, 100, 100));
|
||||
verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDrawNavigationBarBackground() {
|
||||
final Rect insets = new Rect(0, 10, 0, 10);
|
||||
setupSurface(100, 100, insets, 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
|
||||
setupSurface(100, 100, insets, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
|
||||
new Rect(0, 0, 100, 100));
|
||||
mWindow.setFrames(new Rect(0, 0, 100, 100), insets);
|
||||
mSnapshotSurface.setFrames(new Rect(0, 0, 100, 100), insets);
|
||||
final Canvas mockCanvas = mock(Canvas.class);
|
||||
when(mockCanvas.getWidth()).thenReturn(100);
|
||||
when(mockCanvas.getHeight()).thenReturn(100);
|
||||
mWindow.drawNavigationBarBackground(mockCanvas);
|
||||
mSnapshotSurface.drawNavigationBarBackground(mockCanvas);
|
||||
verify(mockCanvas).drawRect(eq(new Rect(0, 90, 100, 100)), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDrawNavigationBarBackground_left() {
|
||||
final Rect insets = new Rect(10, 10, 0, 0);
|
||||
setupSurface(100, 100, insets, 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
|
||||
setupSurface(100, 100, insets, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
|
||||
new Rect(0, 0, 100, 100));
|
||||
mWindow.setFrames(new Rect(0, 0, 100, 100), insets);
|
||||
mSnapshotSurface.setFrames(new Rect(0, 0, 100, 100), insets);
|
||||
final Canvas mockCanvas = mock(Canvas.class);
|
||||
when(mockCanvas.getWidth()).thenReturn(100);
|
||||
when(mockCanvas.getHeight()).thenReturn(100);
|
||||
mWindow.drawNavigationBarBackground(mockCanvas);
|
||||
mSnapshotSurface.drawNavigationBarBackground(mockCanvas);
|
||||
verify(mockCanvas).drawRect(eq(new Rect(0, 0, 10, 100)), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDrawNavigationBarBackground_right() {
|
||||
final Rect insets = new Rect(0, 10, 10, 0);
|
||||
setupSurface(100, 100, insets, 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
|
||||
setupSurface(100, 100, insets, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
|
||||
new Rect(0, 0, 100, 100));
|
||||
mWindow.setFrames(new Rect(0, 0, 100, 100), insets);
|
||||
mSnapshotSurface.setFrames(new Rect(0, 0, 100, 100), insets);
|
||||
final Canvas mockCanvas = mock(Canvas.class);
|
||||
when(mockCanvas.getWidth()).thenReturn(100);
|
||||
when(mockCanvas.getHeight()).thenReturn(100);
|
||||
mWindow.drawNavigationBarBackground(mockCanvas);
|
||||
mSnapshotSurface.drawNavigationBarBackground(mockCanvas);
|
||||
verify(mockCanvas).drawRect(eq(new Rect(90, 0, 100, 100)), any());
|
||||
}
|
||||
}
|
||||
@@ -16,8 +16,10 @@
|
||||
|
||||
package com.android.wm.shell.startingsurface;
|
||||
|
||||
import static android.content.Context.CONTEXT_RESTRICTED;
|
||||
import static android.os.Process.THREAD_PRIORITY_TOP_APP_BOOST;
|
||||
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN;
|
||||
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN;
|
||||
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN;
|
||||
@@ -29,6 +31,7 @@ import android.annotation.ColorInt;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityThread;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
@@ -48,9 +51,11 @@ import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.LayerDrawable;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.IBinder;
|
||||
import android.os.SystemClock;
|
||||
import android.os.Trace;
|
||||
import android.os.UserHandle;
|
||||
@@ -58,7 +63,9 @@ import android.util.ArrayMap;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Slog;
|
||||
import android.view.ContextThemeWrapper;
|
||||
import android.view.Display;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.WindowManager;
|
||||
import android.window.SplashScreenView;
|
||||
import android.window.StartingWindowInfo;
|
||||
import android.window.StartingWindowInfo.StartingWindowType;
|
||||
@@ -133,6 +140,144 @@ public class SplashscreenContentDrawer {
|
||||
mColorCache = new ColorCache(mContext, mSplashscreenWorkerHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Help method to create a layout parameters for a window.
|
||||
*/
|
||||
static Context createContext(Context initContext, StartingWindowInfo windowInfo,
|
||||
int theme, @StartingWindowInfo.StartingWindowType int suggestType,
|
||||
DisplayManager displayManager) {
|
||||
final ActivityManager.RunningTaskInfo taskInfo = windowInfo.taskInfo;
|
||||
final ActivityInfo activityInfo = windowInfo.targetActivityInfo != null
|
||||
? windowInfo.targetActivityInfo
|
||||
: taskInfo.topActivityInfo;
|
||||
if (activityInfo == null || activityInfo.packageName == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final int displayId = taskInfo.displayId;
|
||||
final int taskId = taskInfo.taskId;
|
||||
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
|
||||
"addSplashScreen for package: %s with theme: %s for task: %d, suggestType: %d",
|
||||
activityInfo.packageName, Integer.toHexString(theme), taskId, suggestType);
|
||||
final Display display = displayManager.getDisplay(displayId);
|
||||
if (display == null) {
|
||||
// Can't show splash screen on requested display, so skip showing at all.
|
||||
return null;
|
||||
}
|
||||
Context context = displayId == DEFAULT_DISPLAY
|
||||
? initContext : initContext.createDisplayContext(display);
|
||||
if (context == null) {
|
||||
return null;
|
||||
}
|
||||
if (theme != context.getThemeResId()) {
|
||||
try {
|
||||
context = context.createPackageContextAsUser(activityInfo.packageName,
|
||||
CONTEXT_RESTRICTED, UserHandle.of(taskInfo.userId));
|
||||
context.setTheme(theme);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Slog.w(TAG, "Failed creating package context with package name "
|
||||
+ activityInfo.packageName + " for user " + taskInfo.userId, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
final Configuration taskConfig = taskInfo.getConfiguration();
|
||||
if (taskConfig.diffPublicOnly(context.getResources().getConfiguration()) != 0) {
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
|
||||
"addSplashScreen: creating context based on task Configuration %s",
|
||||
taskConfig);
|
||||
final Context overrideContext = context.createConfigurationContext(taskConfig);
|
||||
overrideContext.setTheme(theme);
|
||||
final TypedArray typedArray = overrideContext.obtainStyledAttributes(
|
||||
com.android.internal.R.styleable.Window);
|
||||
final int resId = typedArray.getResourceId(R.styleable.Window_windowBackground, 0);
|
||||
try {
|
||||
if (resId != 0 && overrideContext.getDrawable(resId) != null) {
|
||||
// We want to use the windowBackground for the override context if it is
|
||||
// available, otherwise we use the default one to make sure a themed starting
|
||||
// window is displayed for the app.
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
|
||||
"addSplashScreen: apply overrideConfig %s",
|
||||
taskConfig);
|
||||
context = overrideContext;
|
||||
}
|
||||
} catch (Resources.NotFoundException e) {
|
||||
Slog.w(TAG, "failed creating starting window for overrideConfig at taskId: "
|
||||
+ taskId, e);
|
||||
return null;
|
||||
}
|
||||
typedArray.recycle();
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the window layout parameters for splashscreen window.
|
||||
*/
|
||||
static WindowManager.LayoutParams createLayoutParameters(Context context,
|
||||
StartingWindowInfo windowInfo,
|
||||
@StartingWindowInfo.StartingWindowType int suggestType,
|
||||
CharSequence title, int pixelFormat, IBinder appToken) {
|
||||
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
|
||||
WindowManager.LayoutParams.TYPE_APPLICATION_STARTING);
|
||||
params.setFitInsetsSides(0);
|
||||
params.setFitInsetsTypes(0);
|
||||
params.format = pixelFormat;
|
||||
int windowFlags = WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
|
||||
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
|
||||
| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
|
||||
final TypedArray a = context.obtainStyledAttributes(R.styleable.Window);
|
||||
if (a.getBoolean(R.styleable.Window_windowShowWallpaper, false)) {
|
||||
windowFlags |= WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
|
||||
}
|
||||
if (suggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) {
|
||||
if (a.getBoolean(R.styleable.Window_windowDrawsSystemBarBackgrounds, false)) {
|
||||
windowFlags |= WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
|
||||
}
|
||||
} else {
|
||||
windowFlags |= WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
|
||||
}
|
||||
params.layoutInDisplayCutoutMode = a.getInt(
|
||||
R.styleable.Window_windowLayoutInDisplayCutoutMode,
|
||||
params.layoutInDisplayCutoutMode);
|
||||
params.windowAnimations = a.getResourceId(R.styleable.Window_windowAnimationStyle, 0);
|
||||
a.recycle();
|
||||
|
||||
final ActivityManager.RunningTaskInfo taskInfo = windowInfo.taskInfo;
|
||||
final ActivityInfo activityInfo = windowInfo.targetActivityInfo != null
|
||||
? windowInfo.targetActivityInfo
|
||||
: taskInfo.topActivityInfo;
|
||||
final int displayId = taskInfo.displayId;
|
||||
// Assumes it's safe to show starting windows of launched apps while
|
||||
// the keyguard is being hidden. This is okay because starting windows never show
|
||||
// secret information.
|
||||
// TODO(b/113840485): Occluded may not only happen on default display
|
||||
if (displayId == DEFAULT_DISPLAY && windowInfo.isKeyguardOccluded) {
|
||||
windowFlags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
|
||||
}
|
||||
|
||||
// Force the window flags: this is a fake window, so it is not really
|
||||
// touchable or focusable by the user. We also add in the ALT_FOCUSABLE_IM
|
||||
// flag because we do know that the next window will take input
|
||||
// focus, so we want to get the IME window up on top of us right away.
|
||||
// Touches will only pass through to the host activity window and will be blocked from
|
||||
// passing to any other windows.
|
||||
windowFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
|
||||
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
||||
| WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
|
||||
params.flags = windowFlags;
|
||||
params.token = appToken;
|
||||
params.packageName = activityInfo.packageName;
|
||||
params.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
|
||||
|
||||
if (!context.getResources().getCompatibilityInfo().supportsScreen()) {
|
||||
params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
|
||||
}
|
||||
|
||||
params.setTitle("Splash Screen " + title);
|
||||
return params;
|
||||
}
|
||||
/**
|
||||
* Create a SplashScreenView object.
|
||||
*
|
||||
@@ -248,6 +393,26 @@ public class SplashscreenContentDrawer {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a SplashScreenView without read animatable icon and branding image.
|
||||
*/
|
||||
SplashScreenView makeSimpleSplashScreenContentView(Context context,
|
||||
StartingWindowInfo info, int themeBGColor) {
|
||||
updateDensity();
|
||||
mTmpAttrs.reset();
|
||||
final ActivityInfo ai = info.targetActivityInfo != null
|
||||
? info.targetActivityInfo
|
||||
: info.taskInfo.topActivityInfo;
|
||||
|
||||
final SplashViewBuilder builder = new SplashViewBuilder(context, ai);
|
||||
final SplashScreenView view = builder
|
||||
.setWindowBGColor(themeBGColor)
|
||||
.chooseStyle(STARTING_WINDOW_TYPE_SPLASH_SCREEN)
|
||||
.build();
|
||||
view.setNotCopyable();
|
||||
return view;
|
||||
}
|
||||
|
||||
private SplashScreenView makeSplashScreenContentView(Context context, StartingWindowInfo info,
|
||||
@StartingWindowType int suggestType, Consumer<Runnable> uiThreadInitConsumer) {
|
||||
updateDensity();
|
||||
@@ -263,7 +428,8 @@ public class SplashscreenContentDrawer {
|
||||
final int themeBGColor = legacyDrawable != null
|
||||
? getBGColorFromCache(ai, () -> estimateWindowBGColor(legacyDrawable))
|
||||
: getBGColorFromCache(ai, () -> peekWindowBGColor(context, mTmpAttrs));
|
||||
return new StartingWindowViewBuilder(context, ai)
|
||||
|
||||
return new SplashViewBuilder(context, ai)
|
||||
.setWindowBGColor(themeBGColor)
|
||||
.overlayDrawable(legacyDrawable)
|
||||
.chooseStyle(suggestType)
|
||||
@@ -322,6 +488,14 @@ public class SplashscreenContentDrawer {
|
||||
private Drawable mSplashScreenIcon = null;
|
||||
private Drawable mBrandingImage = null;
|
||||
private int mIconBgColor = Color.TRANSPARENT;
|
||||
|
||||
void reset() {
|
||||
mWindowBgResId = 0;
|
||||
mWindowBgColor = Color.TRANSPARENT;
|
||||
mSplashScreenIcon = null;
|
||||
mBrandingImage = null;
|
||||
mIconBgColor = Color.TRANSPARENT;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -351,7 +525,7 @@ public class SplashscreenContentDrawer {
|
||||
return appReadyDuration;
|
||||
}
|
||||
|
||||
private class StartingWindowViewBuilder {
|
||||
private class SplashViewBuilder {
|
||||
private final Context mContext;
|
||||
private final ActivityInfo mActivityInfo;
|
||||
|
||||
@@ -364,27 +538,28 @@ public class SplashscreenContentDrawer {
|
||||
/** @see #setAllowHandleSolidColor(boolean) **/
|
||||
private boolean mAllowHandleSolidColor;
|
||||
|
||||
StartingWindowViewBuilder(@NonNull Context context, @NonNull ActivityInfo aInfo) {
|
||||
SplashViewBuilder(@NonNull Context context, @NonNull ActivityInfo aInfo) {
|
||||
mContext = context;
|
||||
mActivityInfo = aInfo;
|
||||
}
|
||||
|
||||
StartingWindowViewBuilder setWindowBGColor(@ColorInt int background) {
|
||||
SplashViewBuilder setWindowBGColor(@ColorInt int background) {
|
||||
mThemeColor = background;
|
||||
return this;
|
||||
}
|
||||
|
||||
StartingWindowViewBuilder overlayDrawable(Drawable overlay) {
|
||||
SplashViewBuilder overlayDrawable(Drawable overlay) {
|
||||
mOverlayDrawable = overlay;
|
||||
return this;
|
||||
}
|
||||
|
||||
StartingWindowViewBuilder chooseStyle(int suggestType) {
|
||||
SplashViewBuilder chooseStyle(int suggestType) {
|
||||
mSuggestType = suggestType;
|
||||
return this;
|
||||
}
|
||||
|
||||
StartingWindowViewBuilder setUiThreadInitConsumer(Consumer<Runnable> uiThreadInitTask) {
|
||||
// Set up the UI thread for the View.
|
||||
SplashViewBuilder setUiThreadInitConsumer(Consumer<Runnable> uiThreadInitTask) {
|
||||
mUiThreadInitTask = uiThreadInitTask;
|
||||
return this;
|
||||
}
|
||||
@@ -395,7 +570,7 @@ public class SplashscreenContentDrawer {
|
||||
* android.window.SplashScreen.OnExitAnimationListener#onSplashScreenExit(SplashScreenView)}
|
||||
* callback, effectively copying the {@link SplashScreenView} into the client process.
|
||||
*/
|
||||
StartingWindowViewBuilder setAllowHandleSolidColor(boolean allowHandleSolidColor) {
|
||||
SplashViewBuilder setAllowHandleSolidColor(boolean allowHandleSolidColor) {
|
||||
mAllowHandleSolidColor = allowHandleSolidColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ public class SplashscreenIconDrawableFactory {
|
||||
*/
|
||||
static Drawable[] makeIconDrawable(@ColorInt int backgroundColor, @ColorInt int themeColor,
|
||||
@NonNull Drawable foregroundDrawable, int srcIconSize, int iconSize,
|
||||
boolean loadInDetail, Handler splashscreenWorkerHandler) {
|
||||
boolean loadInDetail, Handler preDrawHandler) {
|
||||
Drawable foreground;
|
||||
Drawable background = null;
|
||||
boolean drawBackground =
|
||||
@@ -74,13 +74,13 @@ public class SplashscreenIconDrawableFactory {
|
||||
// If the icon is Adaptive, we already use the icon background.
|
||||
drawBackground = false;
|
||||
foreground = new ImmobileIconDrawable(foregroundDrawable,
|
||||
srcIconSize, iconSize, loadInDetail, splashscreenWorkerHandler);
|
||||
srcIconSize, iconSize, loadInDetail, preDrawHandler);
|
||||
} else {
|
||||
// Adaptive icon don't handle transparency so we draw the background of the adaptive
|
||||
// icon with the same color as the window background color instead of using two layers
|
||||
foreground = new ImmobileIconDrawable(
|
||||
new AdaptiveForegroundDrawable(foregroundDrawable),
|
||||
srcIconSize, iconSize, loadInDetail, splashscreenWorkerHandler);
|
||||
srcIconSize, iconSize, loadInDetail, preDrawHandler);
|
||||
}
|
||||
|
||||
if (drawBackground) {
|
||||
@@ -91,9 +91,9 @@ public class SplashscreenIconDrawableFactory {
|
||||
}
|
||||
|
||||
static Drawable[] makeLegacyIconDrawable(@NonNull Drawable iconDrawable, int srcIconSize,
|
||||
int iconSize, boolean loadInDetail, Handler splashscreenWorkerHandler) {
|
||||
int iconSize, boolean loadInDetail, Handler preDrawHandler) {
|
||||
return new Drawable[]{new ImmobileIconDrawable(iconDrawable, srcIconSize, iconSize,
|
||||
loadInDetail, splashscreenWorkerHandler)};
|
||||
loadInDetail, preDrawHandler)};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -107,14 +107,14 @@ public class SplashscreenIconDrawableFactory {
|
||||
private Bitmap mIconBitmap;
|
||||
|
||||
ImmobileIconDrawable(Drawable drawable, int srcIconSize, int iconSize, boolean loadInDetail,
|
||||
Handler splashscreenWorkerHandler) {
|
||||
Handler preDrawHandler) {
|
||||
// This icon has lower density, don't scale it.
|
||||
if (loadInDetail) {
|
||||
splashscreenWorkerHandler.post(() -> preDrawIcon(drawable, iconSize));
|
||||
preDrawHandler.post(() -> preDrawIcon(drawable, iconSize));
|
||||
} else {
|
||||
final float scale = (float) iconSize / srcIconSize;
|
||||
mMatrix.setScale(scale, scale);
|
||||
splashscreenWorkerHandler.post(() -> preDrawIcon(drawable, srcIconSize));
|
||||
preDrawHandler.post(() -> preDrawIcon(drawable, srcIconSize));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package com.android.wm.shell.startingsurface;
|
||||
|
||||
import static android.content.Context.CONTEXT_RESTRICTED;
|
||||
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
|
||||
import static android.view.Choreographer.CALLBACK_INSETS_ANIMATION;
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
@@ -32,8 +31,6 @@ import android.content.Context;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.IPackageManager;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PixelFormat;
|
||||
@@ -198,118 +195,21 @@ public class StartingSurfaceDrawer {
|
||||
if (activityInfo == null || activityInfo.packageName == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int displayId = taskInfo.displayId;
|
||||
final int taskId = taskInfo.taskId;
|
||||
|
||||
// replace with the default theme if the application didn't set
|
||||
final int theme = getSplashScreenTheme(windowInfo.splashScreenThemeResId, activityInfo);
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
|
||||
"addSplashScreen for package: %s with theme: %s for task: %d, suggestType: %d",
|
||||
activityInfo.packageName, Integer.toHexString(theme), taskId, suggestType);
|
||||
final Display display = getDisplay(displayId);
|
||||
if (display == null) {
|
||||
// Can't show splash screen on requested display, so skip showing at all.
|
||||
return;
|
||||
}
|
||||
Context context = displayId == DEFAULT_DISPLAY
|
||||
? mContext : mContext.createDisplayContext(display);
|
||||
final Context context = SplashscreenContentDrawer.createContext(mContext, windowInfo, theme,
|
||||
suggestType, mDisplayManager);
|
||||
if (context == null) {
|
||||
return;
|
||||
}
|
||||
if (theme != context.getThemeResId()) {
|
||||
try {
|
||||
context = context.createPackageContextAsUser(activityInfo.packageName,
|
||||
CONTEXT_RESTRICTED, UserHandle.of(taskInfo.userId));
|
||||
context.setTheme(theme);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Slog.w(TAG, "Failed creating package context with package name "
|
||||
+ activityInfo.packageName + " for user " + taskInfo.userId, e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
final WindowManager.LayoutParams params = SplashscreenContentDrawer.createLayoutParameters(
|
||||
context, windowInfo, suggestType, activityInfo.packageName,
|
||||
suggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN
|
||||
? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT, appToken);
|
||||
|
||||
final Configuration taskConfig = taskInfo.getConfiguration();
|
||||
if (taskConfig.diffPublicOnly(context.getResources().getConfiguration()) != 0) {
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
|
||||
"addSplashScreen: creating context based on task Configuration %s",
|
||||
taskConfig);
|
||||
final Context overrideContext = context.createConfigurationContext(taskConfig);
|
||||
overrideContext.setTheme(theme);
|
||||
final TypedArray typedArray = overrideContext.obtainStyledAttributes(
|
||||
com.android.internal.R.styleable.Window);
|
||||
final int resId = typedArray.getResourceId(R.styleable.Window_windowBackground, 0);
|
||||
try {
|
||||
if (resId != 0 && overrideContext.getDrawable(resId) != null) {
|
||||
// We want to use the windowBackground for the override context if it is
|
||||
// available, otherwise we use the default one to make sure a themed starting
|
||||
// window is displayed for the app.
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
|
||||
"addSplashScreen: apply overrideConfig %s",
|
||||
taskConfig);
|
||||
context = overrideContext;
|
||||
}
|
||||
} catch (Resources.NotFoundException e) {
|
||||
Slog.w(TAG, "failed creating starting window for overrideConfig at taskId: "
|
||||
+ taskId, e);
|
||||
return;
|
||||
}
|
||||
typedArray.recycle();
|
||||
}
|
||||
|
||||
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
|
||||
WindowManager.LayoutParams.TYPE_APPLICATION_STARTING);
|
||||
params.setFitInsetsSides(0);
|
||||
params.setFitInsetsTypes(0);
|
||||
params.format = suggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN
|
||||
? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT;
|
||||
int windowFlags = WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
|
||||
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
|
||||
| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
|
||||
final TypedArray a = context.obtainStyledAttributes(R.styleable.Window);
|
||||
if (a.getBoolean(R.styleable.Window_windowShowWallpaper, false)) {
|
||||
windowFlags |= WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
|
||||
}
|
||||
if (suggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) {
|
||||
if (a.getBoolean(R.styleable.Window_windowDrawsSystemBarBackgrounds, false)) {
|
||||
windowFlags |= WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
|
||||
}
|
||||
} else {
|
||||
windowFlags |= WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
|
||||
}
|
||||
params.layoutInDisplayCutoutMode = a.getInt(
|
||||
R.styleable.Window_windowLayoutInDisplayCutoutMode,
|
||||
params.layoutInDisplayCutoutMode);
|
||||
params.windowAnimations = a.getResourceId(R.styleable.Window_windowAnimationStyle, 0);
|
||||
a.recycle();
|
||||
|
||||
// Assumes it's safe to show starting windows of launched apps while
|
||||
// the keyguard is being hidden. This is okay because starting windows never show
|
||||
// secret information.
|
||||
// TODO(b/113840485): Occluded may not only happen on default display
|
||||
if (displayId == DEFAULT_DISPLAY && windowInfo.isKeyguardOccluded) {
|
||||
windowFlags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
|
||||
}
|
||||
|
||||
// Force the window flags: this is a fake window, so it is not really
|
||||
// touchable or focusable by the user. We also add in the ALT_FOCUSABLE_IM
|
||||
// flag because we do know that the next window will take input
|
||||
// focus, so we want to get the IME window up on top of us right away.
|
||||
// Touches will only pass through to the host activity window and will be blocked from
|
||||
// passing to any other windows.
|
||||
windowFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
|
||||
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
||||
| WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
|
||||
params.flags = windowFlags;
|
||||
params.token = appToken;
|
||||
params.packageName = activityInfo.packageName;
|
||||
params.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
|
||||
|
||||
if (!context.getResources().getCompatibilityInfo().supportsScreen()) {
|
||||
params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
|
||||
}
|
||||
|
||||
params.setTitle("Splash Screen " + activityInfo.packageName);
|
||||
final int displayId = taskInfo.displayId;
|
||||
final int taskId = taskInfo.taskId;
|
||||
final Display display = getDisplay(displayId);
|
||||
|
||||
// TODO(b/173975965) tracking performance
|
||||
// Prepare the splash screen content view on splash screen worker thread in parallel, so the
|
||||
|
||||
@@ -18,50 +18,16 @@ package com.android.wm.shell.startingsurface;
|
||||
|
||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
|
||||
import static android.graphics.Color.WHITE;
|
||||
import static android.graphics.Color.alpha;
|
||||
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
|
||||
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
|
||||
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
|
||||
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
|
||||
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
|
||||
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
|
||||
|
||||
import static com.android.internal.policy.DecorView.NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES;
|
||||
import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES;
|
||||
import static com.android.internal.policy.DecorView.getNavigationBarRect;
|
||||
|
||||
import android.annotation.BinderThread;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManager.TaskDescription;
|
||||
import android.app.ActivityThread;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.GraphicBuffer;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.hardware.HardwareBuffer;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
@@ -73,20 +39,14 @@ import android.view.InputChannel;
|
||||
import android.view.InsetsSourceControl;
|
||||
import android.view.InsetsState;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.SurfaceSession;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowInsets;
|
||||
import android.view.WindowInsets.Type.InsetsType;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowManagerGlobal;
|
||||
import android.window.ClientWindowFrames;
|
||||
import android.window.SnapshotDrawerUtils;
|
||||
import android.window.StartingWindowInfo;
|
||||
import android.window.TaskSnapshot;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.policy.DecorView;
|
||||
import com.android.internal.protolog.common.ProtoLog;
|
||||
import com.android.internal.view.BaseIWindow;
|
||||
import com.android.wm.shell.common.ShellExecutor;
|
||||
@@ -100,27 +60,8 @@ import java.lang.ref.WeakReference;
|
||||
* @hide
|
||||
*/
|
||||
public class TaskSnapshotWindow {
|
||||
/**
|
||||
* When creating the starting window, we use the exact same layout flags such that we end up
|
||||
* with a window with the exact same dimensions etc. However, these flags are not used in layout
|
||||
* and might cause other side effects so we exclude them.
|
||||
*/
|
||||
static final int FLAG_INHERIT_EXCLUDES = FLAG_NOT_FOCUSABLE
|
||||
| FLAG_NOT_TOUCHABLE
|
||||
| FLAG_NOT_TOUCH_MODAL
|
||||
| FLAG_ALT_FOCUSABLE_IM
|
||||
| FLAG_NOT_FOCUSABLE
|
||||
| FLAG_HARDWARE_ACCELERATED
|
||||
| FLAG_IGNORE_CHEEK_PRESSES
|
||||
| FLAG_LOCAL_FOCUS_MODE
|
||||
| FLAG_SLIPPERY
|
||||
| FLAG_WATCH_OUTSIDE_TOUCH
|
||||
| FLAG_SPLIT_TOUCH
|
||||
| FLAG_SCALED
|
||||
| FLAG_SECURE;
|
||||
|
||||
private static final String TAG = StartingWindowController.TAG;
|
||||
private static final String TITLE_FORMAT = "SnapshotStartingWindow for taskId=%s";
|
||||
private static final String TITLE_FORMAT = "SnapshotStartingWindow for taskId=";
|
||||
|
||||
private static final long DELAY_REMOVAL_TIME_GENERAL = 100;
|
||||
/**
|
||||
@@ -133,25 +74,12 @@ public class TaskSnapshotWindow {
|
||||
private final Window mWindow;
|
||||
private final Runnable mClearWindowHandler;
|
||||
private final ShellExecutor mSplashScreenExecutor;
|
||||
private final SurfaceControl mSurfaceControl;
|
||||
private final IWindowSession mSession;
|
||||
private final Rect mTaskBounds;
|
||||
private final Rect mFrame = new Rect();
|
||||
private final Rect mSystemBarInsets = new Rect();
|
||||
private TaskSnapshot mSnapshot;
|
||||
private final RectF mTmpSnapshotSize = new RectF();
|
||||
private final RectF mTmpDstFrame = new RectF();
|
||||
private final CharSequence mTitle;
|
||||
private boolean mHasDrawn;
|
||||
private boolean mSizeMismatch;
|
||||
private final Paint mBackgroundPaint = new Paint();
|
||||
private final int mActivityType;
|
||||
private final int mStatusBarColor;
|
||||
private final SystemBarBackgroundPainter mSystemBarBackgroundPainter;
|
||||
private final int mOrientationOnCreation;
|
||||
private final SurfaceControl.Transaction mTransaction;
|
||||
private final Matrix mSnapshotMatrix = new Matrix();
|
||||
private final float[] mTmpFloat9 = new float[9];
|
||||
|
||||
private final Runnable mScheduledRunnable = this::removeImmediately;
|
||||
private final boolean mHasImeSurface;
|
||||
|
||||
@@ -163,42 +91,15 @@ public class TaskSnapshotWindow {
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
|
||||
"create taskSnapshot surface for task: %d", taskId);
|
||||
|
||||
final WindowManager.LayoutParams attrs = info.topOpaqueWindowLayoutParams;
|
||||
final WindowManager.LayoutParams mainWindowParams = info.mainWindowLayoutParams;
|
||||
final InsetsState topWindowInsetsState = info.topOpaqueWindowInsetsState;
|
||||
if (attrs == null || mainWindowParams == null || topWindowInsetsState == null) {
|
||||
Slog.w(TAG, "unable to create taskSnapshot surface for task: " + taskId);
|
||||
|
||||
final WindowManager.LayoutParams layoutParams = SnapshotDrawerUtils.createLayoutParameters(
|
||||
info, TITLE_FORMAT + taskId, TYPE_APPLICATION_STARTING,
|
||||
snapshot.getHardwareBuffer().getFormat(), appToken);
|
||||
if (layoutParams == null) {
|
||||
Slog.e(TAG, "TaskSnapshotWindow no layoutParams");
|
||||
return null;
|
||||
}
|
||||
final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
|
||||
|
||||
final int appearance = attrs.insetsFlags.appearance;
|
||||
final int windowFlags = attrs.flags;
|
||||
final int windowPrivateFlags = attrs.privateFlags;
|
||||
|
||||
layoutParams.packageName = mainWindowParams.packageName;
|
||||
layoutParams.windowAnimations = mainWindowParams.windowAnimations;
|
||||
layoutParams.dimAmount = mainWindowParams.dimAmount;
|
||||
layoutParams.type = TYPE_APPLICATION_STARTING;
|
||||
layoutParams.format = snapshot.getHardwareBuffer().getFormat();
|
||||
layoutParams.flags = (windowFlags & ~FLAG_INHERIT_EXCLUDES)
|
||||
| FLAG_NOT_FOCUSABLE
|
||||
| FLAG_NOT_TOUCHABLE;
|
||||
// Setting as trusted overlay to let touches pass through. This is safe because this
|
||||
// window is controlled by the system.
|
||||
layoutParams.privateFlags = (windowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS)
|
||||
| PRIVATE_FLAG_TRUSTED_OVERLAY | PRIVATE_FLAG_USE_BLAST;
|
||||
layoutParams.token = appToken;
|
||||
layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
layoutParams.insetsFlags.appearance = appearance;
|
||||
layoutParams.insetsFlags.behavior = attrs.insetsFlags.behavior;
|
||||
layoutParams.layoutInDisplayCutoutMode = attrs.layoutInDisplayCutoutMode;
|
||||
layoutParams.setFitInsetsTypes(attrs.getFitInsetsTypes());
|
||||
layoutParams.setFitInsetsSides(attrs.getFitInsetsSides());
|
||||
layoutParams.setFitInsetsIgnoringVisibility(attrs.isFitInsetsIgnoringVisibility());
|
||||
|
||||
layoutParams.setTitle(String.format(TITLE_FORMAT, taskId));
|
||||
|
||||
final Point taskSize = snapshot.getTaskSize();
|
||||
final Rect taskBounds = new Rect(0, 0, taskSize.x, taskSize.y);
|
||||
@@ -222,9 +123,8 @@ public class TaskSnapshotWindow {
|
||||
}
|
||||
|
||||
final TaskSnapshotWindow snapshotSurface = new TaskSnapshotWindow(
|
||||
surfaceControl, snapshot, layoutParams.getTitle(), taskDescription, appearance,
|
||||
windowFlags, windowPrivateFlags, taskBounds, orientation, activityType,
|
||||
info.requestedVisibleTypes, clearWindowHandler, splashScreenExecutor);
|
||||
snapshot, taskDescription, orientation, activityType,
|
||||
clearWindowHandler, splashScreenExecutor);
|
||||
final Window window = snapshotSurface.mWindow;
|
||||
|
||||
final InsetsState tmpInsetsState = new InsetsState();
|
||||
@@ -255,33 +155,25 @@ public class TaskSnapshotWindow {
|
||||
snapshotSurface.clearWindowSynced();
|
||||
}
|
||||
|
||||
final Rect systemBarInsets = getSystemBarInsets(tmpFrames.frame, topWindowInsetsState);
|
||||
snapshotSurface.setFrames(tmpFrames.frame, systemBarInsets);
|
||||
snapshotSurface.drawSnapshot();
|
||||
SnapshotDrawerUtils.drawSnapshotOnSurface(info, layoutParams, surfaceControl, snapshot,
|
||||
taskBounds, tmpFrames.frame, topWindowInsetsState, true /* releaseAfterDraw */);
|
||||
snapshotSurface.mHasDrawn = true;
|
||||
snapshotSurface.reportDrawn();
|
||||
|
||||
return snapshotSurface;
|
||||
}
|
||||
|
||||
public TaskSnapshotWindow(SurfaceControl surfaceControl,
|
||||
TaskSnapshot snapshot, CharSequence title, TaskDescription taskDescription,
|
||||
int appearance, int windowFlags, int windowPrivateFlags, Rect taskBounds,
|
||||
int currentOrientation, int activityType, @InsetsType int requestedVisibleTypes,
|
||||
Runnable clearWindowHandler, ShellExecutor splashScreenExecutor) {
|
||||
public TaskSnapshotWindow(TaskSnapshot snapshot, TaskDescription taskDescription,
|
||||
int currentOrientation, int activityType, Runnable clearWindowHandler,
|
||||
ShellExecutor splashScreenExecutor) {
|
||||
mSplashScreenExecutor = splashScreenExecutor;
|
||||
mSession = WindowManagerGlobal.getWindowSession();
|
||||
mWindow = new Window();
|
||||
mWindow.setSession(mSession);
|
||||
mSurfaceControl = surfaceControl;
|
||||
mSnapshot = snapshot;
|
||||
mTitle = title;
|
||||
int backgroundColor = taskDescription.getBackgroundColor();
|
||||
mBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE);
|
||||
mTaskBounds = taskBounds;
|
||||
mSystemBarBackgroundPainter = new SystemBarBackgroundPainter(windowFlags,
|
||||
windowPrivateFlags, appearance, taskDescription, 1f, requestedVisibleTypes);
|
||||
mStatusBarColor = taskDescription.getStatusBarColor();
|
||||
mOrientationOnCreation = currentOrientation;
|
||||
mActivityType = activityType;
|
||||
mTransaction = new SurfaceControl.Transaction();
|
||||
mClearWindowHandler = clearWindowHandler;
|
||||
mHasImeSurface = snapshot.hasImeSurface();
|
||||
}
|
||||
@@ -294,23 +186,6 @@ public class TaskSnapshotWindow {
|
||||
return mHasImeSurface;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask system bar background painter to draw status bar background.
|
||||
* @hide
|
||||
*/
|
||||
public void drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame) {
|
||||
mSystemBarBackgroundPainter.drawStatusBarBackground(c, alreadyDrawnFrame,
|
||||
mSystemBarBackgroundPainter.getStatusBarColorViewHeight());
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask system bar background painter to draw navigation bar background.
|
||||
* @hide
|
||||
*/
|
||||
public void drawNavigationBarBackground(Canvas c) {
|
||||
mSystemBarBackgroundPainter.drawNavigationBarBackground(c);
|
||||
}
|
||||
|
||||
void scheduleRemove(boolean deferRemoveForIme) {
|
||||
// Show the latest content as soon as possible for unlocking to home.
|
||||
if (mActivityType == ACTIVITY_TYPE_HOME) {
|
||||
@@ -337,178 +212,6 @@ public class TaskSnapshotWindow {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set frame size.
|
||||
* @hide
|
||||
*/
|
||||
public void setFrames(Rect frame, Rect systemBarInsets) {
|
||||
mFrame.set(frame);
|
||||
mSystemBarInsets.set(systemBarInsets);
|
||||
final HardwareBuffer snapshot = mSnapshot.getHardwareBuffer();
|
||||
mSizeMismatch = (mFrame.width() != snapshot.getWidth()
|
||||
|| mFrame.height() != snapshot.getHeight());
|
||||
mSystemBarBackgroundPainter.setInsets(systemBarInsets);
|
||||
}
|
||||
|
||||
static Rect getSystemBarInsets(Rect frame, InsetsState state) {
|
||||
return state.calculateInsets(frame, WindowInsets.Type.systemBars(),
|
||||
false /* ignoreVisibility */).toRect();
|
||||
}
|
||||
|
||||
private void drawSnapshot() {
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
|
||||
"Drawing snapshot surface sizeMismatch=%b", mSizeMismatch);
|
||||
if (mSizeMismatch) {
|
||||
// The dimensions of the buffer and the window don't match, so attaching the buffer
|
||||
// will fail. Better create a child window with the exact dimensions and fill the parent
|
||||
// window with the background color!
|
||||
drawSizeMismatchSnapshot();
|
||||
} else {
|
||||
drawSizeMatchSnapshot();
|
||||
}
|
||||
mHasDrawn = true;
|
||||
reportDrawn();
|
||||
|
||||
// In case window manager leaks us, make sure we don't retain the snapshot.
|
||||
if (mSnapshot.getHardwareBuffer() != null) {
|
||||
mSnapshot.getHardwareBuffer().close();
|
||||
}
|
||||
mSnapshot = null;
|
||||
mSurfaceControl.release();
|
||||
}
|
||||
|
||||
private void drawSizeMatchSnapshot() {
|
||||
mTransaction.setBuffer(mSurfaceControl, mSnapshot.getHardwareBuffer())
|
||||
.setColorSpace(mSurfaceControl, mSnapshot.getColorSpace())
|
||||
.apply();
|
||||
}
|
||||
|
||||
private void drawSizeMismatchSnapshot() {
|
||||
final HardwareBuffer buffer = mSnapshot.getHardwareBuffer();
|
||||
final SurfaceSession session = new SurfaceSession();
|
||||
|
||||
// We consider nearly matched dimensions as there can be rounding errors and the user won't
|
||||
// notice very minute differences from scaling one dimension more than the other
|
||||
final boolean aspectRatioMismatch = Math.abs(
|
||||
((float) buffer.getWidth() / buffer.getHeight())
|
||||
- ((float) mFrame.width() / mFrame.height())) > 0.01f;
|
||||
|
||||
// Keep a reference to it such that it doesn't get destroyed when finalized.
|
||||
SurfaceControl childSurfaceControl = new SurfaceControl.Builder(session)
|
||||
.setName(mTitle + " - task-snapshot-surface")
|
||||
.setBLASTLayer()
|
||||
.setFormat(buffer.getFormat())
|
||||
.setParent(mSurfaceControl)
|
||||
.setCallsite("TaskSnapshotWindow.drawSizeMismatchSnapshot")
|
||||
.build();
|
||||
|
||||
final Rect frame;
|
||||
// We can just show the surface here as it will still be hidden as the parent is
|
||||
// still hidden.
|
||||
mTransaction.show(childSurfaceControl);
|
||||
if (aspectRatioMismatch) {
|
||||
// Clip off ugly navigation bar.
|
||||
final Rect crop = calculateSnapshotCrop();
|
||||
frame = calculateSnapshotFrame(crop);
|
||||
mTransaction.setWindowCrop(childSurfaceControl, crop);
|
||||
mTransaction.setPosition(childSurfaceControl, frame.left, frame.top);
|
||||
mTmpSnapshotSize.set(crop);
|
||||
mTmpDstFrame.set(frame);
|
||||
} else {
|
||||
frame = null;
|
||||
mTmpSnapshotSize.set(0, 0, buffer.getWidth(), buffer.getHeight());
|
||||
mTmpDstFrame.set(mFrame);
|
||||
mTmpDstFrame.offsetTo(0, 0);
|
||||
}
|
||||
|
||||
// Scale the mismatch dimensions to fill the task bounds
|
||||
mSnapshotMatrix.setRectToRect(mTmpSnapshotSize, mTmpDstFrame, Matrix.ScaleToFit.FILL);
|
||||
mTransaction.setMatrix(childSurfaceControl, mSnapshotMatrix, mTmpFloat9);
|
||||
mTransaction.setColorSpace(childSurfaceControl, mSnapshot.getColorSpace());
|
||||
mTransaction.setBuffer(childSurfaceControl, mSnapshot.getHardwareBuffer());
|
||||
|
||||
if (aspectRatioMismatch) {
|
||||
GraphicBuffer background = GraphicBuffer.create(mFrame.width(), mFrame.height(),
|
||||
PixelFormat.RGBA_8888,
|
||||
GraphicBuffer.USAGE_HW_TEXTURE | GraphicBuffer.USAGE_HW_COMPOSER
|
||||
| GraphicBuffer.USAGE_SW_WRITE_RARELY);
|
||||
// TODO: Support this on HardwareBuffer
|
||||
final Canvas c = background.lockCanvas();
|
||||
drawBackgroundAndBars(c, frame);
|
||||
background.unlockCanvasAndPost(c);
|
||||
mTransaction.setBuffer(mSurfaceControl,
|
||||
HardwareBuffer.createFromGraphicBuffer(background));
|
||||
}
|
||||
mTransaction.apply();
|
||||
childSurfaceControl.release();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the snapshot crop in snapshot coordinate space.
|
||||
*
|
||||
* @return crop rect in snapshot coordinate space.
|
||||
*/
|
||||
public Rect calculateSnapshotCrop() {
|
||||
final Rect rect = new Rect();
|
||||
final HardwareBuffer snapshot = mSnapshot.getHardwareBuffer();
|
||||
rect.set(0, 0, snapshot.getWidth(), snapshot.getHeight());
|
||||
final Rect insets = mSnapshot.getContentInsets();
|
||||
|
||||
final float scaleX = (float) snapshot.getWidth() / mSnapshot.getTaskSize().x;
|
||||
final float scaleY = (float) snapshot.getHeight() / mSnapshot.getTaskSize().y;
|
||||
|
||||
// Let's remove all system decorations except the status bar, but only if the task is at the
|
||||
// very top of the screen.
|
||||
final boolean isTop = mTaskBounds.top == 0 && mFrame.top == 0;
|
||||
rect.inset((int) (insets.left * scaleX),
|
||||
isTop ? 0 : (int) (insets.top * scaleY),
|
||||
(int) (insets.right * scaleX),
|
||||
(int) (insets.bottom * scaleY));
|
||||
return rect;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the snapshot frame in window coordinate space from crop.
|
||||
*
|
||||
* @param crop rect that is in snapshot coordinate space.
|
||||
*/
|
||||
public Rect calculateSnapshotFrame(Rect crop) {
|
||||
final HardwareBuffer snapshot = mSnapshot.getHardwareBuffer();
|
||||
final float scaleX = (float) snapshot.getWidth() / mSnapshot.getTaskSize().x;
|
||||
final float scaleY = (float) snapshot.getHeight() / mSnapshot.getTaskSize().y;
|
||||
|
||||
// Rescale the frame from snapshot to window coordinate space
|
||||
final Rect frame = new Rect(0, 0,
|
||||
(int) (crop.width() / scaleX + 0.5f),
|
||||
(int) (crop.height() / scaleY + 0.5f)
|
||||
);
|
||||
|
||||
// However, we also need to make space for the navigation bar on the left side.
|
||||
frame.offset(mSystemBarInsets.left, 0);
|
||||
return frame;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw status bar and navigation bar background.
|
||||
* @hide
|
||||
*/
|
||||
public void drawBackgroundAndBars(Canvas c, Rect frame) {
|
||||
final int statusBarHeight = mSystemBarBackgroundPainter.getStatusBarColorViewHeight();
|
||||
final boolean fillHorizontally = c.getWidth() > frame.right;
|
||||
final boolean fillVertically = c.getHeight() > frame.bottom;
|
||||
if (fillHorizontally) {
|
||||
c.drawRect(frame.right, alpha(mStatusBarColor) == 0xFF ? statusBarHeight : 0,
|
||||
c.getWidth(), fillVertically
|
||||
? frame.bottom
|
||||
: c.getHeight(),
|
||||
mBackgroundPaint);
|
||||
}
|
||||
if (fillVertically) {
|
||||
c.drawRect(0, frame.bottom, c.getWidth(), c.getHeight(), mBackgroundPaint);
|
||||
}
|
||||
mSystemBarBackgroundPainter.drawDecors(c, frame);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear window from drawer, must be post on main executor.
|
||||
*/
|
||||
@@ -557,92 +260,4 @@ public class TaskSnapshotWindow {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class to draw the background of the system bars in regions the task snapshot isn't
|
||||
* filling the window.
|
||||
*/
|
||||
static class SystemBarBackgroundPainter {
|
||||
private final Paint mStatusBarPaint = new Paint();
|
||||
private final Paint mNavigationBarPaint = new Paint();
|
||||
private final int mStatusBarColor;
|
||||
private final int mNavigationBarColor;
|
||||
private final int mWindowFlags;
|
||||
private final int mWindowPrivateFlags;
|
||||
private final float mScale;
|
||||
private final @InsetsType int mRequestedVisibleTypes;
|
||||
private final Rect mSystemBarInsets = new Rect();
|
||||
|
||||
SystemBarBackgroundPainter(int windowFlags, int windowPrivateFlags, int appearance,
|
||||
TaskDescription taskDescription, float scale,
|
||||
@InsetsType int requestedVisibleTypes) {
|
||||
mWindowFlags = windowFlags;
|
||||
mWindowPrivateFlags = windowPrivateFlags;
|
||||
mScale = scale;
|
||||
final Context context = ActivityThread.currentActivityThread().getSystemUiContext();
|
||||
final int semiTransparent = context.getColor(
|
||||
R.color.system_bar_background_semi_transparent);
|
||||
mStatusBarColor = DecorView.calculateBarColor(windowFlags, FLAG_TRANSLUCENT_STATUS,
|
||||
semiTransparent, taskDescription.getStatusBarColor(), appearance,
|
||||
APPEARANCE_LIGHT_STATUS_BARS,
|
||||
taskDescription.getEnsureStatusBarContrastWhenTransparent());
|
||||
mNavigationBarColor = DecorView.calculateBarColor(windowFlags,
|
||||
FLAG_TRANSLUCENT_NAVIGATION, semiTransparent,
|
||||
taskDescription.getNavigationBarColor(), appearance,
|
||||
APPEARANCE_LIGHT_NAVIGATION_BARS,
|
||||
taskDescription.getEnsureNavigationBarContrastWhenTransparent()
|
||||
&& context.getResources().getBoolean(R.bool.config_navBarNeedsScrim));
|
||||
mStatusBarPaint.setColor(mStatusBarColor);
|
||||
mNavigationBarPaint.setColor(mNavigationBarColor);
|
||||
mRequestedVisibleTypes = requestedVisibleTypes;
|
||||
}
|
||||
|
||||
void setInsets(Rect systemBarInsets) {
|
||||
mSystemBarInsets.set(systemBarInsets);
|
||||
}
|
||||
|
||||
int getStatusBarColorViewHeight() {
|
||||
final boolean forceBarBackground =
|
||||
(mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0;
|
||||
if (STATUS_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
|
||||
mRequestedVisibleTypes, mStatusBarColor, mWindowFlags, forceBarBackground)) {
|
||||
return (int) (mSystemBarInsets.top * mScale);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isNavigationBarColorViewVisible() {
|
||||
final boolean forceBarBackground =
|
||||
(mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0;
|
||||
return NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
|
||||
mRequestedVisibleTypes, mNavigationBarColor, mWindowFlags, forceBarBackground);
|
||||
}
|
||||
|
||||
void drawDecors(Canvas c, @Nullable Rect alreadyDrawnFrame) {
|
||||
drawStatusBarBackground(c, alreadyDrawnFrame, getStatusBarColorViewHeight());
|
||||
drawNavigationBarBackground(c);
|
||||
}
|
||||
|
||||
void drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame,
|
||||
int statusBarHeight) {
|
||||
if (statusBarHeight > 0 && Color.alpha(mStatusBarColor) != 0
|
||||
&& (alreadyDrawnFrame == null || c.getWidth() > alreadyDrawnFrame.right)) {
|
||||
final int rightInset = (int) (mSystemBarInsets.right * mScale);
|
||||
final int left = alreadyDrawnFrame != null ? alreadyDrawnFrame.right : 0;
|
||||
c.drawRect(left, 0, c.getWidth() - rightInset, statusBarHeight, mStatusBarPaint);
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void drawNavigationBarBackground(Canvas c) {
|
||||
final Rect navigationBarRect = new Rect();
|
||||
getNavigationBarRect(c.getWidth(), c.getHeight(), mSystemBarInsets, navigationBarRect,
|
||||
mScale);
|
||||
final boolean visible = isNavigationBarColorViewVisible();
|
||||
if (visible && Color.alpha(mNavigationBarColor) != 0 && !navigationBarRect.isEmpty()) {
|
||||
c.drawRect(navigationBarRect, mNavigationBarPaint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,29 +16,14 @@
|
||||
|
||||
package com.android.server.wm;
|
||||
|
||||
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
|
||||
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
|
||||
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
|
||||
|
||||
import static com.android.internal.policy.DecorView.NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES;
|
||||
import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES;
|
||||
import static com.android.internal.policy.DecorView.getNavigationBarRect;
|
||||
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT;
|
||||
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
|
||||
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityThread;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.RecordingCanvas;
|
||||
@@ -59,12 +44,11 @@ import android.view.WindowInsets.Type;
|
||||
import android.view.WindowInsetsController.Appearance;
|
||||
import android.view.WindowManager.LayoutParams;
|
||||
import android.window.ScreenCapture;
|
||||
import android.window.SnapshotDrawerUtils;
|
||||
import android.window.TaskSnapshot;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.graphics.ColorUtils;
|
||||
import com.android.internal.policy.DecorView;
|
||||
import com.android.server.policy.WindowManagerPolicy.ScreenOffListener;
|
||||
import com.android.server.wm.utils.InsetUtils;
|
||||
|
||||
@@ -585,7 +569,8 @@ class TaskSnapshotController {
|
||||
final Rect taskBounds = task.getBounds();
|
||||
final InsetsState insetsState = mainWindow.getInsetsStateWithVisibilityOverride();
|
||||
final Rect systemBarInsets = getSystemBarInsets(mainWindow.getFrame(), insetsState);
|
||||
final SystemBarBackgroundPainter decorPainter = new SystemBarBackgroundPainter(attrs.flags,
|
||||
final SnapshotDrawerUtils.SystemBarBackgroundPainter decorPainter =
|
||||
new SnapshotDrawerUtils.SystemBarBackgroundPainter(attrs.flags,
|
||||
attrs.privateFlags, attrs.insetsFlags.appearance, task.getTaskDescription(),
|
||||
mHighResTaskSnapshotScale, mainWindow.getRequestedVisibleTypes());
|
||||
final int taskWidth = taskBounds.width();
|
||||
@@ -599,7 +584,7 @@ class TaskSnapshotController {
|
||||
final RecordingCanvas c = node.start(width, height);
|
||||
c.drawColor(color);
|
||||
decorPainter.setInsets(systemBarInsets);
|
||||
decorPainter.drawDecors(c /* statusBarExcludeFrame */);
|
||||
decorPainter.drawDecors(c /* statusBarExcludeFrame */, null /* alreadyDrawnFrame */);
|
||||
node.end(c);
|
||||
final Bitmap hwBitmap = ThreadedRenderer.createHardwareBitmap(node, width, height);
|
||||
if (hwBitmap == null) {
|
||||
@@ -736,92 +721,4 @@ class TaskSnapshotController {
|
||||
pw.println(prefix + "mTaskSnapshotEnabled=" + mTaskSnapshotEnabled);
|
||||
mCache.dump(pw, prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class to draw the background of the system bars in regions the task snapshot isn't
|
||||
* filling the window.
|
||||
*/
|
||||
static class SystemBarBackgroundPainter {
|
||||
|
||||
private final Paint mStatusBarPaint = new Paint();
|
||||
private final Paint mNavigationBarPaint = new Paint();
|
||||
private final int mStatusBarColor;
|
||||
private final int mNavigationBarColor;
|
||||
private final int mWindowFlags;
|
||||
private final int mWindowPrivateFlags;
|
||||
private final float mScale;
|
||||
private final @Type.InsetsType int mRequestedVisibleTypes;
|
||||
private final Rect mSystemBarInsets = new Rect();
|
||||
|
||||
SystemBarBackgroundPainter(int windowFlags, int windowPrivateFlags, int appearance,
|
||||
ActivityManager.TaskDescription taskDescription, float scale,
|
||||
@Type.InsetsType int requestedVisibleTypes) {
|
||||
mWindowFlags = windowFlags;
|
||||
mWindowPrivateFlags = windowPrivateFlags;
|
||||
mScale = scale;
|
||||
final Context context = ActivityThread.currentActivityThread().getSystemUiContext();
|
||||
final int semiTransparent = context.getColor(
|
||||
R.color.system_bar_background_semi_transparent);
|
||||
mStatusBarColor = DecorView.calculateBarColor(windowFlags, FLAG_TRANSLUCENT_STATUS,
|
||||
semiTransparent, taskDescription.getStatusBarColor(), appearance,
|
||||
APPEARANCE_LIGHT_STATUS_BARS,
|
||||
taskDescription.getEnsureStatusBarContrastWhenTransparent());
|
||||
mNavigationBarColor = DecorView.calculateBarColor(windowFlags,
|
||||
FLAG_TRANSLUCENT_NAVIGATION, semiTransparent,
|
||||
taskDescription.getNavigationBarColor(), appearance,
|
||||
APPEARANCE_LIGHT_NAVIGATION_BARS,
|
||||
taskDescription.getEnsureNavigationBarContrastWhenTransparent()
|
||||
&& context.getResources().getBoolean(R.bool.config_navBarNeedsScrim));
|
||||
mStatusBarPaint.setColor(mStatusBarColor);
|
||||
mNavigationBarPaint.setColor(mNavigationBarColor);
|
||||
mRequestedVisibleTypes = requestedVisibleTypes;
|
||||
}
|
||||
|
||||
void setInsets(Rect systemBarInsets) {
|
||||
mSystemBarInsets.set(systemBarInsets);
|
||||
}
|
||||
|
||||
int getStatusBarColorViewHeight() {
|
||||
final boolean forceBarBackground =
|
||||
(mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0;
|
||||
if (STATUS_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
|
||||
mRequestedVisibleTypes, mStatusBarColor, mWindowFlags, forceBarBackground)) {
|
||||
return (int) (mSystemBarInsets.top * mScale);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isNavigationBarColorViewVisible() {
|
||||
final boolean forceBarBackground =
|
||||
(mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0;
|
||||
return NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
|
||||
mRequestedVisibleTypes, mNavigationBarColor, mWindowFlags, forceBarBackground);
|
||||
}
|
||||
|
||||
void drawDecors(Canvas c) {
|
||||
drawStatusBarBackground(c, getStatusBarColorViewHeight());
|
||||
drawNavigationBarBackground(c);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void drawStatusBarBackground(Canvas c,
|
||||
int statusBarHeight) {
|
||||
if (statusBarHeight > 0 && Color.alpha(mStatusBarColor) != 0) {
|
||||
final int rightInset = (int) (mSystemBarInsets.right * mScale);
|
||||
c.drawRect(0, 0, c.getWidth() - rightInset, statusBarHeight, mStatusBarPaint);
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void drawNavigationBarBackground(Canvas c) {
|
||||
final Rect navigationBarRect = new Rect();
|
||||
getNavigationBarRect(c.getWidth(), c.getHeight(), mSystemBarInsets, navigationBarRect,
|
||||
mScale);
|
||||
final boolean visible = isNavigationBarColorViewVisible();
|
||||
if (visible && Color.alpha(mNavigationBarColor) != 0 && !navigationBarRect.isEmpty()) {
|
||||
c.drawRect(navigationBarRect, mNavigationBarPaint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user