1 /* 2 * Copyright (C) 2022 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 android.window; 18 19 import static android.graphics.Color.WHITE; 20 import static android.graphics.Color.alpha; 21 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS; 22 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; 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.INPUT_FEATURE_NO_INPUT_CHANNEL; 38 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS; 39 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; 40 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST; 41 42 import static com.android.internal.policy.DecorView.NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES; 43 import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES; 44 import static com.android.internal.policy.DecorView.getNavigationBarRect; 45 46 import android.annotation.NonNull; 47 import android.annotation.Nullable; 48 import android.app.ActivityManager; 49 import android.app.ActivityThread; 50 import android.content.Context; 51 import android.graphics.Canvas; 52 import android.graphics.Color; 53 import android.graphics.GraphicBuffer; 54 import android.graphics.Matrix; 55 import android.graphics.Paint; 56 import android.graphics.PixelFormat; 57 import android.graphics.Rect; 58 import android.graphics.RectF; 59 import android.hardware.HardwareBuffer; 60 import android.os.IBinder; 61 import android.util.Log; 62 import android.view.InsetsState; 63 import android.view.SurfaceControl; 64 import android.view.SurfaceSession; 65 import android.view.ViewGroup; 66 import android.view.WindowInsets; 67 import android.view.WindowManager; 68 69 import com.android.internal.R; 70 import com.android.internal.annotations.VisibleForTesting; 71 import com.android.internal.policy.DecorView; 72 73 /** 74 * Utils class to help draw a snapshot on a surface. 75 * @hide 76 */ 77 public class SnapshotDrawerUtils { 78 private static final String TAG = "SnapshotDrawerUtils"; 79 80 /** 81 * When creating the starting window, we use the exact same layout flags such that we end up 82 * with a window with the exact same dimensions etc. However, these flags are not used in layout 83 * and might cause other side effects so we exclude them. 84 */ 85 static final int FLAG_INHERIT_EXCLUDES = FLAG_NOT_FOCUSABLE 86 | FLAG_NOT_TOUCHABLE 87 | FLAG_NOT_TOUCH_MODAL 88 | FLAG_ALT_FOCUSABLE_IM 89 | FLAG_NOT_FOCUSABLE 90 | FLAG_HARDWARE_ACCELERATED 91 | FLAG_IGNORE_CHEEK_PRESSES 92 | FLAG_LOCAL_FOCUS_MODE 93 | FLAG_SLIPPERY 94 | FLAG_WATCH_OUTSIDE_TOUCH 95 | FLAG_SPLIT_TOUCH 96 | FLAG_SCALED 97 | FLAG_SECURE; 98 99 private static final RectF sTmpSnapshotSize = new RectF(); 100 private static final RectF sTmpDstFrame = new RectF(); 101 102 private static final Matrix sSnapshotMatrix = new Matrix(); 103 private static final float[] sTmpFloat9 = new float[9]; 104 private static final Paint sBackgroundPaint = new Paint(); 105 106 /** 107 * The internal object to hold the surface and drawing on it. 108 */ 109 @VisibleForTesting 110 public static class SnapshotSurface { 111 private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction(); 112 private final SurfaceControl mRootSurface; 113 private final TaskSnapshot mSnapshot; 114 private final CharSequence mTitle; 115 116 private SystemBarBackgroundPainter mSystemBarBackgroundPainter; 117 private final Rect mTaskBounds; 118 private final Rect mFrame = new Rect(); 119 private final Rect mSystemBarInsets = new Rect(); 120 private boolean mSizeMismatch; 121 SnapshotSurface(SurfaceControl rootSurface, TaskSnapshot snapshot, CharSequence title, Rect taskBounds)122 public SnapshotSurface(SurfaceControl rootSurface, TaskSnapshot snapshot, 123 CharSequence title, 124 Rect taskBounds) { 125 mRootSurface = rootSurface; 126 mSnapshot = snapshot; 127 mTitle = title; 128 mTaskBounds = taskBounds; 129 } 130 131 /** 132 * Initiate system bar painter to draw the system bar background. 133 */ initiateSystemBarPainter(int windowFlags, int windowPrivateFlags, int appearance, ActivityManager.TaskDescription taskDescription, @WindowInsets.Type.InsetsType int requestedVisibleTypes)134 void initiateSystemBarPainter(int windowFlags, int windowPrivateFlags, 135 int appearance, ActivityManager.TaskDescription taskDescription, 136 @WindowInsets.Type.InsetsType int requestedVisibleTypes) { 137 mSystemBarBackgroundPainter = new SystemBarBackgroundPainter(windowFlags, 138 windowPrivateFlags, appearance, taskDescription, 1f, requestedVisibleTypes); 139 int backgroundColor = taskDescription.getBackgroundColor(); 140 sBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE); 141 } 142 143 /** 144 * Set frame size. 145 */ setFrames(Rect frame, Rect systemBarInsets)146 void setFrames(Rect frame, Rect systemBarInsets) { 147 mFrame.set(frame); 148 mSystemBarInsets.set(systemBarInsets); 149 final HardwareBuffer snapshot = mSnapshot.getHardwareBuffer(); 150 mSizeMismatch = (mFrame.width() != snapshot.getWidth() 151 || mFrame.height() != snapshot.getHeight()); 152 mSystemBarBackgroundPainter.setInsets(systemBarInsets); 153 } 154 drawSnapshot(boolean releaseAfterDraw)155 private void drawSnapshot(boolean releaseAfterDraw) { 156 Log.v(TAG, "Drawing snapshot surface sizeMismatch=" + mSizeMismatch); 157 if (mSizeMismatch) { 158 // The dimensions of the buffer and the window don't match, so attaching the buffer 159 // will fail. Better create a child window with the exact dimensions and fill the 160 // parent window with the background color! 161 drawSizeMismatchSnapshot(); 162 } else { 163 drawSizeMatchSnapshot(); 164 } 165 166 // In case window manager leaks us, make sure we don't retain the snapshot. 167 if (mSnapshot.getHardwareBuffer() != null) { 168 mSnapshot.getHardwareBuffer().close(); 169 } 170 if (releaseAfterDraw) { 171 mRootSurface.release(); 172 } 173 } 174 drawSizeMatchSnapshot()175 private void drawSizeMatchSnapshot() { 176 mTransaction.setBuffer(mRootSurface, mSnapshot.getHardwareBuffer()) 177 .setColorSpace(mRootSurface, mSnapshot.getColorSpace()) 178 .apply(); 179 } 180 drawSizeMismatchSnapshot()181 private void drawSizeMismatchSnapshot() { 182 final HardwareBuffer buffer = mSnapshot.getHardwareBuffer(); 183 final SurfaceSession session = new SurfaceSession(); 184 185 // We consider nearly matched dimensions as there can be rounding errors and the user 186 // won't notice very minute differences from scaling one dimension more than the other 187 boolean aspectRatioMismatch = !isAspectRatioMatch(mFrame, mSnapshot); 188 189 // Keep a reference to it such that it doesn't get destroyed when finalized. 190 SurfaceControl childSurfaceControl = new SurfaceControl.Builder(session) 191 .setName(mTitle + " - task-snapshot-surface") 192 .setBLASTLayer() 193 .setFormat(buffer.getFormat()) 194 .setParent(mRootSurface) 195 .setCallsite("TaskSnapshotWindow.drawSizeMismatchSnapshot") 196 .build(); 197 198 final Rect frame; 199 // We can just show the surface here as it will still be hidden as the parent is 200 // still hidden. 201 mTransaction.show(childSurfaceControl); 202 if (aspectRatioMismatch) { 203 Rect crop = null; 204 final Rect letterboxInsets = mSnapshot.getLetterboxInsets(); 205 if (letterboxInsets.left != 0 || letterboxInsets.top != 0 206 || letterboxInsets.right != 0 || letterboxInsets.bottom != 0) { 207 // Clip off letterbox. 208 crop = calculateSnapshotCrop(letterboxInsets); 209 // If the snapshot can cover the frame, then no need to draw background. 210 aspectRatioMismatch = !isAspectRatioMatch(mFrame, crop); 211 } 212 // if letterbox doesn't match window frame, try crop by content insets 213 if (aspectRatioMismatch) { 214 // Clip off ugly navigation bar. 215 crop = calculateSnapshotCrop(mSnapshot.getContentInsets()); 216 } 217 frame = calculateSnapshotFrame(crop); 218 mTransaction.setWindowCrop(childSurfaceControl, crop); 219 mTransaction.setPosition(childSurfaceControl, frame.left, frame.top); 220 sTmpSnapshotSize.set(crop); 221 sTmpDstFrame.set(frame); 222 } else { 223 frame = null; 224 sTmpSnapshotSize.set(0, 0, buffer.getWidth(), buffer.getHeight()); 225 sTmpDstFrame.set(mFrame); 226 sTmpDstFrame.offsetTo(0, 0); 227 } 228 229 // Scale the mismatch dimensions to fill the task bounds 230 sSnapshotMatrix.setRectToRect(sTmpSnapshotSize, sTmpDstFrame, Matrix.ScaleToFit.FILL); 231 mTransaction.setMatrix(childSurfaceControl, sSnapshotMatrix, sTmpFloat9); 232 mTransaction.setColorSpace(childSurfaceControl, mSnapshot.getColorSpace()); 233 mTransaction.setBuffer(childSurfaceControl, mSnapshot.getHardwareBuffer()); 234 235 if (aspectRatioMismatch) { 236 GraphicBuffer background = GraphicBuffer.create(mFrame.width(), mFrame.height(), 237 PixelFormat.RGBA_8888, 238 GraphicBuffer.USAGE_HW_TEXTURE | GraphicBuffer.USAGE_HW_COMPOSER 239 | GraphicBuffer.USAGE_SW_WRITE_RARELY); 240 if (background == null) { 241 Log.e(TAG, "Unable to draw snapshot: failed to allocate graphic buffer for " 242 + mTitle); 243 return; 244 } 245 // TODO: Support this on HardwareBuffer 246 final Canvas c = background.lockCanvas(); 247 drawBackgroundAndBars(c, frame); 248 background.unlockCanvasAndPost(c); 249 mTransaction.setBuffer(mRootSurface, 250 HardwareBuffer.createFromGraphicBuffer(background)); 251 } 252 mTransaction.apply(); 253 childSurfaceControl.release(); 254 } 255 256 /** 257 * Calculates the snapshot crop in snapshot coordinate space. 258 * @param insets Content insets or Letterbox insets 259 * @return crop rect in snapshot coordinate space. 260 */ calculateSnapshotCrop(@onNull Rect insets)261 Rect calculateSnapshotCrop(@NonNull Rect insets) { 262 final Rect rect = new Rect(); 263 final HardwareBuffer snapshot = mSnapshot.getHardwareBuffer(); 264 rect.set(0, 0, snapshot.getWidth(), snapshot.getHeight()); 265 266 final float scaleX = (float) snapshot.getWidth() / mSnapshot.getTaskSize().x; 267 final float scaleY = (float) snapshot.getHeight() / mSnapshot.getTaskSize().y; 268 269 // Let's remove all system decorations except the status bar, but only if the task is at 270 // the very top of the screen. 271 final boolean isTop = mTaskBounds.top == 0 && mFrame.top == 0; 272 rect.inset((int) (insets.left * scaleX), 273 isTop ? 0 : (int) (insets.top * scaleY), 274 (int) (insets.right * scaleX), 275 (int) (insets.bottom * scaleY)); 276 return rect; 277 } 278 279 /** 280 * Calculates the snapshot frame in window coordinate space from crop. 281 * 282 * @param crop rect that is in snapshot coordinate space. 283 */ calculateSnapshotFrame(Rect crop)284 Rect calculateSnapshotFrame(Rect crop) { 285 final HardwareBuffer snapshot = mSnapshot.getHardwareBuffer(); 286 final float scaleX = (float) snapshot.getWidth() / mSnapshot.getTaskSize().x; 287 final float scaleY = (float) snapshot.getHeight() / mSnapshot.getTaskSize().y; 288 289 // Rescale the frame from snapshot to window coordinate space 290 final Rect frame = new Rect(0, 0, 291 (int) (crop.width() / scaleX + 0.5f), 292 (int) (crop.height() / scaleY + 0.5f) 293 ); 294 295 // However, we also need to make space for the navigation bar on the left side. 296 frame.offset(mSystemBarInsets.left, 0); 297 return frame; 298 } 299 300 /** 301 * Draw status bar and navigation bar background. 302 */ drawBackgroundAndBars(Canvas c, Rect frame)303 void drawBackgroundAndBars(Canvas c, Rect frame) { 304 final int statusBarHeight = mSystemBarBackgroundPainter.getStatusBarColorViewHeight(); 305 final boolean fillHorizontally = c.getWidth() > frame.right; 306 final boolean fillVertically = c.getHeight() > frame.bottom; 307 if (fillHorizontally) { 308 c.drawRect(frame.right, alpha(mSystemBarBackgroundPainter.mStatusBarColor) == 0xFF 309 ? statusBarHeight : 0, c.getWidth(), fillVertically 310 ? frame.bottom : c.getHeight(), sBackgroundPaint); 311 } 312 if (fillVertically) { 313 c.drawRect(0, frame.bottom, c.getWidth(), c.getHeight(), sBackgroundPaint); 314 } 315 mSystemBarBackgroundPainter.drawDecors(c, frame); 316 } 317 318 /** 319 * Ask system bar background painter to draw status bar background. 320 * 321 */ drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame)322 void drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame) { 323 mSystemBarBackgroundPainter.drawStatusBarBackground(c, alreadyDrawnFrame, 324 mSystemBarBackgroundPainter.getStatusBarColorViewHeight()); 325 } 326 327 /** 328 * Ask system bar background painter to draw navigation bar background. 329 * 330 */ drawNavigationBarBackground(Canvas c)331 void drawNavigationBarBackground(Canvas c) { 332 mSystemBarBackgroundPainter.drawNavigationBarBackground(c); 333 } 334 } 335 336 /** 337 * @return true if the aspect ratio match between a frame and a snapshot buffer. 338 */ isAspectRatioMatch(Rect frame, TaskSnapshot snapshot)339 public static boolean isAspectRatioMatch(Rect frame, TaskSnapshot snapshot) { 340 if (frame.isEmpty()) { 341 return false; 342 } 343 final HardwareBuffer buffer = snapshot.getHardwareBuffer(); 344 return Math.abs( 345 ((float) buffer.getWidth() / buffer.getHeight()) 346 - ((float) frame.width() / frame.height())) <= 0.01f; 347 } 348 isAspectRatioMatch(Rect frame1, Rect frame2)349 private static boolean isAspectRatioMatch(Rect frame1, Rect frame2) { 350 if (frame1.isEmpty() || frame2.isEmpty()) { 351 return false; 352 } 353 return Math.abs( 354 ((float) frame2.width() / frame2.height()) 355 - ((float) frame1.width() / frame1.height())) <= 0.01f; 356 } 357 358 /** 359 * Get or create a TaskDescription from a RunningTaskInfo. 360 */ getOrCreateTaskDescription( ActivityManager.RunningTaskInfo runningTaskInfo)361 public static ActivityManager.TaskDescription getOrCreateTaskDescription( 362 ActivityManager.RunningTaskInfo runningTaskInfo) { 363 final ActivityManager.TaskDescription taskDescription; 364 if (runningTaskInfo.taskDescription != null) { 365 taskDescription = runningTaskInfo.taskDescription; 366 } else { 367 taskDescription = new ActivityManager.TaskDescription(); 368 taskDescription.setBackgroundColor(WHITE); 369 } 370 return taskDescription; 371 } 372 373 /** 374 * Help method to draw the snapshot on a surface. 375 */ drawSnapshotOnSurface(StartingWindowInfo info, WindowManager.LayoutParams lp, SurfaceControl rootSurface, TaskSnapshot snapshot, Rect configBounds, Rect windowBounds, InsetsState topWindowInsetsState, boolean releaseAfterDraw)376 public static void drawSnapshotOnSurface(StartingWindowInfo info, WindowManager.LayoutParams lp, 377 SurfaceControl rootSurface, TaskSnapshot snapshot, 378 Rect configBounds, Rect windowBounds, InsetsState topWindowInsetsState, 379 boolean releaseAfterDraw) { 380 if (windowBounds.isEmpty()) { 381 Log.e(TAG, "Unable to draw snapshot on an empty windowBounds"); 382 return; 383 } 384 final SnapshotSurface drawSurface = new SnapshotSurface( 385 rootSurface, snapshot, lp.getTitle(), configBounds); 386 387 final WindowManager.LayoutParams attrs = info.topOpaqueWindowLayoutParams; 388 final ActivityManager.RunningTaskInfo runningTaskInfo = info.taskInfo; 389 final ActivityManager.TaskDescription taskDescription = 390 getOrCreateTaskDescription(runningTaskInfo); 391 drawSurface.initiateSystemBarPainter(lp.flags, lp.privateFlags, 392 attrs.insetsFlags.appearance, taskDescription, info.requestedVisibleTypes); 393 final Rect systemBarInsets = getSystemBarInsets(windowBounds, topWindowInsetsState); 394 drawSurface.setFrames(windowBounds, systemBarInsets); 395 drawSurface.drawSnapshot(releaseAfterDraw); 396 } 397 398 /** 399 * Help method to create a layout parameters for a window. 400 */ createLayoutParameters(StartingWindowInfo info, CharSequence title, @WindowManager.LayoutParams.WindowType int windowType, int pixelFormat, IBinder token)401 public static WindowManager.LayoutParams createLayoutParameters(StartingWindowInfo info, 402 CharSequence title, @WindowManager.LayoutParams.WindowType int windowType, 403 int pixelFormat, IBinder token) { 404 final WindowManager.LayoutParams attrs = info.topOpaqueWindowLayoutParams; 405 final WindowManager.LayoutParams mainWindowParams = info.mainWindowLayoutParams; 406 final InsetsState topWindowInsetsState = info.topOpaqueWindowInsetsState; 407 if (attrs == null || mainWindowParams == null || topWindowInsetsState == null) { 408 Log.w(TAG, "unable to create taskSnapshot surface "); 409 return null; 410 } 411 final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(); 412 413 final int appearance = attrs.insetsFlags.appearance; 414 final int windowFlags = attrs.flags; 415 final int windowPrivateFlags = attrs.privateFlags; 416 417 layoutParams.packageName = mainWindowParams.packageName; 418 layoutParams.windowAnimations = mainWindowParams.windowAnimations; 419 layoutParams.dimAmount = mainWindowParams.dimAmount; 420 layoutParams.type = windowType; 421 layoutParams.format = pixelFormat; 422 layoutParams.flags = (windowFlags & ~FLAG_INHERIT_EXCLUDES) 423 | FLAG_NOT_FOCUSABLE 424 | FLAG_NOT_TOUCHABLE; 425 // Setting as trusted overlay to let touches pass through. This is safe because this 426 // window is controlled by the system. 427 layoutParams.privateFlags = (windowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) 428 | PRIVATE_FLAG_TRUSTED_OVERLAY | PRIVATE_FLAG_USE_BLAST; 429 layoutParams.token = token; 430 layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT; 431 layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT; 432 layoutParams.insetsFlags.appearance = appearance; 433 layoutParams.insetsFlags.behavior = attrs.insetsFlags.behavior; 434 layoutParams.layoutInDisplayCutoutMode = attrs.layoutInDisplayCutoutMode; 435 layoutParams.setFitInsetsTypes(attrs.getFitInsetsTypes()); 436 layoutParams.setFitInsetsSides(attrs.getFitInsetsSides()); 437 layoutParams.setFitInsetsIgnoringVisibility(attrs.isFitInsetsIgnoringVisibility()); 438 439 layoutParams.setTitle(title); 440 layoutParams.inputFeatures |= INPUT_FEATURE_NO_INPUT_CHANNEL; 441 return layoutParams; 442 } 443 getSystemBarInsets(Rect frame, InsetsState state)444 static Rect getSystemBarInsets(Rect frame, InsetsState state) { 445 return state.calculateInsets(frame, WindowInsets.Type.systemBars(), 446 false /* ignoreVisibility */).toRect(); 447 } 448 449 /** 450 * Helper class to draw the background of the system bars in regions the task snapshot isn't 451 * filling the window. 452 */ 453 public static class SystemBarBackgroundPainter { 454 private final Paint mStatusBarPaint = new Paint(); 455 private final Paint mNavigationBarPaint = new Paint(); 456 private final int mStatusBarColor; 457 private final int mNavigationBarColor; 458 private final int mWindowFlags; 459 private final int mWindowPrivateFlags; 460 private final float mScale; 461 private final @WindowInsets.Type.InsetsType int mRequestedVisibleTypes; 462 private final Rect mSystemBarInsets = new Rect(); 463 SystemBarBackgroundPainter(int windowFlags, int windowPrivateFlags, int appearance, ActivityManager.TaskDescription taskDescription, float scale, @WindowInsets.Type.InsetsType int requestedVisibleTypes)464 public SystemBarBackgroundPainter(int windowFlags, int windowPrivateFlags, int appearance, 465 ActivityManager.TaskDescription taskDescription, float scale, 466 @WindowInsets.Type.InsetsType int requestedVisibleTypes) { 467 mWindowFlags = windowFlags; 468 mWindowPrivateFlags = windowPrivateFlags; 469 mScale = scale; 470 final Context context = ActivityThread.currentActivityThread().getSystemUiContext(); 471 final int semiTransparent = context.getColor( 472 R.color.system_bar_background_semi_transparent); 473 mStatusBarColor = DecorView.calculateBarColor(windowFlags, FLAG_TRANSLUCENT_STATUS, 474 semiTransparent, taskDescription.getStatusBarColor(), appearance, 475 APPEARANCE_LIGHT_STATUS_BARS, 476 taskDescription.getEnsureStatusBarContrastWhenTransparent()); 477 mNavigationBarColor = DecorView.calculateBarColor(windowFlags, 478 FLAG_TRANSLUCENT_NAVIGATION, semiTransparent, 479 taskDescription.getNavigationBarColor(), appearance, 480 APPEARANCE_LIGHT_NAVIGATION_BARS, 481 taskDescription.getEnsureNavigationBarContrastWhenTransparent() 482 && context.getResources().getBoolean( 483 R.bool.config_navBarNeedsScrim)); 484 mStatusBarPaint.setColor(mStatusBarColor); 485 mNavigationBarPaint.setColor(mNavigationBarColor); 486 mRequestedVisibleTypes = requestedVisibleTypes; 487 } 488 489 /** 490 * Set system bar insets. 491 */ setInsets(Rect systemBarInsets)492 public void setInsets(Rect systemBarInsets) { 493 mSystemBarInsets.set(systemBarInsets); 494 } 495 getStatusBarColorViewHeight()496 int getStatusBarColorViewHeight() { 497 final boolean forceBarBackground = 498 (mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0; 499 if (STATUS_BAR_COLOR_VIEW_ATTRIBUTES.isVisible( 500 mRequestedVisibleTypes, mStatusBarColor, mWindowFlags, 501 forceBarBackground)) { 502 return (int) (mSystemBarInsets.top * mScale); 503 } else { 504 return 0; 505 } 506 } 507 isNavigationBarColorViewVisible()508 private boolean isNavigationBarColorViewVisible() { 509 final boolean forceBarBackground = 510 (mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0; 511 return NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES.isVisible( 512 mRequestedVisibleTypes, mNavigationBarColor, mWindowFlags, 513 forceBarBackground); 514 } 515 516 /** 517 * Draw bar colors to a canvas. 518 */ drawDecors(Canvas c, @Nullable Rect alreadyDrawnFrame)519 public void drawDecors(Canvas c, @Nullable Rect alreadyDrawnFrame) { 520 drawStatusBarBackground(c, alreadyDrawnFrame, getStatusBarColorViewHeight()); 521 drawNavigationBarBackground(c); 522 } 523 drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame, int statusBarHeight)524 void drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame, 525 int statusBarHeight) { 526 if (statusBarHeight > 0 && Color.alpha(mStatusBarColor) != 0 527 && (alreadyDrawnFrame == null || c.getWidth() > alreadyDrawnFrame.right)) { 528 final int rightInset = (int) (mSystemBarInsets.right * mScale); 529 final int left = alreadyDrawnFrame != null ? alreadyDrawnFrame.right : 0; 530 c.drawRect(left, 0, c.getWidth() - rightInset, statusBarHeight, 531 mStatusBarPaint); 532 } 533 } 534 drawNavigationBarBackground(Canvas c)535 void drawNavigationBarBackground(Canvas c) { 536 final Rect navigationBarRect = new Rect(); 537 getNavigationBarRect(c.getWidth(), c.getHeight(), mSystemBarInsets, navigationBarRect, 538 mScale); 539 final boolean visible = isNavigationBarColorViewVisible(); 540 if (visible && Color.alpha(mNavigationBarColor) != 0 541 && !navigationBarRect.isEmpty()) { 542 c.drawRect(navigationBarRect, mNavigationBarPaint); 543 } 544 } 545 } 546 } 547