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 final GraphicBuffer buffer = mSnapshot.getSnapshot(); 288 mSurface.copyFrom(mSurfaceControl); 289 290 if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Drawing snapshot surface sizeMismatch=" 291 + mSizeMismatch); 292 if (mSizeMismatch) { 293 // The dimensions of the buffer and the window don't match, so attaching the buffer 294 // will fail. Better create a child window with the exact dimensions and fill the parent 295 // window with the background color! 296 drawSizeMismatchSnapshot(buffer); 297 } else { 298 drawSizeMatchSnapshot(buffer); 299 } 300 synchronized (mService.mGlobalLock) { 301 mShownTime = SystemClock.uptimeMillis(); 302 mHasDrawn = true; 303 } 304 reportDrawn(); 305 306 // In case window manager leaks us, make sure we don't retain the snapshot. 307 mSnapshot = null; 308 } 309 drawSizeMatchSnapshot(GraphicBuffer buffer)310 private void drawSizeMatchSnapshot(GraphicBuffer buffer) { 311 mSurface.attachAndQueueBuffer(buffer); 312 mSurface.release(); 313 } 314 drawSizeMismatchSnapshot(GraphicBuffer buffer)315 private void drawSizeMismatchSnapshot(GraphicBuffer buffer) { 316 if (!mSurface.isValid()) { 317 throw new IllegalStateException("mSurface does not hold a valid surface."); 318 } 319 final SurfaceSession session = new SurfaceSession(); 320 // We consider nearly matched dimensions as there can be rounding errors and the user won't 321 // notice very minute differences from scaling one dimension more than the other 322 final boolean aspectRatioMismatch = Math.abs( 323 ((float) buffer.getWidth() / buffer.getHeight()) 324 - ((float) mFrame.width() / mFrame.height())) > 0.01f; 325 326 // Keep a reference to it such that it doesn't get destroyed when finalized. 327 mChildSurfaceControl = new SurfaceControl.Builder(session) 328 .setName(mTitle + " - task-snapshot-surface") 329 .setBufferSize(buffer.getWidth(), buffer.getHeight()) 330 .setFormat(buffer.getFormat()) 331 .setParent(mSurfaceControl) 332 .build(); 333 Surface surface = new Surface(); 334 surface.copyFrom(mChildSurfaceControl); 335 336 final Rect frame; 337 SurfaceControl.openTransaction(); 338 try { 339 // We can just show the surface here as it will still be hidden as the parent is 340 // still hidden. 341 mChildSurfaceControl.show(); 342 if (aspectRatioMismatch) { 343 // Clip off ugly navigation bar. 344 final Rect crop = calculateSnapshotCrop(); 345 frame = calculateSnapshotFrame(crop); 346 mChildSurfaceControl.setWindowCrop(crop); 347 mChildSurfaceControl.setPosition(frame.left, frame.top); 348 } else { 349 frame = null; 350 } 351 352 // Scale the mismatch dimensions to fill the task bounds 353 final float scale = 1 / mSnapshot.getScale(); 354 mChildSurfaceControl.setMatrix(scale, 0, 0, scale); 355 } finally { 356 SurfaceControl.closeTransaction(); 357 } 358 surface.attachAndQueueBuffer(buffer); 359 surface.release(); 360 361 if (aspectRatioMismatch) { 362 final Canvas c = mSurface.lockCanvas(null); 363 drawBackgroundAndBars(c, frame); 364 mSurface.unlockCanvasAndPost(c); 365 mSurface.release(); 366 } 367 } 368 369 /** 370 * Calculates the snapshot crop in snapshot coordinate space. 371 * 372 * @return crop rect in snapshot coordinate space. 373 */ 374 @VisibleForTesting calculateSnapshotCrop()375 Rect calculateSnapshotCrop() { 376 final Rect rect = new Rect(); 377 rect.set(0, 0, mSnapshot.getSnapshot().getWidth(), mSnapshot.getSnapshot().getHeight()); 378 final Rect insets = mSnapshot.getContentInsets(); 379 380 // Let's remove all system decorations except the status bar, but only if the task is at the 381 // very top of the screen. 382 final boolean isTop = mTaskBounds.top == 0 && mFrame.top == 0; 383 rect.inset((int) (insets.left * mSnapshot.getScale()), 384 isTop ? 0 : (int) (insets.top * mSnapshot.getScale()), 385 (int) (insets.right * mSnapshot.getScale()), 386 (int) (insets.bottom * mSnapshot.getScale())); 387 return rect; 388 } 389 390 /** 391 * Calculates the snapshot frame in window coordinate space from crop. 392 * 393 * @param crop rect that is in snapshot coordinate space. 394 */ 395 @VisibleForTesting calculateSnapshotFrame(Rect crop)396 Rect calculateSnapshotFrame(Rect crop) { 397 final Rect frame = new Rect(crop); 398 final float scale = mSnapshot.getScale(); 399 400 // Rescale the frame from snapshot to window coordinate space 401 frame.scale(1 / scale); 402 403 // By default, offset it to to top/left corner 404 frame.offsetTo((int) (-crop.left / scale), (int) (-crop.top / scale)); 405 406 // However, we also need to make space for the navigation bar on the left side. 407 final int colorViewLeftInset = getColorViewLeftInset(mStableInsets.left, 408 mContentInsets.left); 409 frame.offset(colorViewLeftInset, 0); 410 return frame; 411 } 412 413 @VisibleForTesting drawBackgroundAndBars(Canvas c, Rect frame)414 void drawBackgroundAndBars(Canvas c, Rect frame) { 415 final int statusBarHeight = mSystemBarBackgroundPainter.getStatusBarColorViewHeight(); 416 final boolean fillHorizontally = c.getWidth() > frame.right; 417 final boolean fillVertically = c.getHeight() > frame.bottom; 418 if (fillHorizontally) { 419 c.drawRect(frame.right, alpha(mStatusBarColor) == 0xFF ? statusBarHeight : 0, 420 c.getWidth(), fillVertically 421 ? frame.bottom 422 : c.getHeight(), 423 mBackgroundPaint); 424 } 425 if (fillVertically) { 426 c.drawRect(0, frame.bottom, c.getWidth(), c.getHeight(), mBackgroundPaint); 427 } 428 mSystemBarBackgroundPainter.drawDecors(c, frame); 429 } 430 reportDrawn()431 private void reportDrawn() { 432 try { 433 mSession.finishDrawing(mWindow); 434 } catch (RemoteException e) { 435 // Local call. 436 } 437 } 438 439 private static Handler sHandler = new Handler(Looper.getMainLooper()) { 440 441 @Override 442 public void handleMessage(Message msg) { 443 switch (msg.what) { 444 case MSG_REPORT_DRAW: 445 final boolean hasDrawn; 446 final TaskSnapshotSurface surface = (TaskSnapshotSurface) msg.obj; 447 synchronized (surface.mService.mGlobalLock) { 448 hasDrawn = surface.mHasDrawn; 449 } 450 if (hasDrawn) { 451 surface.reportDrawn(); 452 } 453 break; 454 } 455 } 456 }; 457 458 @VisibleForTesting 459 static class Window extends BaseIWindow { 460 461 private TaskSnapshotSurface mOuter; 462 setOuter(TaskSnapshotSurface outer)463 public void setOuter(TaskSnapshotSurface outer) { 464 mOuter = outer; 465 } 466 467 @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)468 public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets, 469 Rect stableInsets, Rect outsets, boolean reportDraw, 470 MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout, 471 boolean alwaysConsumeSystemBars, int displayId, 472 DisplayCutout.ParcelableWrapper displayCutout) { 473 if (mergedConfiguration != null && mOuter != null 474 && mOuter.mOrientationOnCreation 475 != mergedConfiguration.getMergedConfiguration().orientation) { 476 477 // The orientation of the screen is changing. We better remove the snapshot ASAP as 478 // we are going to wait on the new window in any case to unfreeze the screen, and 479 // the starting window is not needed anymore. 480 sHandler.post(mOuter::remove); 481 } 482 if (reportDraw) { 483 sHandler.obtainMessage(MSG_REPORT_DRAW, mOuter).sendToTarget(); 484 } 485 } 486 } 487 488 /** 489 * Helper class to draw the background of the system bars in regions the task snapshot isn't 490 * filling the window. 491 */ 492 static class SystemBarBackgroundPainter { 493 494 private final Rect mContentInsets = new Rect(); 495 private final Rect mStableInsets = new Rect(); 496 private final Paint mStatusBarPaint = new Paint(); 497 private final Paint mNavigationBarPaint = new Paint(); 498 private final int mStatusBarColor; 499 private final int mNavigationBarColor; 500 private final int mWindowFlags; 501 private final int mWindowPrivateFlags; 502 private final int mSysUiVis; 503 private final float mScale; 504 SystemBarBackgroundPainter(int windowFlags, int windowPrivateFlags, int sysUiVis, TaskDescription taskDescription, float scale)505 SystemBarBackgroundPainter(int windowFlags, int windowPrivateFlags, int sysUiVis, 506 TaskDescription taskDescription, float scale) { 507 mWindowFlags = windowFlags; 508 mWindowPrivateFlags = windowPrivateFlags; 509 mSysUiVis = sysUiVis; 510 mScale = scale; 511 final Context context = ActivityThread.currentActivityThread().getSystemUiContext(); 512 final int semiTransparent = context.getColor( 513 R.color.system_bar_background_semi_transparent); 514 mStatusBarColor = DecorView.calculateBarColor(windowFlags, FLAG_TRANSLUCENT_STATUS, 515 semiTransparent, taskDescription.getStatusBarColor(), sysUiVis, 516 SYSTEM_UI_FLAG_LIGHT_STATUS_BAR, 517 taskDescription.getEnsureStatusBarContrastWhenTransparent()); 518 mNavigationBarColor = DecorView.calculateBarColor(windowFlags, 519 FLAG_TRANSLUCENT_NAVIGATION, semiTransparent, 520 taskDescription.getNavigationBarColor(), sysUiVis, 521 SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, 522 taskDescription.getEnsureNavigationBarContrastWhenTransparent() 523 && context.getResources().getBoolean(R.bool.config_navBarNeedsScrim)); 524 mStatusBarPaint.setColor(mStatusBarColor); 525 mNavigationBarPaint.setColor(mNavigationBarColor); 526 } 527 setInsets(Rect contentInsets, Rect stableInsets)528 void setInsets(Rect contentInsets, Rect stableInsets) { 529 mContentInsets.set(contentInsets); 530 mStableInsets.set(stableInsets); 531 } 532 getStatusBarColorViewHeight()533 int getStatusBarColorViewHeight() { 534 final boolean forceBarBackground = 535 (mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0; 536 if (STATUS_BAR_COLOR_VIEW_ATTRIBUTES.isVisible( 537 mSysUiVis, mStatusBarColor, mWindowFlags, forceBarBackground)) { 538 return (int) (getColorViewTopInset(mStableInsets.top, mContentInsets.top) * mScale); 539 } else { 540 return 0; 541 } 542 } 543 isNavigationBarColorViewVisible()544 private boolean isNavigationBarColorViewVisible() { 545 final boolean forceBarBackground = 546 (mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0; 547 return NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES.isVisible( 548 mSysUiVis, mNavigationBarColor, mWindowFlags, forceBarBackground); 549 } 550 drawDecors(Canvas c, @Nullable Rect alreadyDrawnFrame)551 void drawDecors(Canvas c, @Nullable Rect alreadyDrawnFrame) { 552 drawStatusBarBackground(c, alreadyDrawnFrame, getStatusBarColorViewHeight()); 553 drawNavigationBarBackground(c); 554 } 555 556 @VisibleForTesting drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame, int statusBarHeight)557 void drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame, 558 int statusBarHeight) { 559 if (statusBarHeight > 0 && Color.alpha(mStatusBarColor) != 0 560 && (alreadyDrawnFrame == null || c.getWidth() > alreadyDrawnFrame.right)) { 561 final int rightInset = (int) (DecorView.getColorViewRightInset(mStableInsets.right, 562 mContentInsets.right) * mScale); 563 final int left = alreadyDrawnFrame != null ? alreadyDrawnFrame.right : 0; 564 c.drawRect(left, 0, c.getWidth() - rightInset, statusBarHeight, mStatusBarPaint); 565 } 566 } 567 568 @VisibleForTesting drawNavigationBarBackground(Canvas c)569 void drawNavigationBarBackground(Canvas c) { 570 final Rect navigationBarRect = new Rect(); 571 getNavigationBarRect(c.getWidth(), c.getHeight(), mStableInsets, mContentInsets, 572 navigationBarRect, mScale); 573 final boolean visible = isNavigationBarColorViewVisible(); 574 if (visible && Color.alpha(mNavigationBarColor) != 0 && !navigationBarRect.isEmpty()) { 575 c.drawRect(navigationBarRect, mNavigationBarPaint); 576 } 577 } 578 } 579 } 580