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