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