1 /*
2  * Copyright (C) 2014 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.phone;
18 
19 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
20 
21 import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
22 
23 import android.app.ActivityManager;
24 import android.app.IActivityManager;
25 import android.content.Context;
26 import android.content.pm.ActivityInfo;
27 import android.content.res.Resources;
28 import android.graphics.PixelFormat;
29 import android.os.Binder;
30 import android.os.RemoteException;
31 import android.os.SystemProperties;
32 import android.util.Log;
33 import android.view.Gravity;
34 import android.view.View;
35 import android.view.ViewGroup;
36 import android.view.WindowManager;
37 import android.view.WindowManager.LayoutParams;
38 
39 import com.android.keyguard.R;
40 import com.android.systemui.Dumpable;
41 import com.android.systemui.keyguard.KeyguardViewMediator;
42 import com.android.systemui.statusbar.RemoteInputController;
43 import com.android.systemui.statusbar.StatusBarState;
44 
45 import java.io.FileDescriptor;
46 import java.io.PrintWriter;
47 import java.lang.reflect.Field;
48 
49 /**
50  * Encapsulates all logic for the status bar window state management.
51  */
52 public class StatusBarWindowManager implements RemoteInputController.Callback, Dumpable {
53 
54     private static final String TAG = "StatusBarWindowManager";
55 
56     private final Context mContext;
57     private final WindowManager mWindowManager;
58     private final IActivityManager mActivityManager;
59     private final DozeParameters mDozeParameters;
60     private View mStatusBarView;
61     private WindowManager.LayoutParams mLp;
62     private WindowManager.LayoutParams mLpChanged;
63     private boolean mHasTopUi;
64     private boolean mHasTopUiChanged;
65     private int mBarHeight;
66     private final boolean mKeyguardScreenRotation;
67     private float mScreenBrightnessDoze;
68     private final State mCurrentState = new State();
69     private OtherwisedCollapsedListener mListener;
70 
StatusBarWindowManager(Context context)71     public StatusBarWindowManager(Context context) {
72         mContext = context;
73         mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
74         mActivityManager = ActivityManager.getService();
75         mKeyguardScreenRotation = shouldEnableKeyguardScreenRotation();
76         mDozeParameters = DozeParameters.getInstance(mContext);
77         mScreenBrightnessDoze = mDozeParameters.getScreenBrightnessDoze();
78     }
79 
shouldEnableKeyguardScreenRotation()80     private boolean shouldEnableKeyguardScreenRotation() {
81         Resources res = mContext.getResources();
82         return SystemProperties.getBoolean("lockscreen.rot_override", false)
83                 || res.getBoolean(R.bool.config_enableLockScreenRotation);
84     }
85 
86     /**
87      * Adds the status bar view to the window manager.
88      *
89      * @param statusBarView The view to add.
90      * @param barHeight The height of the status bar in collapsed state.
91      */
add(View statusBarView, int barHeight)92     public void add(View statusBarView, int barHeight) {
93 
94         // Now that the status bar window encompasses the sliding panel and its
95         // translucent backdrop, the entire thing is made TRANSLUCENT and is
96         // hardware-accelerated.
97         mLp = new WindowManager.LayoutParams(
98                 ViewGroup.LayoutParams.MATCH_PARENT,
99                 barHeight,
100                 WindowManager.LayoutParams.TYPE_STATUS_BAR,
101                 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
102                         | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
103                         | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
104                         | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
105                         | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
106                 PixelFormat.TRANSLUCENT);
107         mLp.token = new Binder();
108         mLp.gravity = Gravity.TOP;
109         mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
110         mLp.setTitle("StatusBar");
111         mLp.packageName = mContext.getPackageName();
112         mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
113         mStatusBarView = statusBarView;
114         mBarHeight = barHeight;
115         mWindowManager.addView(mStatusBarView, mLp);
116         mLpChanged = new WindowManager.LayoutParams();
117         mLpChanged.copyFrom(mLp);
118     }
119 
setDozeScreenBrightness(int value)120     public void setDozeScreenBrightness(int value) {
121         mScreenBrightnessDoze = value / 255f;
122     }
123 
setKeyguardDark(boolean dark)124     public void setKeyguardDark(boolean dark) {
125         int vis = mStatusBarView.getSystemUiVisibility();
126         if (dark) {
127             vis = vis | View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
128             vis = vis | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
129         } else {
130             vis = vis & ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
131             vis = vis & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
132         }
133         mStatusBarView.setSystemUiVisibility(vis);
134     }
135 
applyKeyguardFlags(State state)136     private void applyKeyguardFlags(State state) {
137         if (state.keyguardShowing) {
138             mLpChanged.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
139         } else {
140             mLpChanged.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
141         }
142 
143         final boolean scrimsOccludingWallpaper =
144                 state.scrimsVisibility == ScrimController.VISIBILITY_FULLY_OPAQUE;
145         final boolean keyguardOrAod = state.keyguardShowing
146                 || (state.dozing && mDozeParameters.getAlwaysOn());
147         if (keyguardOrAod && !state.backdropShowing && !scrimsOccludingWallpaper) {
148             mLpChanged.flags |= WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
149         } else {
150             mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
151         }
152     }
153 
adjustScreenOrientation(State state)154     private void adjustScreenOrientation(State state) {
155         if (state.isKeyguardShowingAndNotOccluded() || state.dozing) {
156             if (mKeyguardScreenRotation) {
157                 mLpChanged.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_USER;
158             } else {
159                 mLpChanged.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
160             }
161         } else {
162             mLpChanged.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
163         }
164     }
165 
applyFocusableFlag(State state)166     private void applyFocusableFlag(State state) {
167         boolean panelFocusable = state.statusBarFocusable && state.panelExpanded;
168         if (state.bouncerShowing && (state.keyguardOccluded || state.keyguardNeedsInput)
169                 || ENABLE_REMOTE_INPUT && state.remoteInputActive) {
170             mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
171             mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
172         } else if (state.isKeyguardShowingAndNotOccluded() || panelFocusable) {
173             mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
174             mLpChanged.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
175         } else {
176             mLpChanged.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
177             mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
178         }
179 
180         mLpChanged.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
181     }
182 
applyHeight(State state)183     private void applyHeight(State state) {
184         boolean expanded = isExpanded(state);
185         if (state.forcePluginOpen) {
186             mListener.setWouldOtherwiseCollapse(expanded);
187             expanded = true;
188         }
189         if (expanded) {
190             mLpChanged.height = ViewGroup.LayoutParams.MATCH_PARENT;
191         } else {
192             mLpChanged.height = mBarHeight;
193         }
194     }
195 
isExpanded(State state)196     private boolean isExpanded(State state) {
197         return !state.forceCollapsed && (state.isKeyguardShowingAndNotOccluded()
198                 || state.panelVisible || state.keyguardFadingAway || state.bouncerShowing
199                 || state.headsUpShowing
200                 || state.scrimsVisibility != ScrimController.VISIBILITY_FULLY_TRANSPARENT);
201     }
202 
applyFitsSystemWindows(State state)203     private void applyFitsSystemWindows(State state) {
204         boolean fitsSystemWindows = !state.isKeyguardShowingAndNotOccluded();
205         if (mStatusBarView.getFitsSystemWindows() != fitsSystemWindows) {
206             mStatusBarView.setFitsSystemWindows(fitsSystemWindows);
207             mStatusBarView.requestApplyInsets();
208         }
209     }
210 
applyUserActivityTimeout(State state)211     private void applyUserActivityTimeout(State state) {
212         if (state.isKeyguardShowingAndNotOccluded()
213                 && state.statusBarState == StatusBarState.KEYGUARD
214                 && !state.qsExpanded) {
215             mLpChanged.userActivityTimeout = KeyguardViewMediator.AWAKE_INTERVAL_DEFAULT_MS;
216         } else {
217             mLpChanged.userActivityTimeout = -1;
218         }
219     }
220 
applyInputFeatures(State state)221     private void applyInputFeatures(State state) {
222         if (state.isKeyguardShowingAndNotOccluded()
223                 && state.statusBarState == StatusBarState.KEYGUARD
224                 && !state.qsExpanded && !state.forceUserActivity) {
225             mLpChanged.inputFeatures |=
226                     WindowManager.LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY;
227         } else {
228             mLpChanged.inputFeatures &=
229                     ~WindowManager.LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY;
230         }
231     }
232 
apply(State state)233     private void apply(State state) {
234         applyKeyguardFlags(state);
235         applyForceStatusBarVisibleFlag(state);
236         applyFocusableFlag(state);
237         adjustScreenOrientation(state);
238         applyHeight(state);
239         applyUserActivityTimeout(state);
240         applyInputFeatures(state);
241         applyFitsSystemWindows(state);
242         applyModalFlag(state);
243         applyBrightness(state);
244         applyHasTopUi(state);
245         applySleepToken(state);
246         if (mLp.copyFrom(mLpChanged) != 0) {
247             mWindowManager.updateViewLayout(mStatusBarView, mLp);
248         }
249         if (mHasTopUi != mHasTopUiChanged) {
250             try {
251                 mActivityManager.setHasTopUi(mHasTopUiChanged);
252             } catch (RemoteException e) {
253                 Log.e(TAG, "Failed to call setHasTopUi", e);
254             }
255             mHasTopUi = mHasTopUiChanged;
256         }
257     }
258 
applyForceStatusBarVisibleFlag(State state)259     private void applyForceStatusBarVisibleFlag(State state) {
260         if (state.forceStatusBarVisible) {
261             mLpChanged.privateFlags |= WindowManager
262                     .LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT;
263         } else {
264             mLpChanged.privateFlags &= ~WindowManager
265                     .LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT;
266         }
267     }
268 
applyModalFlag(State state)269     private void applyModalFlag(State state) {
270         if (state.headsUpShowing) {
271             mLpChanged.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
272         } else {
273             mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
274         }
275     }
276 
applyBrightness(State state)277     private void applyBrightness(State state) {
278         if (state.forceDozeBrightness) {
279             mLpChanged.screenBrightness = mScreenBrightnessDoze;
280         } else {
281             mLpChanged.screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE;
282         }
283     }
284 
applyHasTopUi(State state)285     private void applyHasTopUi(State state) {
286         mHasTopUiChanged = isExpanded(state);
287     }
288 
applySleepToken(State state)289     private void applySleepToken(State state) {
290         if (state.dozing) {
291             mLpChanged.privateFlags |= LayoutParams.PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN;
292         } else {
293             mLpChanged.privateFlags &= ~LayoutParams.PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN;
294         }
295     }
296 
setKeyguardShowing(boolean showing)297     public void setKeyguardShowing(boolean showing) {
298         mCurrentState.keyguardShowing = showing;
299         apply(mCurrentState);
300     }
301 
setKeyguardOccluded(boolean occluded)302     public void setKeyguardOccluded(boolean occluded) {
303         mCurrentState.keyguardOccluded = occluded;
304         apply(mCurrentState);
305     }
306 
setKeyguardNeedsInput(boolean needsInput)307     public void setKeyguardNeedsInput(boolean needsInput) {
308         mCurrentState.keyguardNeedsInput = needsInput;
309         apply(mCurrentState);
310     }
311 
setPanelVisible(boolean visible)312     public void setPanelVisible(boolean visible) {
313         mCurrentState.panelVisible = visible;
314         mCurrentState.statusBarFocusable = visible;
315         apply(mCurrentState);
316     }
317 
setStatusBarFocusable(boolean focusable)318     public void setStatusBarFocusable(boolean focusable) {
319         mCurrentState.statusBarFocusable = focusable;
320         apply(mCurrentState);
321     }
322 
setBouncerShowing(boolean showing)323     public void setBouncerShowing(boolean showing) {
324         mCurrentState.bouncerShowing = showing;
325         apply(mCurrentState);
326     }
327 
setBackdropShowing(boolean showing)328     public void setBackdropShowing(boolean showing) {
329         mCurrentState.backdropShowing = showing;
330         apply(mCurrentState);
331     }
332 
setKeyguardFadingAway(boolean keyguardFadingAway)333     public void setKeyguardFadingAway(boolean keyguardFadingAway) {
334         mCurrentState.keyguardFadingAway = keyguardFadingAway;
335         apply(mCurrentState);
336     }
337 
setQsExpanded(boolean expanded)338     public void setQsExpanded(boolean expanded) {
339         mCurrentState.qsExpanded = expanded;
340         apply(mCurrentState);
341     }
342 
setForceUserActivity(boolean forceUserActivity)343     public void setForceUserActivity(boolean forceUserActivity) {
344         mCurrentState.forceUserActivity = forceUserActivity;
345         apply(mCurrentState);
346     }
347 
setScrimsVisibility(int scrimsVisibility)348     public void setScrimsVisibility(int scrimsVisibility) {
349         mCurrentState.scrimsVisibility = scrimsVisibility;
350         apply(mCurrentState);
351     }
352 
setHeadsUpShowing(boolean showing)353     public void setHeadsUpShowing(boolean showing) {
354         mCurrentState.headsUpShowing = showing;
355         apply(mCurrentState);
356     }
357 
setWallpaperSupportsAmbientMode(boolean supportsAmbientMode)358     public void setWallpaperSupportsAmbientMode(boolean supportsAmbientMode) {
359         mCurrentState.wallpaperSupportsAmbientMode = supportsAmbientMode;
360         apply(mCurrentState);
361     }
362 
363     /**
364      * @param state The {@link StatusBarState} of the status bar.
365      */
setStatusBarState(int state)366     public void setStatusBarState(int state) {
367         mCurrentState.statusBarState = state;
368         apply(mCurrentState);
369     }
370 
setForceStatusBarVisible(boolean forceStatusBarVisible)371     public void setForceStatusBarVisible(boolean forceStatusBarVisible) {
372         mCurrentState.forceStatusBarVisible = forceStatusBarVisible;
373         apply(mCurrentState);
374     }
375 
376     /**
377      * Force the window to be collapsed, even if it should theoretically be expanded.
378      * Used for when a heads-up comes in but we still need to wait for the touchable regions to
379      * be computed.
380      */
setForceWindowCollapsed(boolean force)381     public void setForceWindowCollapsed(boolean force) {
382         mCurrentState.forceCollapsed = force;
383         apply(mCurrentState);
384     }
385 
setPanelExpanded(boolean isExpanded)386     public void setPanelExpanded(boolean isExpanded) {
387         mCurrentState.panelExpanded = isExpanded;
388         apply(mCurrentState);
389     }
390 
391     @Override
onRemoteInputActive(boolean remoteInputActive)392     public void onRemoteInputActive(boolean remoteInputActive) {
393         mCurrentState.remoteInputActive = remoteInputActive;
394         apply(mCurrentState);
395     }
396 
397     /**
398      * Set whether the screen brightness is forced to the value we use for doze mode by the status
399      * bar window.
400      */
setForceDozeBrightness(boolean forceDozeBrightness)401     public void setForceDozeBrightness(boolean forceDozeBrightness) {
402         mCurrentState.forceDozeBrightness = forceDozeBrightness;
403         apply(mCurrentState);
404     }
405 
setDozing(boolean dozing)406     public void setDozing(boolean dozing) {
407         mCurrentState.dozing = dozing;
408         apply(mCurrentState);
409     }
410 
setBarHeight(int barHeight)411     public void setBarHeight(int barHeight) {
412         mBarHeight = barHeight;
413         apply(mCurrentState);
414     }
415 
setForcePluginOpen(boolean forcePluginOpen)416     public void setForcePluginOpen(boolean forcePluginOpen) {
417         mCurrentState.forcePluginOpen = forcePluginOpen;
418         apply(mCurrentState);
419     }
420 
setStateListener(OtherwisedCollapsedListener listener)421     public void setStateListener(OtherwisedCollapsedListener listener) {
422         mListener = listener;
423     }
424 
dump(FileDescriptor fd, PrintWriter pw, String[] args)425     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
426         pw.println("StatusBarWindowManager state:");
427         pw.println(mCurrentState);
428     }
429 
isShowingWallpaper()430     public boolean isShowingWallpaper() {
431         return !mCurrentState.backdropShowing;
432     }
433 
434     private static class State {
435         boolean keyguardShowing;
436         boolean keyguardOccluded;
437         boolean keyguardNeedsInput;
438         boolean panelVisible;
439         boolean panelExpanded;
440         boolean statusBarFocusable;
441         boolean bouncerShowing;
442         boolean keyguardFadingAway;
443         boolean qsExpanded;
444         boolean headsUpShowing;
445         boolean forceStatusBarVisible;
446         boolean forceCollapsed;
447         boolean forceDozeBrightness;
448         boolean forceUserActivity;
449         boolean backdropShowing;
450         boolean wallpaperSupportsAmbientMode;
451 
452         /**
453          * The {@link StatusBar} state from the status bar.
454          */
455         int statusBarState;
456 
457         boolean remoteInputActive;
458         boolean forcePluginOpen;
459         boolean dozing;
460         int scrimsVisibility;
461 
isKeyguardShowingAndNotOccluded()462         private boolean isKeyguardShowingAndNotOccluded() {
463             return keyguardShowing && !keyguardOccluded;
464         }
465 
466         @Override
toString()467         public String toString() {
468             StringBuilder result = new StringBuilder();
469             String newLine = "\n";
470             result.append("Window State {");
471             result.append(newLine);
472 
473             Field[] fields = this.getClass().getDeclaredFields();
474 
475             // Print field names paired with their values
476             for (Field field : fields) {
477                 result.append("  ");
478                 try {
479                     result.append(field.getName());
480                     result.append(": ");
481                     //requires access to private field:
482                     result.append(field.get(this));
483                 } catch (IllegalAccessException ex) {
484                 }
485                 result.append(newLine);
486             }
487             result.append("}");
488 
489             return result.toString();
490         }
491     }
492 
493     /**
494      * Custom listener to pipe data back to plugins about whether or not the status bar would be
495      * collapsed if not for the plugin.
496      * TODO: Find cleaner way to do this.
497      */
498     public interface OtherwisedCollapsedListener {
setWouldOtherwiseCollapse(boolean otherwiseCollapse)499         void setWouldOtherwiseCollapse(boolean otherwiseCollapse);
500     }
501 }
502