• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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.systemui.statusbar.window;
18 
19 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
20 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
21 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
22 
23 import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE;
24 import static com.android.systemui.util.leak.RotationUtils.ROTATION_NONE;
25 import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE;
26 import static com.android.systemui.util.leak.RotationUtils.ROTATION_UPSIDE_DOWN;
27 
28 import android.content.Context;
29 import android.content.res.Resources;
30 import android.graphics.PixelFormat;
31 import android.graphics.Rect;
32 import android.os.Binder;
33 import android.os.RemoteException;
34 import android.os.Trace;
35 import android.util.Log;
36 import android.view.DisplayCutout;
37 import android.view.Gravity;
38 import android.view.IWindowManager;
39 import android.view.Surface;
40 import android.view.View;
41 import android.view.ViewGroup;
42 import android.view.WindowManager;
43 
44 import com.android.internal.policy.SystemBarUtils;
45 import com.android.systemui.R;
46 import com.android.systemui.animation.ActivityLaunchAnimator;
47 import com.android.systemui.animation.DelegateLaunchAnimatorController;
48 import com.android.systemui.dagger.SysUISingleton;
49 import com.android.systemui.dagger.qualifiers.Main;
50 import com.android.systemui.fragments.FragmentHostManager;
51 import com.android.systemui.fragments.FragmentService;
52 import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider;
53 import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
54 import com.android.systemui.unfold.util.JankMonitorTransitionProgressListener;
55 
56 import java.util.Optional;
57 
58 import javax.inject.Inject;
59 
60 /**
61  * Encapsulates all logic for the status bar window state management.
62  */
63 @SysUISingleton
64 public class StatusBarWindowController {
65     private static final String TAG = "StatusBarWindowController";
66     private static final boolean DEBUG = false;
67 
68     private final Context mContext;
69     private final WindowManager mWindowManager;
70     private final IWindowManager mIWindowManager;
71     private final StatusBarContentInsetsProvider mContentInsetsProvider;
72     private int mBarHeight = -1;
73     private final State mCurrentState = new State();
74     private boolean mIsAttached;
75 
76     private final ViewGroup mStatusBarWindowView;
77     private final FragmentService mFragmentService;
78     // The container in which we should run launch animations started from the status bar and
79     //   expanding into the opening window.
80     private final ViewGroup mLaunchAnimationContainer;
81     private WindowManager.LayoutParams mLp;
82     private final WindowManager.LayoutParams mLpChanged;
83 
84     @Inject
StatusBarWindowController( Context context, @StatusBarWindowModule.InternalWindowView StatusBarWindowView statusBarWindowView, WindowManager windowManager, IWindowManager iWindowManager, StatusBarContentInsetsProvider contentInsetsProvider, FragmentService fragmentService, @Main Resources resources, Optional<UnfoldTransitionProgressProvider> unfoldTransitionProgressProvider)85     public StatusBarWindowController(
86             Context context,
87             @StatusBarWindowModule.InternalWindowView StatusBarWindowView statusBarWindowView,
88             WindowManager windowManager,
89             IWindowManager iWindowManager,
90             StatusBarContentInsetsProvider contentInsetsProvider,
91             FragmentService fragmentService,
92             @Main Resources resources,
93             Optional<UnfoldTransitionProgressProvider> unfoldTransitionProgressProvider) {
94         mContext = context;
95         mWindowManager = windowManager;
96         mIWindowManager = iWindowManager;
97         mContentInsetsProvider = contentInsetsProvider;
98         mStatusBarWindowView = statusBarWindowView;
99         mFragmentService = fragmentService;
100         mLaunchAnimationContainer = mStatusBarWindowView.findViewById(
101                 R.id.status_bar_launch_animation_container);
102         mLpChanged = new WindowManager.LayoutParams();
103 
104         if (mBarHeight < 0) {
105             mBarHeight = SystemBarUtils.getStatusBarHeight(mContext);
106         }
107         unfoldTransitionProgressProvider.ifPresent(
108                 unfoldProgressProvider -> unfoldProgressProvider.addCallback(
109                         new JankMonitorTransitionProgressListener(
110                                 /* attachedViewProvider=*/ () -> mStatusBarWindowView)));
111     }
112 
getStatusBarHeight()113     public int getStatusBarHeight() {
114         return mBarHeight;
115     }
116 
117     /**
118      * Rereads the status bar height and reapplys the current state if the height
119      * is different.
120      */
refreshStatusBarHeight()121     public void refreshStatusBarHeight() {
122         int heightFromConfig = SystemBarUtils.getStatusBarHeight(mContext);
123 
124         if (mBarHeight != heightFromConfig) {
125             mBarHeight = heightFromConfig;
126             apply(mCurrentState);
127         }
128 
129         if (DEBUG) Log.v(TAG, "defineSlots");
130     }
131 
132     /**
133      * Adds the status bar view to the window manager.
134      */
attach()135     public void attach() {
136         // Now that the status bar window encompasses the sliding panel and its
137         // translucent backdrop, the entire thing is made TRANSLUCENT and is
138         // hardware-accelerated.
139         Trace.beginSection("StatusBarWindowController.getBarLayoutParams");
140         mLp = getBarLayoutParams(mContext.getDisplay().getRotation());
141         Trace.endSection();
142 
143         mWindowManager.addView(mStatusBarWindowView, mLp);
144         mLpChanged.copyFrom(mLp);
145 
146         mContentInsetsProvider.addCallback(this::calculateStatusBarLocationsForAllRotations);
147         calculateStatusBarLocationsForAllRotations();
148         mIsAttached = true;
149         apply(mCurrentState);
150     }
151 
152     /** Adds the given view to the status bar window view. */
addViewToWindow(View view, ViewGroup.LayoutParams layoutParams)153     public void addViewToWindow(View view, ViewGroup.LayoutParams layoutParams) {
154         mStatusBarWindowView.addView(view, layoutParams);
155     }
156 
157     /** Returns the status bar window's background view. */
getBackgroundView()158     public View getBackgroundView() {
159         return mStatusBarWindowView.findViewById(R.id.status_bar_container);
160     }
161 
162     /** Returns a fragment host manager for the status bar window view. */
getFragmentHostManager()163     public FragmentHostManager getFragmentHostManager() {
164         return mFragmentService.getFragmentHostManager(mStatusBarWindowView);
165     }
166 
167     /**
168      * Provides an updated animation controller if we're animating a view in the status bar.
169      *
170      * This is needed because we have to make sure that the status bar window matches the full
171      * screen during the animation and that we are expanding the view below the other status bar
172      * text.
173      *
174      * @param rootView the root view of the animation
175      * @param animationController the default animation controller to use
176      * @return If the animation is on a view in the status bar, returns an Optional containing an
177      *   updated animation controller that handles status-bar-related animation details. Returns an
178      *   empty optional if the animation is *not* on a view in the status bar.
179      */
wrapAnimationControllerIfInStatusBar( View rootView, ActivityLaunchAnimator.Controller animationController)180     public Optional<ActivityLaunchAnimator.Controller> wrapAnimationControllerIfInStatusBar(
181             View rootView, ActivityLaunchAnimator.Controller animationController) {
182         if (rootView != mStatusBarWindowView) {
183             return Optional.empty();
184         }
185 
186         animationController.setLaunchContainer(mLaunchAnimationContainer);
187         return Optional.of(new DelegateLaunchAnimatorController(animationController) {
188             @Override
189             public void onLaunchAnimationStart(boolean isExpandingFullyAbove) {
190                 getDelegate().onLaunchAnimationStart(isExpandingFullyAbove);
191                 setLaunchAnimationRunning(true);
192             }
193 
194             @Override
195             public void onLaunchAnimationEnd(boolean isExpandingFullyAbove) {
196                 getDelegate().onLaunchAnimationEnd(isExpandingFullyAbove);
197                 setLaunchAnimationRunning(false);
198             }
199         });
200     }
201 
202     private WindowManager.LayoutParams getBarLayoutParams(int rotation) {
203         WindowManager.LayoutParams lp = getBarLayoutParamsForRotation(rotation);
204         lp.paramsForRotation = new WindowManager.LayoutParams[4];
205         for (int rot = Surface.ROTATION_0; rot <= Surface.ROTATION_270; rot++) {
206             lp.paramsForRotation[rot] = getBarLayoutParamsForRotation(rot);
207         }
208         return lp;
209     }
210 
211     private WindowManager.LayoutParams getBarLayoutParamsForRotation(int rotation) {
212         int height = SystemBarUtils.getStatusBarHeightForRotation(mContext, rotation);
213         WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
214                 WindowManager.LayoutParams.MATCH_PARENT,
215                 height,
216                 WindowManager.LayoutParams.TYPE_STATUS_BAR,
217                 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
218                         | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
219                         | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
220                 PixelFormat.TRANSLUCENT);
221         lp.privateFlags |= PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
222         lp.token = new Binder();
223         lp.gravity = Gravity.TOP;
224         lp.setFitInsetsTypes(0 /* types */);
225         lp.setTitle("StatusBar");
226         lp.packageName = mContext.getPackageName();
227         lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
228         return lp;
229 
230     }
231 
232     private void calculateStatusBarLocationsForAllRotations() {
233         Rect[] bounds = new Rect[4];
234         final DisplayCutout displayCutout = mContext.getDisplay().getCutout();
235         bounds[0] = mContentInsetsProvider
236                 .getBoundingRectForPrivacyChipForRotation(ROTATION_NONE, displayCutout);
237         bounds[1] = mContentInsetsProvider
238                 .getBoundingRectForPrivacyChipForRotation(ROTATION_LANDSCAPE, displayCutout);
239         bounds[2] = mContentInsetsProvider
240                 .getBoundingRectForPrivacyChipForRotation(ROTATION_UPSIDE_DOWN, displayCutout);
241         bounds[3] = mContentInsetsProvider
242                 .getBoundingRectForPrivacyChipForRotation(ROTATION_SEASCAPE, displayCutout);
243 
244         try {
245             mIWindowManager.updateStaticPrivacyIndicatorBounds(mContext.getDisplayId(), bounds);
246         } catch (RemoteException e) {
247              //Swallow
248         }
249     }
250 
251     /** Set force status bar visible. */
252     public void setForceStatusBarVisible(boolean forceStatusBarVisible) {
253         mCurrentState.mForceStatusBarVisible = forceStatusBarVisible;
254         apply(mCurrentState);
255     }
256 
257     /**
258      * Sets whether an ongoing process requires the status bar to be forced visible.
259      *
260      * This method is separate from {@link this#setForceStatusBarVisible} because the ongoing
261      * process **takes priority**. For example, if {@link this#setForceStatusBarVisible} is set to
262      * false but this method is set to true, then the status bar **will** be visible.
263      *
264      * TODO(b/195839150): We should likely merge this method and
265      * {@link this#setForceStatusBarVisible} together and use some sort of ranking system instead.
266      */
267     public void setOngoingProcessRequiresStatusBarVisible(boolean visible) {
268         mCurrentState.mOngoingProcessRequiresStatusBarVisible = visible;
269         apply(mCurrentState);
270     }
271 
272     /**
273      * Set whether a launch animation is currently running. If true, this will ensure that the
274      * window matches its parent height so that the animation is not clipped by the normal status
275      * bar height.
276      */
277     private void setLaunchAnimationRunning(boolean isLaunchAnimationRunning) {
278         if (isLaunchAnimationRunning == mCurrentState.mIsLaunchAnimationRunning) {
279             return;
280         }
281 
282         mCurrentState.mIsLaunchAnimationRunning = isLaunchAnimationRunning;
283         apply(mCurrentState);
284     }
285 
286     private void applyHeight(State state) {
287         mLpChanged.height =
288                 state.mIsLaunchAnimationRunning ? ViewGroup.LayoutParams.MATCH_PARENT : mBarHeight;
289         for (int rot = Surface.ROTATION_0; rot <= Surface.ROTATION_270; rot++) {
290             mLpChanged.paramsForRotation[rot].height =
291                     state.mIsLaunchAnimationRunning ? ViewGroup.LayoutParams.MATCH_PARENT :
292                     SystemBarUtils.getStatusBarHeightForRotation(mContext, rot);
293         }
294     }
295 
296     private void apply(State state) {
297         if (!mIsAttached) {
298             return;
299         }
300         applyForceStatusBarVisibleFlag(state);
301         applyHeight(state);
302         if (mLp != null && mLp.copyFrom(mLpChanged) != 0) {
303             mWindowManager.updateViewLayout(mStatusBarWindowView, mLp);
304         }
305     }
306 
307     private static class State {
308         boolean mForceStatusBarVisible;
309         boolean mIsLaunchAnimationRunning;
310         boolean mOngoingProcessRequiresStatusBarVisible;
311     }
312 
313     private void applyForceStatusBarVisibleFlag(State state) {
314         if (state.mForceStatusBarVisible
315                 || state.mIsLaunchAnimationRunning
316                 // Don't force-show the status bar if the user has already dismissed it.
317                 || state.mOngoingProcessRequiresStatusBarVisible) {
318             mLpChanged.privateFlags |= PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
319         } else {
320             mLpChanged.privateFlags &= ~PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
321         }
322     }
323 }
324