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 com.android.wm.shell.back;
18 
19 import static android.view.Display.DEFAULT_DISPLAY;
20 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
21 
22 import android.annotation.NonNull;
23 import android.graphics.Color;
24 import android.graphics.Rect;
25 import android.view.SurfaceControl;
26 
27 import com.android.internal.graphics.ColorUtils;
28 import com.android.internal.view.AppearanceRegion;
29 import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
30 
31 /**
32  * Controls background surface for the back animations
33  */
34 public class BackAnimationBackground {
35     private static final int BACKGROUND_LAYER = -1;
36 
37     private static final int NO_APPEARANCE = 0;
38 
39     private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
40     private SurfaceControl mBackgroundSurface;
41 
42     private StatusBarCustomizer mCustomizer;
43     private boolean mIsRequestingStatusBarAppearance;
44     private boolean mBackgroundIsDark;
45     private Rect mStartBounds;
46     private int mStatusbarHeight;
47 
BackAnimationBackground(RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer)48     public BackAnimationBackground(RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
49         mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
50     }
51 
52     /**
53      * Ensures the back animation background color layer is present.
54      *
55      * @param startRect The start bounds of the closing target.
56      * @param color The background color.
57      * @param transaction The animation transaction.
58      * @param statusbarHeight The height of the statusbar (in px).
59      */
ensureBackground(Rect startRect, int color, @NonNull SurfaceControl.Transaction transaction, int statusbarHeight)60     public void ensureBackground(Rect startRect, int color,
61             @NonNull SurfaceControl.Transaction transaction, int statusbarHeight) {
62         if (mBackgroundSurface != null) {
63             return;
64         }
65 
66         mBackgroundIsDark = ColorUtils.calculateLuminance(color) < 0.5f;
67 
68         final float[] colorComponents = new float[] { Color.red(color) / 255.f,
69                 Color.green(color) / 255.f, Color.blue(color) / 255.f };
70 
71         final SurfaceControl.Builder colorLayerBuilder = new SurfaceControl.Builder()
72                 .setName("back-animation-background")
73                 .setCallsite("BackAnimationBackground")
74                 .setColorLayer();
75 
76         mRootTaskDisplayAreaOrganizer.attachToDisplayArea(DEFAULT_DISPLAY, colorLayerBuilder);
77         mBackgroundSurface = colorLayerBuilder.build();
78         transaction.setColor(mBackgroundSurface, colorComponents)
79                 .setLayer(mBackgroundSurface, BACKGROUND_LAYER)
80                 .show(mBackgroundSurface);
81         mStartBounds = startRect;
82         mIsRequestingStatusBarAppearance = false;
83         mStatusbarHeight = statusbarHeight;
84     }
85 
86     /**
87      * Remove the back animation background.
88      *
89      * @param transaction The animation transaction.
90      */
91     public void removeBackground(@NonNull SurfaceControl.Transaction transaction) {
92         if (mBackgroundSurface == null) {
93             return;
94         }
95 
96         if (mBackgroundSurface.isValid()) {
97             transaction.remove(mBackgroundSurface);
98         }
99         mBackgroundSurface = null;
100         mIsRequestingStatusBarAppearance = false;
101     }
102 
103     /**
104      * Attach a {@link StatusBarCustomizer} instance to allow status bar animate with back progress.
105      *
106      * @param customizer The {@link StatusBarCustomizer} to be used.
107      */
108     void setStatusBarCustomizer(StatusBarCustomizer customizer) {
109         mCustomizer = customizer;
110     }
111 
112     /**
113      * Update back animation background with for the progress.
114      *
115      * @param top The top coordinate of the closing target
116      */
117     public void customizeStatusBarAppearance(int top) {
118         if (mCustomizer == null || mStartBounds.isEmpty()) {
119             return;
120         }
121 
122         final boolean shouldCustomizeSystemBar = top > mStatusbarHeight / 2;
123         if (shouldCustomizeSystemBar == mIsRequestingStatusBarAppearance) {
124             return;
125         }
126 
127         mIsRequestingStatusBarAppearance = shouldCustomizeSystemBar;
128         if (mIsRequestingStatusBarAppearance) {
129             final AppearanceRegion region = new AppearanceRegion(!mBackgroundIsDark
130                     ? APPEARANCE_LIGHT_STATUS_BARS : NO_APPEARANCE,
131                     mStartBounds);
132             mCustomizer.customizeStatusBarAppearance(region);
133         } else {
134             resetStatusBarCustomization();
135         }
136     }
137 
138     /**
139      * Resets the statusbar customization
140      */
resetStatusBarCustomization()141     public void resetStatusBarCustomization() {
142         mCustomizer.customizeStatusBarAppearance(null);
143     }
144 }
145