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