/*
|
* Copyright (C) 2016 The Android Open Source Project
|
*
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
* you may not use this file except in compliance with the License.
|
* You may obtain a copy of the License at
|
*
|
* http://www.apache.org/licenses/LICENSE-2.0
|
*
|
* Unless required by applicable law or agreed to in writing, software
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* See the License for the specific language governing permissions and
|
* limitations under the License
|
*/
|
|
package com.android.server.wm;
|
|
import static android.graphics.Color.WHITE;
|
import static android.graphics.Color.alpha;
|
import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
|
import static android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
|
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.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.getColorViewLeftInset;
|
import static com.android.internal.policy.DecorView.getColorViewTopInset;
|
import static com.android.internal.policy.DecorView.getNavigationBarRect;
|
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
|
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
|
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
|
|
import android.annotation.Nullable;
|
import android.app.ActivityManager.TaskDescription;
|
import android.app.ActivityManager.TaskSnapshot;
|
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.Rect;
|
import android.os.Handler;
|
import android.os.Looper;
|
import android.os.Message;
|
import android.os.RemoteException;
|
import android.os.SystemClock;
|
import android.util.MergedConfiguration;
|
import android.util.Slog;
|
import android.view.DisplayCutout;
|
import android.view.IWindowSession;
|
import android.view.InsetsState;
|
import android.view.Surface;
|
import android.view.SurfaceControl;
|
import android.view.SurfaceSession;
|
import android.view.View;
|
import android.view.ViewGroup.LayoutParams;
|
import android.view.WindowManager;
|
import android.view.WindowManagerGlobal;
|
|
import com.android.internal.R;
|
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.policy.DecorView;
|
import com.android.internal.view.BaseIWindow;
|
import com.android.server.policy.WindowManagerPolicy.StartingSurface;
|
|
/**
|
* This class represents a starting window that shows a snapshot.
|
* <p>
|
* DO NOT HOLD THE WINDOW MANAGER LOCK WHEN CALLING METHODS OF THIS CLASS!
|
*/
|
class TaskSnapshotSurface implements StartingSurface {
|
|
private static final long SIZE_MISMATCH_MINIMUM_TIME_MS = 450;
|
|
/**
|
* 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.
|
*/
|
private 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 int PRIVATE_FLAG_INHERITS = PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
|
|
private static final String TAG = TAG_WITH_CLASS_NAME ? "SnapshotStartingWindow" : TAG_WM;
|
private static final int MSG_REPORT_DRAW = 0;
|
private static final String TITLE_FORMAT = "SnapshotStartingWindow for taskId=%s";
|
private final Window mWindow;
|
private final Surface mSurface;
|
private SurfaceControl mSurfaceControl;
|
private SurfaceControl mChildSurfaceControl;
|
private final IWindowSession mSession;
|
private final WindowManagerService mService;
|
private final Rect mTaskBounds;
|
private final Rect mStableInsets = new Rect();
|
private final Rect mContentInsets = new Rect();
|
private final Rect mFrame = new Rect();
|
private TaskSnapshot mSnapshot;
|
private final CharSequence mTitle;
|
private boolean mHasDrawn;
|
private long mShownTime;
|
private final Handler mHandler;
|
private boolean mSizeMismatch;
|
private final Paint mBackgroundPaint = new Paint();
|
private final int mStatusBarColor;
|
@VisibleForTesting final SystemBarBackgroundPainter mSystemBarBackgroundPainter;
|
private final int mOrientationOnCreation;
|
|
static TaskSnapshotSurface create(WindowManagerService service, AppWindowToken token,
|
TaskSnapshot snapshot) {
|
|
final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
|
final Window window = new Window();
|
final IWindowSession session = WindowManagerGlobal.getWindowSession();
|
window.setSession(session);
|
final SurfaceControl surfaceControl = new SurfaceControl();
|
final Rect tmpRect = new Rect();
|
final DisplayCutout.ParcelableWrapper tmpCutout = new DisplayCutout.ParcelableWrapper();
|
final Rect tmpFrame = new Rect();
|
final Rect taskBounds;
|
final Rect tmpContentInsets = new Rect();
|
final Rect tmpStableInsets = new Rect();
|
final InsetsState mTmpInsetsState = new InsetsState();
|
final MergedConfiguration tmpMergedConfiguration = new MergedConfiguration();
|
final TaskDescription taskDescription = new TaskDescription();
|
taskDescription.setBackgroundColor(WHITE);
|
final int sysUiVis;
|
final int windowFlags;
|
final int windowPrivateFlags;
|
final int currentOrientation;
|
synchronized (service.mGlobalLock) {
|
final WindowState mainWindow = token.findMainWindow();
|
final Task task = token.getTask();
|
if (task == null) {
|
Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find task for token="
|
+ token);
|
return null;
|
}
|
final AppWindowToken topFullscreenToken = token.getTask().getTopFullscreenAppToken();
|
if (topFullscreenToken == null) {
|
Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find top fullscreen for task="
|
+ task);
|
return null;
|
}
|
final WindowState topFullscreenWindow = topFullscreenToken.getTopFullscreenWindow();
|
if (mainWindow == null || topFullscreenWindow == null) {
|
Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find main window for token="
|
+ token);
|
return null;
|
}
|
sysUiVis = topFullscreenWindow.getSystemUiVisibility();
|
windowFlags = topFullscreenWindow.getAttrs().flags;
|
windowPrivateFlags = topFullscreenWindow.getAttrs().privateFlags;
|
|
layoutParams.packageName = mainWindow.getAttrs().packageName;
|
layoutParams.windowAnimations = mainWindow.getAttrs().windowAnimations;
|
layoutParams.dimAmount = mainWindow.getAttrs().dimAmount;
|
layoutParams.type = TYPE_APPLICATION_STARTING;
|
layoutParams.format = snapshot.getSnapshot().getFormat();
|
layoutParams.flags = (windowFlags & ~FLAG_INHERIT_EXCLUDES)
|
| FLAG_NOT_FOCUSABLE
|
| FLAG_NOT_TOUCHABLE;
|
layoutParams.privateFlags = windowPrivateFlags & PRIVATE_FLAG_INHERITS;
|
layoutParams.token = token.token;
|
layoutParams.width = LayoutParams.MATCH_PARENT;
|
layoutParams.height = LayoutParams.MATCH_PARENT;
|
layoutParams.systemUiVisibility = sysUiVis;
|
layoutParams.setTitle(String.format(TITLE_FORMAT, task.mTaskId));
|
|
final TaskDescription td = task.getTaskDescription();
|
if (td != null) {
|
taskDescription.copyFrom(td);
|
}
|
taskBounds = new Rect();
|
task.getBounds(taskBounds);
|
currentOrientation = topFullscreenWindow.getConfiguration().orientation;
|
}
|
try {
|
final int res = session.addToDisplay(window, window.mSeq, layoutParams,
|
View.GONE, token.getDisplayContent().getDisplayId(), tmpFrame, tmpRect, tmpRect,
|
tmpRect, tmpCutout, null, mTmpInsetsState);
|
if (res < 0) {
|
Slog.w(TAG, "Failed to add snapshot starting window res=" + res);
|
return null;
|
}
|
} catch (RemoteException e) {
|
// Local call.
|
}
|
final TaskSnapshotSurface snapshotSurface = new TaskSnapshotSurface(service, window,
|
surfaceControl, snapshot, layoutParams.getTitle(), taskDescription, sysUiVis,
|
windowFlags, windowPrivateFlags, taskBounds,
|
currentOrientation);
|
window.setOuter(snapshotSurface);
|
try {
|
session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, -1,
|
tmpFrame, tmpRect, tmpContentInsets, tmpRect, tmpStableInsets, tmpRect, tmpRect,
|
tmpCutout, tmpMergedConfiguration, surfaceControl, mTmpInsetsState);
|
} catch (RemoteException e) {
|
// Local call.
|
}
|
snapshotSurface.setFrames(tmpFrame, tmpContentInsets, tmpStableInsets);
|
snapshotSurface.drawSnapshot();
|
return snapshotSurface;
|
}
|
|
@VisibleForTesting
|
TaskSnapshotSurface(WindowManagerService service, Window window, SurfaceControl surfaceControl,
|
TaskSnapshot snapshot, CharSequence title, TaskDescription taskDescription,
|
int sysUiVis, int windowFlags, int windowPrivateFlags, Rect taskBounds,
|
int currentOrientation) {
|
mService = service;
|
mSurface = new Surface();
|
mHandler = new Handler(mService.mH.getLooper());
|
mSession = WindowManagerGlobal.getWindowSession();
|
mWindow = window;
|
mSurfaceControl = surfaceControl;
|
mSnapshot = snapshot;
|
mTitle = title;
|
int backgroundColor = taskDescription.getBackgroundColor();
|
mBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE);
|
mTaskBounds = taskBounds;
|
mSystemBarBackgroundPainter = new SystemBarBackgroundPainter(windowFlags,
|
windowPrivateFlags, sysUiVis, taskDescription, 1f);
|
mStatusBarColor = taskDescription.getStatusBarColor();
|
mOrientationOnCreation = currentOrientation;
|
}
|
|
@Override
|
public void remove() {
|
synchronized (mService.mGlobalLock) {
|
final long now = SystemClock.uptimeMillis();
|
if (mSizeMismatch && now - mShownTime < SIZE_MISMATCH_MINIMUM_TIME_MS) {
|
mHandler.postAtTime(this::remove, mShownTime + SIZE_MISMATCH_MINIMUM_TIME_MS);
|
if (DEBUG_STARTING_WINDOW) {
|
Slog.v(TAG, "Defer removing snapshot surface in " + (now - mShownTime) + "ms");
|
}
|
return;
|
}
|
}
|
try {
|
if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Removing snapshot surface");
|
mSession.remove(mWindow);
|
} catch (RemoteException e) {
|
// Local call.
|
}
|
}
|
|
@VisibleForTesting
|
void setFrames(Rect frame, Rect contentInsets, Rect stableInsets) {
|
mFrame.set(frame);
|
mContentInsets.set(contentInsets);
|
mStableInsets.set(stableInsets);
|
mSizeMismatch = (mFrame.width() != mSnapshot.getSnapshot().getWidth()
|
|| mFrame.height() != mSnapshot.getSnapshot().getHeight());
|
mSystemBarBackgroundPainter.setInsets(contentInsets, stableInsets);
|
}
|
|
private void drawSnapshot() {
|
final GraphicBuffer buffer = mSnapshot.getSnapshot();
|
mSurface.copyFrom(mSurfaceControl);
|
|
if (DEBUG_STARTING_WINDOW) Slog.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(buffer);
|
} else {
|
drawSizeMatchSnapshot(buffer);
|
}
|
synchronized (mService.mGlobalLock) {
|
mShownTime = SystemClock.uptimeMillis();
|
mHasDrawn = true;
|
}
|
reportDrawn();
|
|
// In case window manager leaks us, make sure we don't retain the snapshot.
|
mSnapshot = null;
|
}
|
|
private void drawSizeMatchSnapshot(GraphicBuffer buffer) {
|
mSurface.attachAndQueueBuffer(buffer);
|
mSurface.release();
|
}
|
|
private void drawSizeMismatchSnapshot(GraphicBuffer buffer) {
|
if (!mSurface.isValid()) {
|
throw new IllegalStateException("mSurface does not hold a valid surface.");
|
}
|
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.
|
mChildSurfaceControl = new SurfaceControl.Builder(session)
|
.setName(mTitle + " - task-snapshot-surface")
|
.setBufferSize(buffer.getWidth(), buffer.getHeight())
|
.setFormat(buffer.getFormat())
|
.setParent(mSurfaceControl)
|
.build();
|
Surface surface = new Surface();
|
surface.copyFrom(mChildSurfaceControl);
|
|
final Rect frame;
|
SurfaceControl.openTransaction();
|
try {
|
// We can just show the surface here as it will still be hidden as the parent is
|
// still hidden.
|
mChildSurfaceControl.show();
|
if (aspectRatioMismatch) {
|
// Clip off ugly navigation bar.
|
final Rect crop = calculateSnapshotCrop();
|
frame = calculateSnapshotFrame(crop);
|
mChildSurfaceControl.setWindowCrop(crop);
|
mChildSurfaceControl.setPosition(frame.left, frame.top);
|
} else {
|
frame = null;
|
}
|
|
// Scale the mismatch dimensions to fill the task bounds
|
final float scale = 1 / mSnapshot.getScale();
|
mChildSurfaceControl.setMatrix(scale, 0, 0, scale);
|
} finally {
|
SurfaceControl.closeTransaction();
|
}
|
surface.attachAndQueueBuffer(buffer);
|
surface.release();
|
|
if (aspectRatioMismatch) {
|
final Canvas c = mSurface.lockCanvas(null);
|
drawBackgroundAndBars(c, frame);
|
mSurface.unlockCanvasAndPost(c);
|
mSurface.release();
|
}
|
}
|
|
/**
|
* Calculates the snapshot crop in snapshot coordinate space.
|
*
|
* @return crop rect in snapshot coordinate space.
|
*/
|
@VisibleForTesting
|
Rect calculateSnapshotCrop() {
|
final Rect rect = new Rect();
|
rect.set(0, 0, mSnapshot.getSnapshot().getWidth(), mSnapshot.getSnapshot().getHeight());
|
final Rect insets = mSnapshot.getContentInsets();
|
|
// 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 * mSnapshot.getScale()),
|
isTop ? 0 : (int) (insets.top * mSnapshot.getScale()),
|
(int) (insets.right * mSnapshot.getScale()),
|
(int) (insets.bottom * mSnapshot.getScale()));
|
return rect;
|
}
|
|
/**
|
* Calculates the snapshot frame in window coordinate space from crop.
|
*
|
* @param crop rect that is in snapshot coordinate space.
|
*/
|
@VisibleForTesting
|
Rect calculateSnapshotFrame(Rect crop) {
|
final Rect frame = new Rect(crop);
|
final float scale = mSnapshot.getScale();
|
|
// Rescale the frame from snapshot to window coordinate space
|
frame.scale(1 / scale);
|
|
// By default, offset it to to top/left corner
|
frame.offsetTo((int) (-crop.left / scale), (int) (-crop.top / scale));
|
|
// However, we also need to make space for the navigation bar on the left side.
|
final int colorViewLeftInset = getColorViewLeftInset(mStableInsets.left,
|
mContentInsets.left);
|
frame.offset(colorViewLeftInset, 0);
|
return frame;
|
}
|
|
@VisibleForTesting
|
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);
|
}
|
|
private void reportDrawn() {
|
try {
|
mSession.finishDrawing(mWindow);
|
} catch (RemoteException e) {
|
// Local call.
|
}
|
}
|
|
private static Handler sHandler = new Handler(Looper.getMainLooper()) {
|
|
@Override
|
public void handleMessage(Message msg) {
|
switch (msg.what) {
|
case MSG_REPORT_DRAW:
|
final boolean hasDrawn;
|
final TaskSnapshotSurface surface = (TaskSnapshotSurface) msg.obj;
|
synchronized (surface.mService.mGlobalLock) {
|
hasDrawn = surface.mHasDrawn;
|
}
|
if (hasDrawn) {
|
surface.reportDrawn();
|
}
|
break;
|
}
|
}
|
};
|
|
@VisibleForTesting
|
static class Window extends BaseIWindow {
|
|
private TaskSnapshotSurface mOuter;
|
|
public void setOuter(TaskSnapshotSurface outer) {
|
mOuter = outer;
|
}
|
|
@Override
|
public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets,
|
Rect stableInsets, Rect outsets, boolean reportDraw,
|
MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout,
|
boolean alwaysConsumeSystemBars, int displayId,
|
DisplayCutout.ParcelableWrapper displayCutout) {
|
if (mergedConfiguration != null && mOuter != null
|
&& mOuter.mOrientationOnCreation
|
!= mergedConfiguration.getMergedConfiguration().orientation) {
|
|
// The orientation of the screen is changing. We better remove the snapshot ASAP as
|
// we are going to wait on the new window in any case to unfreeze the screen, and
|
// the starting window is not needed anymore.
|
sHandler.post(mOuter::remove);
|
}
|
if (reportDraw) {
|
sHandler.obtainMessage(MSG_REPORT_DRAW, mOuter).sendToTarget();
|
}
|
}
|
}
|
|
/**
|
* 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 Rect mContentInsets = new Rect();
|
private final Rect mStableInsets = new Rect();
|
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 int mSysUiVis;
|
private final float mScale;
|
|
SystemBarBackgroundPainter(int windowFlags, int windowPrivateFlags, int sysUiVis,
|
TaskDescription taskDescription, float scale) {
|
mWindowFlags = windowFlags;
|
mWindowPrivateFlags = windowPrivateFlags;
|
mSysUiVis = sysUiVis;
|
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(), sysUiVis,
|
SYSTEM_UI_FLAG_LIGHT_STATUS_BAR,
|
taskDescription.getEnsureStatusBarContrastWhenTransparent());
|
mNavigationBarColor = DecorView.calculateBarColor(windowFlags,
|
FLAG_TRANSLUCENT_NAVIGATION, semiTransparent,
|
taskDescription.getNavigationBarColor(), sysUiVis,
|
SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
|
taskDescription.getEnsureNavigationBarContrastWhenTransparent()
|
&& context.getResources().getBoolean(R.bool.config_navBarNeedsScrim));
|
mStatusBarPaint.setColor(mStatusBarColor);
|
mNavigationBarPaint.setColor(mNavigationBarColor);
|
}
|
|
void setInsets(Rect contentInsets, Rect stableInsets) {
|
mContentInsets.set(contentInsets);
|
mStableInsets.set(stableInsets);
|
}
|
|
int getStatusBarColorViewHeight() {
|
final boolean forceBarBackground =
|
(mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0;
|
if (STATUS_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
|
mSysUiVis, mStatusBarColor, mWindowFlags, forceBarBackground)) {
|
return (int) (getColorViewTopInset(mStableInsets.top, mContentInsets.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(
|
mSysUiVis, mNavigationBarColor, mWindowFlags, forceBarBackground);
|
}
|
|
void drawDecors(Canvas c, @Nullable Rect alreadyDrawnFrame) {
|
drawStatusBarBackground(c, alreadyDrawnFrame, getStatusBarColorViewHeight());
|
drawNavigationBarBackground(c);
|
}
|
|
@VisibleForTesting
|
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) (DecorView.getColorViewRightInset(mStableInsets.right,
|
mContentInsets.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(), mStableInsets, mContentInsets,
|
navigationBarRect, mScale);
|
final boolean visible = isNavigationBarColorViewVisible();
|
if (visible && Color.alpha(mNavigationBarColor) != 0 && !navigationBarRect.isEmpty()) {
|
c.drawRect(navigationBarRect, mNavigationBarPaint);
|
}
|
}
|
}
|
}
|