1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.server.wm; 18 19 import static android.graphics.Color.WHITE; 20 import static android.graphics.Color.alpha; 21 import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; 22 import static android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; 23 import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; 24 import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; 25 import static android.view.WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES; 26 import static android.view.WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE; 27 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; 28 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; 29 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; 30 import static android.view.WindowManager.LayoutParams.FLAG_SCALED; 31 import static android.view.WindowManager.LayoutParams.FLAG_SECURE; 32 import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY; 33 import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH; 34 import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; 35 import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; 36 import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; 37 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS; 38 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; 39 40 import static com.android.internal.policy.DecorView.NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES; 41 import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES; 42 import static com.android.internal.policy.DecorView.getColorViewLeftInset; 43 import static com.android.internal.policy.DecorView.getColorViewTopInset; 44 import static com.android.internal.policy.DecorView.getNavigationBarRect; 45 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW; 46 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; 47 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 48 49 import android.annotation.Nullable; 50 import android.app.ActivityManager.TaskDescription; 51 import android.app.ActivityManager.TaskSnapshot; 52 import android.app.ActivityThread; 53 import android.content.Context; 54 import android.graphics.Canvas; 55 import android.graphics.Color; 56 import android.graphics.GraphicBuffer; 57 import android.graphics.Paint; 58 import android.graphics.Rect; 59 import android.os.Handler; 60 import android.os.Looper; 61 import android.os.Message; 62 import android.os.RemoteException; 63 import android.os.SystemClock; 64 import android.util.MergedConfiguration; 65 import android.util.Slog; 66 import android.view.DisplayCutout; 67 import android.view.IWindowSession; 68 import android.view.InsetsState; 69 import android.view.Surface; 70 import android.view.SurfaceControl; 71 import android.view.SurfaceSession; 72 import android.view.View; 73 import android.view.ViewGroup.LayoutParams; 74 import android.view.WindowManager; 75 import android.view.WindowManagerGlobal; 76 77 import com.android.internal.R; 78 import com.android.internal.annotations.VisibleForTesting; 79 import com.android.internal.policy.DecorView; 80 import com.android.internal.view.BaseIWindow; 81 import com.android.server.policy.WindowManagerPolicy.StartingSurface; 82 83 /** 84 * This class represents a starting window that shows a snapshot. 85 * <p> 86 * DO NOT HOLD THE WINDOW MANAGER LOCK WHEN CALLING METHODS OF THIS CLASS! 87 */ 88 class TaskSnapshotSurface implements StartingSurface { 89 90 private static final long SIZE_MISMATCH_MINIMUM_TIME_MS = 450; 91 92 /** 93 * When creating the starting window, we use the exact same layout flags such that we end up 94 * with a window with the exact same dimensions etc. However, these flags are not used in layout 95 * and might cause other side effects so we exclude them. 96 */ 97 private static final int FLAG_INHERIT_EXCLUDES = FLAG_NOT_FOCUSABLE 98 | FLAG_NOT_TOUCHABLE 99 | FLAG_NOT_TOUCH_MODAL 100 | FLAG_ALT_FOCUSABLE_IM 101 | FLAG_NOT_FOCUSABLE 102 | FLAG_HARDWARE_ACCELERATED 103 | FLAG_IGNORE_CHEEK_PRESSES 104 | FLAG_LOCAL_FOCUS_MODE 105 | FLAG_SLIPPERY 106 | FLAG_WATCH_OUTSIDE_TOUCH 107 | FLAG_SPLIT_TOUCH 108 | FLAG_SCALED 109 | FLAG_SECURE; 110 111 private static final int PRIVATE_FLAG_INHERITS = PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS; 112 113 private static final String TAG = TAG_WITH_CLASS_NAME ? "SnapshotStartingWindow" : TAG_WM; 114 private static final int MSG_REPORT_DRAW = 0; 115 private static final String TITLE_FORMAT = "SnapshotStartingWindow for taskId=%s"; 116 private final Window mWindow; 117 private final Surface mSurface; 118 private SurfaceControl mSurfaceControl; 119 private SurfaceControl mChildSurfaceControl; 120 private final IWindowSession mSession; 121 private final WindowManagerService mService; 122 private final Rect mTaskBounds; 123 private final Rect mStableInsets = new Rect(); 124 private final Rect mContentInsets = new Rect(); 125 private final Rect mFrame = new Rect(); 126 private TaskSnapshot mSnapshot; 127 private final CharSequence mTitle; 128 private boolean mHasDrawn; 129 private long mShownTime; 130 private final Handler mHandler; 131 private boolean mSizeMismatch; 132 private final Paint mBackgroundPaint = new Paint(); 133 private final int mStatusBarColor; 134 @VisibleForTesting final SystemBarBackgroundPainter mSystemBarBackgroundPainter; 135 private final int mOrientationOnCreation; 136 create(WindowManagerService service, AppWindowToken token, TaskSnapshot snapshot)137 static TaskSnapshotSurface create(WindowManagerService service, AppWindowToken token, 138 TaskSnapshot snapshot) { 139 140 final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(); 141 final Window window = new Window(); 142 final IWindowSession session = WindowManagerGlobal.getWindowSession(); 143 window.setSession(session); 144 final SurfaceControl surfaceControl = new SurfaceControl(); 145 final Rect tmpRect = new Rect(); 146 final DisplayCutout.ParcelableWrapper tmpCutout = new DisplayCutout.ParcelableWrapper(); 147 final Rect tmpFrame = new Rect(); 148 final Rect taskBounds; 149 final Rect tmpContentInsets = new Rect(); 150 final Rect tmpStableInsets = new Rect(); 151 final InsetsState mTmpInsetsState = new InsetsState(); 152 final MergedConfiguration tmpMergedConfiguration = new MergedConfiguration(); 153 final TaskDescription taskDescription = new TaskDescription(); 154 taskDescription.setBackgroundColor(WHITE); 155 final int sysUiVis; 156 final int windowFlags; 157 final int windowPrivateFlags; 158 final int currentOrientation; 159 synchronized (service.mGlobalLock) { 160 final WindowState mainWindow = token.findMainWindow(); 161 final Task task = token.getTask(); 162 if (task == null) { 163 Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find task for token=" 164 + token); 165 return null; 166 } 167 final AppWindowToken topFullscreenToken = token.getTask().getTopFullscreenAppToken(); 168 if (topFullscreenToken == null) { 169 Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find top fullscreen for task=" 170 + task); 171 return null; 172 } 173 final WindowState topFullscreenWindow = topFullscreenToken.getTopFullscreenWindow(); 174 if (mainWindow == null || topFullscreenWindow == null) { 175 Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find main window for token=" 176 + token); 177 return null; 178 } 179 sysUiVis = topFullscreenWindow.getSystemUiVisibility(); 180 windowFlags = topFullscreenWindow.getAttrs().flags; 181 windowPrivateFlags = topFullscreenWindow.getAttrs().privateFlags; 182 183 layoutParams.packageName = mainWindow.getAttrs().packageName; 184 layoutParams.windowAnimations = mainWindow.getAttrs().windowAnimations; 185 layoutParams.dimAmount = mainWindow.getAttrs().dimAmount; 186 layoutParams.type = TYPE_APPLICATION_STARTING; 187 layoutParams.format = snapshot.getSnapshot().getFormat(); 188 layoutParams.flags = (windowFlags & ~FLAG_INHERIT_EXCLUDES) 189 | FLAG_NOT_FOCUSABLE 190 | FLAG_NOT_TOUCHABLE; 191 layoutParams.privateFlags = windowPrivateFlags & PRIVATE_FLAG_INHERITS; 192 layoutParams.token = token.token; 193 layoutParams.width = LayoutParams.MATCH_PARENT; 194 layoutParams.height = LayoutParams.MATCH_PARENT; 195 layoutParams.systemUiVisibility = sysUiVis; 196 layoutParams.setTitle(String.format(TITLE_FORMAT, task.mTaskId)); 197 198 final TaskDescription td = task.getTaskDescription(); 199 if (td != null) { 200 taskDescription.copyFrom(td); 201 } 202 taskBounds = new Rect(); 203 task.getBounds(taskBounds); 204 currentOrientation = topFullscreenWindow.getConfiguration().orientation; 205 } 206 try { 207 final int res = session.addToDisplay(window, window.mSeq, layoutParams, 208 View.GONE, token.getDisplayContent().getDisplayId(), tmpFrame, tmpRect, tmpRect, 209 tmpRect, tmpCutout, null, mTmpInsetsState); 210 if (res < 0) { 211 Slog.w(TAG, "Failed to add snapshot starting window res=" + res); 212 return null; 213 } 214 } catch (RemoteException e) { 215 // Local call. 216 } 217 final TaskSnapshotSurface snapshotSurface = new TaskSnapshotSurface(service, window, 218 surfaceControl, snapshot, layoutParams.getTitle(), taskDescription, sysUiVis, 219 windowFlags, windowPrivateFlags, taskBounds, 220 currentOrientation); 221 window.setOuter(snapshotSurface); 222 try { 223 session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, -1, 224 tmpFrame, tmpRect, tmpContentInsets, tmpRect, tmpStableInsets, tmpRect, tmpRect, 225 tmpCutout, tmpMergedConfiguration, surfaceControl, mTmpInsetsState); 226 } catch (RemoteException e) { 227 // Local call. 228 } 229 snapshotSurface.setFrames(tmpFrame, tmpContentInsets, tmpStableInsets); 230 snapshotSurface.drawSnapshot(); 231 return snapshotSurface; 232 } 233 234 @VisibleForTesting TaskSnapshotSurface(WindowManagerService service, Window window, SurfaceControl surfaceControl, TaskSnapshot snapshot, CharSequence title, TaskDescription taskDescription, int sysUiVis, int windowFlags, int windowPrivateFlags, Rect taskBounds, int currentOrientation)235 TaskSnapshotSurface(WindowManagerService service, Window window, SurfaceControl surfaceControl, 236 TaskSnapshot snapshot, CharSequence title, TaskDescription taskDescription, 237 int sysUiVis, int windowFlags, int windowPrivateFlags, Rect taskBounds, 238 int currentOrientation) { 239 mService = service; 240 mSurface = new Surface(); 241 mHandler = new Handler(mService.mH.getLooper()); 242 mSession = WindowManagerGlobal.getWindowSession(); 243 mWindow = window; 244 mSurfaceControl = surfaceControl; 245 mSnapshot = snapshot; 246 mTitle = title; 247 int backgroundColor = taskDescription.getBackgroundColor(); 248 mBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE); 249 mTaskBounds = taskBounds; 250 mSystemBarBackgroundPainter = new SystemBarBackgroundPainter(windowFlags, 251 windowPrivateFlags, sysUiVis, taskDescription, 1f); 252 mStatusBarColor = taskDescription.getStatusBarColor(); 253 mOrientationOnCreation = currentOrientation; 254 } 255 256 @Override remove()257 public void remove() { 258 synchronized (mService.mGlobalLock) { 259 final long now = SystemClock.uptimeMillis(); 260 if (mSizeMismatch && now - mShownTime < SIZE_MISMATCH_MINIMUM_TIME_MS) { 261 mHandler.postAtTime(this::remove, mShownTime + SIZE_MISMATCH_MINIMUM_TIME_MS); 262 if (DEBUG_STARTING_WINDOW) { 263 Slog.v(TAG, "Defer removing snapshot surface in " + (now - mShownTime) + "ms"); 264 } 265 return; 266 } 267 } 268 try { 269 if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Removing snapshot surface"); 270 mSession.remove(mWindow); 271 } catch (RemoteException e) { 272 // Local call. 273 } 274 } 275 276 @VisibleForTesting setFrames(Rect frame, Rect contentInsets, Rect stableInsets)277 void setFrames(Rect frame, Rect contentInsets, Rect stableInsets) { 278 mFrame.set(frame); 279 mContentInsets.set(contentInsets); 280 mStableInsets.set(stableInsets); 281 mSizeMismatch = (mFrame.width() != mSnapshot.getSnapshot().getWidth() 282 || mFrame.height() != mSnapshot.getSnapshot().getHeight()); 283 mSystemBarBackgroundPainter.setInsets(contentInsets, stableInsets); 284 } 285 drawSnapshot()286 private void drawSnapshot() { 287 mSurface.copyFrom(mSurfaceControl); 288 289 if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Drawing snapshot surface sizeMismatch=" 290 + mSizeMismatch); 291 if (mSizeMismatch) { 292 // The dimensions of the buffer and the window don't match, so attaching the buffer 293 // will fail. Better create a child window with the exact dimensions and fill the parent 294 // window with the background color! 295 drawSizeMismatchSnapshot(); 296 } else { 297 drawSizeMatchSnapshot(); 298 } 299 synchronized (mService.mGlobalLock) { 300 mShownTime = SystemClock.uptimeMillis(); 301 mHasDrawn = true; 302 } 303 reportDrawn(); 304 305 // In case window manager leaks us, make sure we don't retain the snapshot. 306 mSnapshot = null; 307 } 308 drawSizeMatchSnapshot()309 private void drawSizeMatchSnapshot() { 310 mSurface.attachAndQueueBufferWithColorSpace(mSnapshot.getSnapshot(), 311 mSnapshot.getColorSpace()); 312 mSurface.release(); 313 } 314 drawSizeMismatchSnapshot()315 private void drawSizeMismatchSnapshot() { 316 if (!mSurface.isValid()) { 317 throw new IllegalStateException("mSurface does not hold a valid surface."); 318 } 319 final GraphicBuffer buffer = mSnapshot.getSnapshot(); 320 final SurfaceSession session = new SurfaceSession(); 321 // We consider nearly matched dimensions as there can be rounding errors and the user won't 322 // notice very minute differences from scaling one dimension more than the other 323 final boolean aspectRatioMismatch = Math.abs( 324 ((float) buffer.getWidth() / buffer.getHeight()) 325 - ((float) mFrame.width() / mFrame.height())) > 0.01f; 326 327 // Keep a reference to it such that it doesn't get destroyed when finalized. 328 mChildSurfaceControl = new SurfaceControl.Builder(session) 329 .setName(mTitle + " - task-snapshot-surface") 330 .setBufferSize(buffer.getWidth(), buffer.getHeight()) 331 .setFormat(buffer.getFormat()) 332 .setParent(mSurfaceControl) 333 .build(); 334 Surface surface = new Surface(); 335 surface.copyFrom(mChildSurfaceControl); 336 337 final Rect frame; 338 SurfaceControl.openTransaction(); 339 try { 340 // We can just show the surface here as it will still be hidden as the parent is 341 // still hidden. 342 mChildSurfaceControl.show(); 343 if (aspectRatioMismatch) { 344 // Clip off ugly navigation bar. 345 final Rect crop = calculateSnapshotCrop(); 346 frame = calculateSnapshotFrame(crop); 347 mChildSurfaceControl.setWindowCrop(crop); 348 mChildSurfaceControl.setPosition(frame.left, frame.top); 349 } else { 350 frame = null; 351 } 352 353 // Scale the mismatch dimensions to fill the task bounds 354 final float scale = 1 / mSnapshot.getScale(); 355 mChildSurfaceControl.setMatrix(scale, 0, 0, scale); 356 } finally { 357 SurfaceControl.closeTransaction(); 358 } 359 surface.attachAndQueueBufferWithColorSpace(buffer, mSnapshot.getColorSpace()); 360 surface.release(); 361 362 if (aspectRatioMismatch) { 363 final Canvas c = mSurface.lockCanvas(null); 364 drawBackgroundAndBars(c, frame); 365 mSurface.unlockCanvasAndPost(c); 366 mSurface.release(); 367 } 368 } 369 370 /** 371 * Calculates the snapshot crop in snapshot coordinate space. 372 * 373 * @return crop rect in snapshot coordinate space. 374 */ 375 @VisibleForTesting calculateSnapshotCrop()376 Rect calculateSnapshotCrop() { 377 final Rect rect = new Rect(); 378 rect.set(0, 0, mSnapshot.getSnapshot().getWidth(), mSnapshot.getSnapshot().getHeight()); 379 final Rect insets = mSnapshot.getContentInsets(); 380 381 // Let's remove all system decorations except the status bar, but only if the task is at the 382 // very top of the screen. 383 final boolean isTop = mTaskBounds.top == 0 && mFrame.top == 0; 384 rect.inset((int) (insets.left * mSnapshot.getScale()), 385 isTop ? 0 : (int) (insets.top * mSnapshot.getScale()), 386 (int) (insets.right * mSnapshot.getScale()), 387 (int) (insets.bottom * mSnapshot.getScale())); 388 return rect; 389 } 390 391 /** 392 * Calculates the snapshot frame in window coordinate space from crop. 393 * 394 * @param crop rect that is in snapshot coordinate space. 395 */ 396 @VisibleForTesting calculateSnapshotFrame(Rect crop)397 Rect calculateSnapshotFrame(Rect crop) { 398 final Rect frame = new Rect(crop); 399 final float scale = mSnapshot.getScale(); 400 401 // Rescale the frame from snapshot to window coordinate space 402 frame.scale(1 / scale); 403 404 // By default, offset it to to top/left corner 405 frame.offsetTo((int) (-crop.left / scale), (int) (-crop.top / scale)); 406 407 // However, we also need to make space for the navigation bar on the left side. 408 final int colorViewLeftInset = getColorViewLeftInset(mStableInsets.left, 409 mContentInsets.left); 410 frame.offset(colorViewLeftInset, 0); 411 return frame; 412 } 413 414 @VisibleForTesting drawBackgroundAndBars(Canvas c, Rect frame)415 void drawBackgroundAndBars(Canvas c, Rect frame) { 416 final int statusBarHeight = mSystemBarBackgroundPainter.getStatusBarColorViewHeight(); 417 final boolean fillHorizontally = c.getWidth() > frame.right; 418 final boolean fillVertically = c.getHeight() > frame.bottom; 419 if (fillHorizontally) { 420 c.drawRect(frame.right, alpha(mStatusBarColor) == 0xFF ? statusBarHeight : 0, 421 c.getWidth(), fillVertically 422 ? frame.bottom 423 : c.getHeight(), 424 mBackgroundPaint); 425 } 426 if (fillVertically) { 427 c.drawRect(0, frame.bottom, c.getWidth(), c.getHeight(), mBackgroundPaint); 428 } 429 mSystemBarBackgroundPainter.drawDecors(c, frame); 430 } 431 reportDrawn()432 private void reportDrawn() { 433 try { 434 mSession.finishDrawing(mWindow); 435 } catch (RemoteException e) { 436 // Local call. 437 } 438 } 439 440 private static Handler sHandler = new Handler(Looper.getMainLooper()) { 441 442 @Override 443 public void handleMessage(Message msg) { 444 switch (msg.what) { 445 case MSG_REPORT_DRAW: 446 final boolean hasDrawn; 447 final TaskSnapshotSurface surface = (TaskSnapshotSurface) msg.obj; 448 synchronized (surface.mService.mGlobalLock) { 449 hasDrawn = surface.mHasDrawn; 450 } 451 if (hasDrawn) { 452 surface.reportDrawn(); 453 } 454 break; 455 } 456 } 457 }; 458 459 @VisibleForTesting 460 static class Window extends BaseIWindow { 461 462 private TaskSnapshotSurface mOuter; 463 setOuter(TaskSnapshotSurface outer)464 public void setOuter(TaskSnapshotSurface outer) { 465 mOuter = outer; 466 } 467 468 @Override 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)469 public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets, 470 Rect stableInsets, Rect outsets, boolean reportDraw, 471 MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout, 472 boolean alwaysConsumeSystemBars, int displayId, 473 DisplayCutout.ParcelableWrapper displayCutout) { 474 if (mergedConfiguration != null && mOuter != null 475 && mOuter.mOrientationOnCreation 476 != mergedConfiguration.getMergedConfiguration().orientation) { 477 478 // The orientation of the screen is changing. We better remove the snapshot ASAP as 479 // we are going to wait on the new window in any case to unfreeze the screen, and 480 // the starting window is not needed anymore. 481 sHandler.post(mOuter::remove); 482 } 483 if (reportDraw) { 484 sHandler.obtainMessage(MSG_REPORT_DRAW, mOuter).sendToTarget(); 485 } 486 } 487 } 488 489 /** 490 * Helper class to draw the background of the system bars in regions the task snapshot isn't 491 * filling the window. 492 */ 493 static class SystemBarBackgroundPainter { 494 495 private final Rect mContentInsets = new Rect(); 496 private final Rect mStableInsets = new Rect(); 497 private final Paint mStatusBarPaint = new Paint(); 498 private final Paint mNavigationBarPaint = new Paint(); 499 private final int mStatusBarColor; 500 private final int mNavigationBarColor; 501 private final int mWindowFlags; 502 private final int mWindowPrivateFlags; 503 private final int mSysUiVis; 504 private final float mScale; 505 SystemBarBackgroundPainter(int windowFlags, int windowPrivateFlags, int sysUiVis, TaskDescription taskDescription, float scale)506 SystemBarBackgroundPainter(int windowFlags, int windowPrivateFlags, int sysUiVis, 507 TaskDescription taskDescription, float scale) { 508 mWindowFlags = windowFlags; 509 mWindowPrivateFlags = windowPrivateFlags; 510 mSysUiVis = sysUiVis; 511 mScale = scale; 512 final Context context = ActivityThread.currentActivityThread().getSystemUiContext(); 513 final int semiTransparent = context.getColor( 514 R.color.system_bar_background_semi_transparent); 515 mStatusBarColor = DecorView.calculateBarColor(windowFlags, FLAG_TRANSLUCENT_STATUS, 516 semiTransparent, taskDescription.getStatusBarColor(), sysUiVis, 517 SYSTEM_UI_FLAG_LIGHT_STATUS_BAR, 518 taskDescription.getEnsureStatusBarContrastWhenTransparent()); 519 mNavigationBarColor = DecorView.calculateBarColor(windowFlags, 520 FLAG_TRANSLUCENT_NAVIGATION, semiTransparent, 521 taskDescription.getNavigationBarColor(), sysUiVis, 522 SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, 523 taskDescription.getEnsureNavigationBarContrastWhenTransparent() 524 && context.getResources().getBoolean(R.bool.config_navBarNeedsScrim)); 525 mStatusBarPaint.setColor(mStatusBarColor); 526 mNavigationBarPaint.setColor(mNavigationBarColor); 527 } 528 setInsets(Rect contentInsets, Rect stableInsets)529 void setInsets(Rect contentInsets, Rect stableInsets) { 530 mContentInsets.set(contentInsets); 531 mStableInsets.set(stableInsets); 532 } 533 getStatusBarColorViewHeight()534 int getStatusBarColorViewHeight() { 535 final boolean forceBarBackground = 536 (mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0; 537 if (STATUS_BAR_COLOR_VIEW_ATTRIBUTES.isVisible( 538 mSysUiVis, mStatusBarColor, mWindowFlags, forceBarBackground)) { 539 return (int) (getColorViewTopInset(mStableInsets.top, mContentInsets.top) * mScale); 540 } else { 541 return 0; 542 } 543 } 544 isNavigationBarColorViewVisible()545 private boolean isNavigationBarColorViewVisible() { 546 final boolean forceBarBackground = 547 (mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0; 548 return NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES.isVisible( 549 mSysUiVis, mNavigationBarColor, mWindowFlags, forceBarBackground); 550 } 551 drawDecors(Canvas c, @Nullable Rect alreadyDrawnFrame)552 void drawDecors(Canvas c, @Nullable Rect alreadyDrawnFrame) { 553 drawStatusBarBackground(c, alreadyDrawnFrame, getStatusBarColorViewHeight()); 554 drawNavigationBarBackground(c); 555 } 556 557 @VisibleForTesting drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame, int statusBarHeight)558 void drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame, 559 int statusBarHeight) { 560 if (statusBarHeight > 0 && Color.alpha(mStatusBarColor) != 0 561 && (alreadyDrawnFrame == null || c.getWidth() > alreadyDrawnFrame.right)) { 562 final int rightInset = (int) (DecorView.getColorViewRightInset(mStableInsets.right, 563 mContentInsets.right) * mScale); 564 final int left = alreadyDrawnFrame != null ? alreadyDrawnFrame.right : 0; 565 c.drawRect(left, 0, c.getWidth() - rightInset, statusBarHeight, mStatusBarPaint); 566 } 567 } 568 569 @VisibleForTesting drawNavigationBarBackground(Canvas c)570 void drawNavigationBarBackground(Canvas c) { 571 final Rect navigationBarRect = new Rect(); 572 getNavigationBarRect(c.getWidth(), c.getHeight(), mStableInsets, mContentInsets, 573 navigationBarRect, mScale); 574 final boolean visible = isNavigationBarColorViewVisible(); 575 if (visible && Color.alpha(mNavigationBarColor) != 0 && !navigationBarRect.isEmpty()) { 576 c.drawRect(navigationBarRect, mNavigationBarPaint); 577 } 578 } 579 } 580 } 581