551 lines
25 KiB
Java
551 lines
25 KiB
Java
/*
|
|
* 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_DIM_BEHIND;
|
|
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.INPUT_FEATURE_NO_INPUT_CHANNEL;
|
|
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EDGE_TO_EDGE_ENFORCED;
|
|
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
|
|
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
|
|
|
|
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.NonNull;
|
|
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.Paint;
|
|
import android.graphics.PixelFormat;
|
|
import android.graphics.Rect;
|
|
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
|
|
| FLAG_DIM_BEHIND;
|
|
|
|
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 mFrame = new Rect();
|
|
private final Rect mSystemBarInsets = new Rect();
|
|
private final int mSnapshotW;
|
|
private final int mSnapshotH;
|
|
private boolean mSizeMismatch;
|
|
|
|
public SnapshotSurface(SurfaceControl rootSurface, TaskSnapshot snapshot,
|
|
CharSequence title) {
|
|
mRootSurface = rootSurface;
|
|
mSnapshot = snapshot;
|
|
mTitle = title;
|
|
final HardwareBuffer hwBuffer = snapshot.getHardwareBuffer();
|
|
mSnapshotW = hwBuffer.getWidth();
|
|
mSnapshotH = hwBuffer.getHeight();
|
|
}
|
|
|
|
/**
|
|
* Initiate system bar painter to draw the system bar background.
|
|
*/
|
|
@VisibleForTesting
|
|
public 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 that the snapshot should fill. It is the bounds of a task or activity.
|
|
*/
|
|
@VisibleForTesting
|
|
public void setFrames(Rect frame, Rect systemBarInsets) {
|
|
mFrame.set(frame);
|
|
mSystemBarInsets.set(systemBarInsets);
|
|
mSizeMismatch = (mFrame.width() != mSnapshotW || mFrame.height() != mSnapshotH);
|
|
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
|
|
boolean aspectRatioMismatch = !isAspectRatioMatch(mFrame, mSnapshotW, mSnapshotH);
|
|
|
|
// 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;
|
|
final Rect letterboxInsets = mSnapshot.getLetterboxInsets();
|
|
float offsetX = letterboxInsets.left;
|
|
float offsetY = letterboxInsets.top;
|
|
// We can just show the surface here as it will still be hidden as the parent is
|
|
// still hidden.
|
|
mTransaction.show(childSurfaceControl);
|
|
if (aspectRatioMismatch) {
|
|
Rect crop = null;
|
|
if (letterboxInsets.left != 0 || letterboxInsets.top != 0
|
|
|| letterboxInsets.right != 0 || letterboxInsets.bottom != 0) {
|
|
// Clip off letterbox.
|
|
crop = calculateSnapshotCrop(letterboxInsets);
|
|
// If the snapshot can cover the frame, then no need to draw background.
|
|
aspectRatioMismatch = !isAspectRatioMatch(mFrame, crop);
|
|
}
|
|
// if letterbox doesn't match window frame, try crop by content insets
|
|
if (aspectRatioMismatch) {
|
|
// Clip off ugly navigation bar.
|
|
final Rect contentInsets = mSnapshot.getContentInsets();
|
|
crop = calculateSnapshotCrop(contentInsets);
|
|
offsetX = contentInsets.left;
|
|
offsetY = contentInsets.top;
|
|
}
|
|
frame = calculateSnapshotFrame(crop);
|
|
mTransaction.setCrop(childSurfaceControl, crop);
|
|
} else {
|
|
frame = null;
|
|
}
|
|
|
|
// Align the snapshot with content area.
|
|
if (offsetX != 0f || offsetY != 0f) {
|
|
mTransaction.setPosition(childSurfaceControl,
|
|
-offsetX * mFrame.width() / mSnapshot.getTaskSize().x,
|
|
-offsetY * mFrame.height() / mSnapshot.getTaskSize().y);
|
|
}
|
|
// Scale the mismatch dimensions to fill the target frame.
|
|
final float scaleX = (float) mFrame.width() / mSnapshotW;
|
|
final float scaleY = (float) mFrame.height() / mSnapshotH;
|
|
mTransaction.setScale(childSurfaceControl, scaleX, scaleY);
|
|
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);
|
|
final Canvas c = background != null ? background.lockCanvas() : null;
|
|
if (c == null) {
|
|
Log.e(TAG, "Unable to draw snapshot: failed to allocate graphic buffer for "
|
|
+ mTitle);
|
|
mTransaction.clear();
|
|
childSurfaceControl.release();
|
|
return;
|
|
}
|
|
drawBackgroundAndBars(c, frame);
|
|
background.unlockCanvasAndPost(c);
|
|
mTransaction.setBuffer(mRootSurface,
|
|
HardwareBuffer.createFromGraphicBuffer(background));
|
|
}
|
|
mTransaction.apply();
|
|
childSurfaceControl.release();
|
|
}
|
|
|
|
/**
|
|
* Calculates the snapshot crop in snapshot coordinate space.
|
|
* @param insets Content insets or Letterbox insets
|
|
* @return crop rect in snapshot coordinate space.
|
|
*/
|
|
@VisibleForTesting
|
|
public Rect calculateSnapshotCrop(@NonNull Rect insets) {
|
|
final Rect rect = new Rect();
|
|
rect.set(0, 0, mSnapshotW, mSnapshotH);
|
|
|
|
final float scaleX = (float) mSnapshotW / mSnapshot.getTaskSize().x;
|
|
final float scaleY = (float) mSnapshotH / 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 = 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.
|
|
*/
|
|
@VisibleForTesting
|
|
public Rect calculateSnapshotFrame(Rect crop) {
|
|
final float scaleX = (float) mSnapshotW / mSnapshot.getTaskSize().x;
|
|
final float scaleY = (float) mSnapshotH / 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.
|
|
*/
|
|
@VisibleForTesting
|
|
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(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.
|
|
*/
|
|
@VisibleForTesting
|
|
public void drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame) {
|
|
mSystemBarBackgroundPainter.drawStatusBarBackground(c, alreadyDrawnFrame,
|
|
mSystemBarBackgroundPainter.getStatusBarColorViewHeight());
|
|
}
|
|
|
|
/**
|
|
* Ask system bar background painter to draw navigation bar background.
|
|
*/
|
|
@VisibleForTesting
|
|
public void drawNavigationBarBackground(Canvas c) {
|
|
mSystemBarBackgroundPainter.drawNavigationBarBackground(c);
|
|
}
|
|
}
|
|
|
|
private static boolean isAspectRatioMatch(Rect frame, int w, int h) {
|
|
if (frame.isEmpty()) {
|
|
return false;
|
|
}
|
|
return Math.abs(((float) w / h) - ((float) frame.width() / frame.height())) <= 0.01f;
|
|
}
|
|
|
|
private static boolean isAspectRatioMatch(Rect frame1, Rect frame2) {
|
|
if (frame1.isEmpty() || frame2.isEmpty()) {
|
|
return false;
|
|
}
|
|
return Math.abs(
|
|
((float) frame2.width() / frame2.height())
|
|
- ((float) frame1.width() / frame1.height())) <= 0.01f;
|
|
}
|
|
|
|
/**
|
|
* Get or create a TaskDescription from a RunningTaskInfo.
|
|
*/
|
|
public static ActivityManager.TaskDescription getOrCreateTaskDescription(
|
|
ActivityManager.RunningTaskInfo runningTaskInfo) {
|
|
final ActivityManager.TaskDescription taskDescription;
|
|
if (runningTaskInfo.taskDescription != null) {
|
|
taskDescription = runningTaskInfo.taskDescription;
|
|
} else {
|
|
taskDescription = new ActivityManager.TaskDescription();
|
|
taskDescription.setBackgroundColor(WHITE);
|
|
}
|
|
return taskDescription;
|
|
}
|
|
|
|
/**
|
|
* Help method to draw the snapshot on a surface.
|
|
*/
|
|
public static void drawSnapshotOnSurface(StartingWindowInfo info, WindowManager.LayoutParams lp,
|
|
SurfaceControl rootSurface, TaskSnapshot snapshot,
|
|
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());
|
|
|
|
final WindowManager.LayoutParams attrs = info.topOpaqueWindowLayoutParams;
|
|
final ActivityManager.RunningTaskInfo runningTaskInfo = info.taskInfo;
|
|
final ActivityManager.TaskDescription taskDescription =
|
|
getOrCreateTaskDescription(runningTaskInfo);
|
|
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;
|
|
layoutParams.privateFlags =
|
|
(windowPrivateFlags
|
|
& (PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS
|
|
| PRIVATE_FLAG_EDGE_TO_EDGE_ENFORCED))
|
|
// Setting as trusted overlay to let touches pass through. This is safe because this
|
|
// window is controlled by the system.
|
|
| PRIVATE_FLAG_TRUSTED_OVERLAY;
|
|
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);
|
|
layoutParams.inputFeatures |= INPUT_FEATURE_NO_INPUT_CHANNEL;
|
|
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(),
|
|
false /* movesBarColorToScrim */);
|
|
mNavigationBarColor = DecorView.calculateBarColor(windowFlags,
|
|
FLAG_TRANSLUCENT_NAVIGATION, semiTransparent,
|
|
taskDescription.getNavigationBarColor(), appearance,
|
|
APPEARANCE_LIGHT_NAVIGATION_BARS,
|
|
taskDescription.getEnsureNavigationBarContrastWhenTransparent()
|
|
&& context.getResources().getBoolean(
|
|
R.bool.config_navBarNeedsScrim),
|
|
(windowPrivateFlags & PRIVATE_FLAG_EDGE_TO_EDGE_ENFORCED) != 0);
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|