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