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.os.Trace;
33 import android.util.Log;
34 import android.view.Display;
35 import android.view.Gravity;
36 import android.view.View;
37 import android.view.ViewGroup;
38 import android.view.WindowManager;
39 import android.view.WindowManager.LayoutParams;
40 
41 import com.android.internal.annotations.VisibleForTesting;
42 import com.android.systemui.Dependency;
43 import com.android.systemui.Dumpable;
44 import com.android.systemui.R;
45 import com.android.systemui.colorextraction.SysuiColorExtractor;
46 import com.android.systemui.keyguard.KeyguardViewMediator;
47 import com.android.systemui.plugins.statusbar.StatusBarStateController;
48 import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
49 import com.android.systemui.statusbar.RemoteInputController.Callback;
50 import com.android.systemui.statusbar.StatusBarState;
51 import com.android.systemui.statusbar.SysuiStatusBarStateController;
52 import com.android.systemui.statusbar.policy.ConfigurationController;
53 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
54 
55 import com.google.android.collect.Lists;
56 
57 import java.io.FileDescriptor;
58 import java.io.PrintWriter;
59 import java.lang.ref.WeakReference;
60 import java.lang.reflect.Field;
61 import java.util.ArrayList;
62 import java.util.Arrays;
63 
64 import javax.inject.Inject;
65 import javax.inject.Singleton;
66 
67 /**
68  * Encapsulates all logic for the status bar window state management.
69  */
70 @Singleton
71 public class StatusBarWindowController implements Callback, Dumpable, ConfigurationListener {
72 
73     private static final String TAG = "StatusBarWindowController";
74 
75     private final Context mContext;
76     private final WindowManager mWindowManager;
77     private final IActivityManager mActivityManager;
78     private final DozeParameters mDozeParameters;
79     private final LayoutParams mLpChanged;
80     private final boolean mKeyguardScreenRotation;
81     private final long mLockScreenDisplayTimeout;
82     private final Display.Mode mKeyguardDisplayMode;
83     private final KeyguardBypassController mKeyguardBypassController;
84     private ViewGroup mStatusBarView;
85     private LayoutParams mLp;
86     private boolean mHasTopUi;
87     private boolean mHasTopUiChanged;
88     private int mBarHeight;
89     private float mScreenBrightnessDoze;
90     private final State mCurrentState = new State();
91     private OtherwisedCollapsedListener mListener;
92     private ForcePluginOpenListener mForcePluginOpenListener;
93     private final ArrayList<WeakReference<StatusBarWindowCallback>>
94             mCallbacks = Lists.newArrayList();
95 
96     private final SysuiColorExtractor mColorExtractor = Dependency.get(SysuiColorExtractor.class);
97 
98     @Inject
StatusBarWindowController(Context context, StatusBarStateController statusBarStateController, ConfigurationController configurationController, KeyguardBypassController keyguardBypassController)99     public StatusBarWindowController(Context context,
100             StatusBarStateController statusBarStateController,
101             ConfigurationController configurationController,
102             KeyguardBypassController keyguardBypassController) {
103         this(context, context.getSystemService(WindowManager.class), ActivityManager.getService(),
104                 DozeParameters.getInstance(context), statusBarStateController,
105                 configurationController, keyguardBypassController);
106     }
107 
108     @VisibleForTesting
StatusBarWindowController(Context context, WindowManager windowManager, IActivityManager activityManager, DozeParameters dozeParameters, StatusBarStateController statusBarStateController, ConfigurationController configurationController, KeyguardBypassController keyguardBypassController)109     public StatusBarWindowController(Context context, WindowManager windowManager,
110             IActivityManager activityManager, DozeParameters dozeParameters,
111             StatusBarStateController statusBarStateController,
112             ConfigurationController configurationController,
113             KeyguardBypassController keyguardBypassController) {
114         mContext = context;
115         mWindowManager = windowManager;
116         mActivityManager = activityManager;
117         mKeyguardScreenRotation = shouldEnableKeyguardScreenRotation();
118         mDozeParameters = dozeParameters;
119         mScreenBrightnessDoze = mDozeParameters.getScreenBrightnessDoze();
120         mLpChanged = new LayoutParams();
121         mKeyguardBypassController = keyguardBypassController;
122         mLockScreenDisplayTimeout = context.getResources()
123                 .getInteger(R.integer.config_lockScreenDisplayTimeout);
124         ((SysuiStatusBarStateController) statusBarStateController)
125                 .addCallback(mStateListener,
126                         SysuiStatusBarStateController.RANK_STATUS_BAR_WINDOW_CONTROLLER);
127         configurationController.addCallback(this);
128 
129         Display.Mode[] supportedModes = context.getDisplay().getSupportedModes();
130         Display.Mode currentMode = context.getDisplay().getMode();
131         // Running on the highest frame rate available can be expensive.
132         // Let's specify a preferred refresh rate, and allow higher FPS only when we
133         // know that we're not falsing (because we unlocked.)
134         int keyguardRefreshRate = context.getResources()
135                 .getInteger(R.integer.config_keyguardRefreshRate);
136         // Find supported display mode with the same resolution and requested refresh rate.
137         mKeyguardDisplayMode = Arrays.stream(supportedModes).filter(mode ->
138                 (int) mode.getRefreshRate() == keyguardRefreshRate
139                         && mode.getPhysicalWidth() == currentMode.getPhysicalWidth()
140                         && mode.getPhysicalHeight() == currentMode.getPhysicalHeight())
141                 .findFirst().orElse(null);
142     }
143 
144     /**
145      * Register to receive notifications about status bar window state changes.
146      */
registerCallback(StatusBarWindowCallback callback)147     public void registerCallback(StatusBarWindowCallback callback) {
148         // Prevent adding duplicate callbacks
149         for (int i = 0; i < mCallbacks.size(); i++) {
150             if (mCallbacks.get(i).get() == callback) {
151                 return;
152             }
153         }
154         mCallbacks.add(new WeakReference<StatusBarWindowCallback>(callback));
155     }
156 
shouldEnableKeyguardScreenRotation()157     private boolean shouldEnableKeyguardScreenRotation() {
158         Resources res = mContext.getResources();
159         return SystemProperties.getBoolean("lockscreen.rot_override", false)
160                 || res.getBoolean(R.bool.config_enableLockScreenRotation);
161     }
162 
163     /**
164      * Adds the status bar view to the window manager.
165      *
166      * @param statusBarView The view to add.
167      * @param barHeight The height of the status bar in collapsed state.
168      */
add(ViewGroup statusBarView, int barHeight)169     public void add(ViewGroup statusBarView, int barHeight) {
170 
171         // Now that the status bar window encompasses the sliding panel and its
172         // translucent backdrop, the entire thing is made TRANSLUCENT and is
173         // hardware-accelerated.
174         mLp = new LayoutParams(
175                 ViewGroup.LayoutParams.MATCH_PARENT,
176                 barHeight,
177                 LayoutParams.TYPE_STATUS_BAR,
178                 LayoutParams.FLAG_NOT_FOCUSABLE
179                         | LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
180                         | LayoutParams.FLAG_SPLIT_TOUCH
181                         | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
182                         | LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
183                 PixelFormat.TRANSLUCENT);
184         mLp.token = new Binder();
185         mLp.gravity = Gravity.TOP;
186         mLp.softInputMode = LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
187         mLp.setTitle("StatusBar");
188         mLp.packageName = mContext.getPackageName();
189         mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
190         mStatusBarView = statusBarView;
191         mBarHeight = barHeight;
192         mWindowManager.addView(mStatusBarView, mLp);
193         mLpChanged.copyFrom(mLp);
194         onThemeChanged();
195     }
196 
getStatusBarView()197     public ViewGroup getStatusBarView() {
198         return mStatusBarView;
199     }
200 
setDozeScreenBrightness(int value)201     public void setDozeScreenBrightness(int value) {
202         mScreenBrightnessDoze = value / 255f;
203     }
204 
setKeyguardDark(boolean dark)205     private void setKeyguardDark(boolean dark) {
206         int vis = mStatusBarView.getSystemUiVisibility();
207         if (dark) {
208             vis = vis | View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
209             vis = vis | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
210         } else {
211             vis = vis & ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
212             vis = vis & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
213         }
214         mStatusBarView.setSystemUiVisibility(vis);
215     }
216 
applyKeyguardFlags(State state)217     private void applyKeyguardFlags(State state) {
218         if (state.keyguardShowing) {
219             mLpChanged.privateFlags |= LayoutParams.PRIVATE_FLAG_KEYGUARD;
220         } else {
221             mLpChanged.privateFlags &= ~LayoutParams.PRIVATE_FLAG_KEYGUARD;
222         }
223 
224         final boolean scrimsOccludingWallpaper =
225                 state.scrimsVisibility == ScrimController.VISIBILITY_FULLY_OPAQUE;
226         final boolean keyguardOrAod = state.keyguardShowing
227                 || (state.dozing && mDozeParameters.getAlwaysOn());
228         if (keyguardOrAod && !state.backdropShowing && !scrimsOccludingWallpaper) {
229             mLpChanged.flags |= LayoutParams.FLAG_SHOW_WALLPAPER;
230         } else {
231             mLpChanged.flags &= ~LayoutParams.FLAG_SHOW_WALLPAPER;
232         }
233 
234         if (state.dozing) {
235             mLpChanged.privateFlags |= LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
236         } else {
237             mLpChanged.privateFlags &= ~LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
238         }
239 
240         if (mKeyguardDisplayMode != null) {
241             boolean bypassOnKeyguard = mKeyguardBypassController.getBypassEnabled()
242                     && state.statusBarState == StatusBarState.KEYGUARD && !state.keyguardFadingAway
243                     && !state.keyguardGoingAway;
244             if (state.dozing || bypassOnKeyguard) {
245                 mLpChanged.preferredDisplayModeId = mKeyguardDisplayMode.getModeId();
246             } else {
247                 mLpChanged.preferredDisplayModeId = 0;
248             }
249             Trace.setCounter("display_mode_id", mLpChanged.preferredDisplayModeId);
250         }
251     }
252 
adjustScreenOrientation(State state)253     private void adjustScreenOrientation(State state) {
254         if (state.isKeyguardShowingAndNotOccluded() || state.dozing) {
255             if (mKeyguardScreenRotation) {
256                 mLpChanged.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_USER;
257             } else {
258                 mLpChanged.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
259             }
260         } else {
261             mLpChanged.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
262         }
263     }
264 
applyFocusableFlag(State state)265     private void applyFocusableFlag(State state) {
266         boolean panelFocusable = state.statusBarFocusable && state.panelExpanded;
267         if (state.bouncerShowing && (state.keyguardOccluded || state.keyguardNeedsInput)
268                 || ENABLE_REMOTE_INPUT && state.remoteInputActive
269                 || state.bubbleExpanded) {
270             mLpChanged.flags &= ~LayoutParams.FLAG_NOT_FOCUSABLE;
271             mLpChanged.flags &= ~LayoutParams.FLAG_ALT_FOCUSABLE_IM;
272         } else if (state.isKeyguardShowingAndNotOccluded() || panelFocusable) {
273             mLpChanged.flags &= ~LayoutParams.FLAG_NOT_FOCUSABLE;
274             mLpChanged.flags |= LayoutParams.FLAG_ALT_FOCUSABLE_IM;
275         } else {
276             mLpChanged.flags |= LayoutParams.FLAG_NOT_FOCUSABLE;
277             mLpChanged.flags &= ~LayoutParams.FLAG_ALT_FOCUSABLE_IM;
278         }
279 
280         mLpChanged.softInputMode = LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
281     }
282 
applyForceShowNavigationFlag(State state)283     private void applyForceShowNavigationFlag(State state) {
284         if (state.panelExpanded || state.bouncerShowing
285                 || ENABLE_REMOTE_INPUT && state.remoteInputActive) {
286             mLpChanged.privateFlags |= LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
287         } else {
288             mLpChanged.privateFlags &= ~LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
289         }
290     }
291 
applyHeight(State state)292     private void applyHeight(State state) {
293         boolean expanded = isExpanded(state);
294         if (state.forcePluginOpen) {
295             if (mListener != null) {
296                 mListener.setWouldOtherwiseCollapse(expanded);
297             }
298             expanded = true;
299         }
300         if (expanded) {
301             mLpChanged.height = ViewGroup.LayoutParams.MATCH_PARENT;
302         } else {
303             mLpChanged.height = mBarHeight;
304         }
305     }
306 
isExpanded(State state)307     private boolean isExpanded(State state) {
308         return !state.forceCollapsed && (state.isKeyguardShowingAndNotOccluded()
309                 || state.panelVisible || state.keyguardFadingAway || state.bouncerShowing
310                 || state.headsUpShowing || state.bubblesShowing
311                 || state.scrimsVisibility != ScrimController.VISIBILITY_FULLY_TRANSPARENT);
312     }
313 
applyFitsSystemWindows(State state)314     private void applyFitsSystemWindows(State state) {
315         boolean fitsSystemWindows = !state.isKeyguardShowingAndNotOccluded();
316         if (mStatusBarView != null && mStatusBarView.getFitsSystemWindows() != fitsSystemWindows) {
317             mStatusBarView.setFitsSystemWindows(fitsSystemWindows);
318             mStatusBarView.requestApplyInsets();
319         }
320     }
321 
applyUserActivityTimeout(State state)322     private void applyUserActivityTimeout(State state) {
323         if (state.isKeyguardShowingAndNotOccluded()
324                 && state.statusBarState == StatusBarState.KEYGUARD
325                 && !state.qsExpanded) {
326             mLpChanged.userActivityTimeout = state.bouncerShowing
327                     ? KeyguardViewMediator.AWAKE_INTERVAL_BOUNCER_MS : mLockScreenDisplayTimeout;
328         } else {
329             mLpChanged.userActivityTimeout = -1;
330         }
331     }
332 
applyInputFeatures(State state)333     private void applyInputFeatures(State state) {
334         if (state.isKeyguardShowingAndNotOccluded()
335                 && state.statusBarState == StatusBarState.KEYGUARD
336                 && !state.qsExpanded && !state.forceUserActivity) {
337             mLpChanged.inputFeatures |=
338                     LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY;
339         } else {
340             mLpChanged.inputFeatures &=
341                     ~LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY;
342         }
343     }
344 
applyStatusBarColorSpaceAgnosticFlag(State state)345     private void applyStatusBarColorSpaceAgnosticFlag(State state) {
346         if (!isExpanded(state)) {
347             mLpChanged.privateFlags |= LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
348         } else {
349             mLpChanged.privateFlags &=
350                     ~LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
351         }
352     }
353 
apply(State state)354     private void apply(State state) {
355         applyKeyguardFlags(state);
356         applyForceStatusBarVisibleFlag(state);
357         applyFocusableFlag(state);
358         applyForceShowNavigationFlag(state);
359         adjustScreenOrientation(state);
360         applyHeight(state);
361         applyUserActivityTimeout(state);
362         applyInputFeatures(state);
363         applyFitsSystemWindows(state);
364         applyModalFlag(state);
365         applyBrightness(state);
366         applyHasTopUi(state);
367         applyNotTouchable(state);
368         applyStatusBarColorSpaceAgnosticFlag(state);
369         if (mLp != null && mLp.copyFrom(mLpChanged) != 0) {
370             mWindowManager.updateViewLayout(mStatusBarView, mLp);
371         }
372         if (mHasTopUi != mHasTopUiChanged) {
373             try {
374                 mActivityManager.setHasTopUi(mHasTopUiChanged);
375             } catch (RemoteException e) {
376                 Log.e(TAG, "Failed to call setHasTopUi", e);
377             }
378             mHasTopUi = mHasTopUiChanged;
379         }
380         notifyStateChangedCallbacks();
381     }
382 
notifyStateChangedCallbacks()383     public void notifyStateChangedCallbacks() {
384         for (int i = 0; i < mCallbacks.size(); i++) {
385             StatusBarWindowCallback cb = mCallbacks.get(i).get();
386             if (cb != null) {
387                 cb.onStateChanged(mCurrentState.keyguardShowing,
388                         mCurrentState.keyguardOccluded,
389                         mCurrentState.bouncerShowing);
390             }
391         }
392     }
393 
applyForceStatusBarVisibleFlag(State state)394     private void applyForceStatusBarVisibleFlag(State state) {
395         if (state.forceStatusBarVisible || state.forcePluginOpen) {
396             mLpChanged.privateFlags |= WindowManager
397                     .LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT;
398         } else {
399             mLpChanged.privateFlags
400                     &= ~LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT;
401         }
402     }
403 
applyModalFlag(State state)404     private void applyModalFlag(State state) {
405         if (state.headsUpShowing) {
406             mLpChanged.flags |= LayoutParams.FLAG_NOT_TOUCH_MODAL;
407         } else {
408             mLpChanged.flags &= ~LayoutParams.FLAG_NOT_TOUCH_MODAL;
409         }
410     }
411 
applyBrightness(State state)412     private void applyBrightness(State state) {
413         if (state.forceDozeBrightness) {
414             mLpChanged.screenBrightness = mScreenBrightnessDoze;
415         } else {
416             mLpChanged.screenBrightness = LayoutParams.BRIGHTNESS_OVERRIDE_NONE;
417         }
418     }
419 
applyHasTopUi(State state)420     private void applyHasTopUi(State state) {
421         mHasTopUiChanged = state.forceHasTopUi || isExpanded(state);
422     }
423 
applyNotTouchable(State state)424     private void applyNotTouchable(State state) {
425         if (state.notTouchable) {
426             mLpChanged.flags |= LayoutParams.FLAG_NOT_TOUCHABLE;
427         } else {
428             mLpChanged.flags &= ~LayoutParams.FLAG_NOT_TOUCHABLE;
429         }
430     }
431 
setKeyguardShowing(boolean showing)432     public void setKeyguardShowing(boolean showing) {
433         mCurrentState.keyguardShowing = showing;
434         apply(mCurrentState);
435     }
436 
setKeyguardOccluded(boolean occluded)437     public void setKeyguardOccluded(boolean occluded) {
438         mCurrentState.keyguardOccluded = occluded;
439         apply(mCurrentState);
440     }
441 
setKeyguardNeedsInput(boolean needsInput)442     public void setKeyguardNeedsInput(boolean needsInput) {
443         mCurrentState.keyguardNeedsInput = needsInput;
444         apply(mCurrentState);
445     }
446 
setPanelVisible(boolean visible)447     public void setPanelVisible(boolean visible) {
448         mCurrentState.panelVisible = visible;
449         mCurrentState.statusBarFocusable = visible;
450         apply(mCurrentState);
451     }
452 
setStatusBarFocusable(boolean focusable)453     public void setStatusBarFocusable(boolean focusable) {
454         mCurrentState.statusBarFocusable = focusable;
455         apply(mCurrentState);
456     }
457 
setBouncerShowing(boolean showing)458     public void setBouncerShowing(boolean showing) {
459         mCurrentState.bouncerShowing = showing;
460         apply(mCurrentState);
461     }
462 
setBackdropShowing(boolean showing)463     public void setBackdropShowing(boolean showing) {
464         mCurrentState.backdropShowing = showing;
465         apply(mCurrentState);
466     }
467 
setKeyguardFadingAway(boolean keyguardFadingAway)468     public void setKeyguardFadingAway(boolean keyguardFadingAway) {
469         mCurrentState.keyguardFadingAway = keyguardFadingAway;
470         apply(mCurrentState);
471     }
472 
setQsExpanded(boolean expanded)473     public void setQsExpanded(boolean expanded) {
474         mCurrentState.qsExpanded = expanded;
475         apply(mCurrentState);
476     }
477 
setForceUserActivity(boolean forceUserActivity)478     public void setForceUserActivity(boolean forceUserActivity) {
479         mCurrentState.forceUserActivity = forceUserActivity;
480         apply(mCurrentState);
481     }
482 
setScrimsVisibility(int scrimsVisibility)483     public void setScrimsVisibility(int scrimsVisibility) {
484         mCurrentState.scrimsVisibility = scrimsVisibility;
485         apply(mCurrentState);
486     }
487 
setHeadsUpShowing(boolean showing)488     public void setHeadsUpShowing(boolean showing) {
489         mCurrentState.headsUpShowing = showing;
490         apply(mCurrentState);
491     }
492 
setWallpaperSupportsAmbientMode(boolean supportsAmbientMode)493     public void setWallpaperSupportsAmbientMode(boolean supportsAmbientMode) {
494         mCurrentState.wallpaperSupportsAmbientMode = supportsAmbientMode;
495         apply(mCurrentState);
496     }
497 
498     /**
499      * @param state The {@link StatusBarStateController} of the status bar.
500      */
setStatusBarState(int state)501     private void setStatusBarState(int state) {
502         mCurrentState.statusBarState = state;
503         apply(mCurrentState);
504     }
505 
setForceStatusBarVisible(boolean forceStatusBarVisible)506     public void setForceStatusBarVisible(boolean forceStatusBarVisible) {
507         mCurrentState.forceStatusBarVisible = forceStatusBarVisible;
508         apply(mCurrentState);
509     }
510 
511     /**
512      * Force the window to be collapsed, even if it should theoretically be expanded.
513      * Used for when a heads-up comes in but we still need to wait for the touchable regions to
514      * be computed.
515      */
setForceWindowCollapsed(boolean force)516     public void setForceWindowCollapsed(boolean force) {
517         mCurrentState.forceCollapsed = force;
518         apply(mCurrentState);
519     }
520 
setPanelExpanded(boolean isExpanded)521     public void setPanelExpanded(boolean isExpanded) {
522         mCurrentState.panelExpanded = isExpanded;
523         apply(mCurrentState);
524     }
525 
526     @Override
onRemoteInputActive(boolean remoteInputActive)527     public void onRemoteInputActive(boolean remoteInputActive) {
528         mCurrentState.remoteInputActive = remoteInputActive;
529         apply(mCurrentState);
530     }
531 
532     /**
533      * Set whether the screen brightness is forced to the value we use for doze mode by the status
534      * bar window.
535      */
setForceDozeBrightness(boolean forceDozeBrightness)536     public void setForceDozeBrightness(boolean forceDozeBrightness) {
537         mCurrentState.forceDozeBrightness = forceDozeBrightness;
538         apply(mCurrentState);
539     }
540 
setDozing(boolean dozing)541     public void setDozing(boolean dozing) {
542         mCurrentState.dozing = dozing;
543         apply(mCurrentState);
544     }
545 
setBarHeight(int barHeight)546     public void setBarHeight(int barHeight) {
547         mBarHeight = barHeight;
548         apply(mCurrentState);
549     }
550 
setForcePluginOpen(boolean forcePluginOpen)551     public void setForcePluginOpen(boolean forcePluginOpen) {
552         mCurrentState.forcePluginOpen = forcePluginOpen;
553         apply(mCurrentState);
554         if (mForcePluginOpenListener != null) {
555             mForcePluginOpenListener.onChange(forcePluginOpen);
556         }
557     }
558 
559     /**
560      * The forcePluginOpen state for the status bar.
561      */
getForcePluginOpen()562     public boolean getForcePluginOpen() {
563         return mCurrentState.forcePluginOpen;
564     }
565 
setNotTouchable(boolean notTouchable)566     public void setNotTouchable(boolean notTouchable) {
567         mCurrentState.notTouchable = notTouchable;
568         apply(mCurrentState);
569     }
570 
571     /**
572      * Sets whether there are bubbles showing on the screen.
573      */
setBubblesShowing(boolean bubblesShowing)574     public void setBubblesShowing(boolean bubblesShowing) {
575         mCurrentState.bubblesShowing = bubblesShowing;
576         apply(mCurrentState);
577     }
578 
579     /**
580      * The bubbles showing state for the status bar.
581      */
getBubblesShowing()582     public boolean getBubblesShowing() {
583         return mCurrentState.bubblesShowing;
584     }
585 
586     /**
587      * Sets if there is a bubble being expanded on the screen.
588      */
setBubbleExpanded(boolean bubbleExpanded)589     public void setBubbleExpanded(boolean bubbleExpanded) {
590         mCurrentState.bubbleExpanded = bubbleExpanded;
591         apply(mCurrentState);
592     }
593 
594     /**
595      * Whether the bubble is shown in expanded state for the status bar.
596      */
getBubbleExpanded()597     public boolean getBubbleExpanded() {
598         return mCurrentState.bubbleExpanded;
599     }
600 
601     /**
602      * Whether the status bar panel is expanded or not.
603      */
getPanelExpanded()604     public boolean getPanelExpanded() {
605         return mCurrentState.panelExpanded;
606     }
607 
setStateListener(OtherwisedCollapsedListener listener)608     public void setStateListener(OtherwisedCollapsedListener listener) {
609         mListener = listener;
610     }
611 
setForcePluginOpenListener(ForcePluginOpenListener listener)612     public void setForcePluginOpenListener(ForcePluginOpenListener listener) {
613         mForcePluginOpenListener = listener;
614     }
615 
dump(FileDescriptor fd, PrintWriter pw, String[] args)616     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
617         pw.println("StatusBarWindowController:");
618         pw.println("  mKeyguardDisplayMode=" + mKeyguardDisplayMode);
619         pw.println(mCurrentState);
620     }
621 
isShowingWallpaper()622     public boolean isShowingWallpaper() {
623         return !mCurrentState.backdropShowing;
624     }
625 
626     @Override
onThemeChanged()627     public void onThemeChanged() {
628         if (mStatusBarView == null) {
629             return;
630         }
631 
632         final boolean useDarkText = mColorExtractor.getNeutralColors().supportsDarkText();
633         // Make sure we have the correct navbar/statusbar colors.
634         setKeyguardDark(useDarkText);
635     }
636 
637     /**
638      * When keyguard will be dismissed but didn't start animation yet.
639      */
setKeyguardGoingAway(boolean goingAway)640     public void setKeyguardGoingAway(boolean goingAway) {
641         mCurrentState.keyguardGoingAway = goingAway;
642         apply(mCurrentState);
643     }
644 
getForceHasTopUi()645     public boolean getForceHasTopUi() {
646         return mCurrentState.forceHasTopUi;
647     }
648 
setForceHasTopUi(boolean forceHasTopUi)649     public void setForceHasTopUi(boolean forceHasTopUi) {
650         mCurrentState.forceHasTopUi = forceHasTopUi;
651         apply(mCurrentState);
652     }
653 
654     private static class State {
655         boolean keyguardShowing;
656         boolean keyguardOccluded;
657         boolean keyguardNeedsInput;
658         boolean panelVisible;
659         boolean panelExpanded;
660         boolean statusBarFocusable;
661         boolean bouncerShowing;
662         boolean keyguardFadingAway;
663         boolean keyguardGoingAway;
664         boolean qsExpanded;
665         boolean headsUpShowing;
666         boolean forceStatusBarVisible;
667         boolean forceCollapsed;
668         boolean forceDozeBrightness;
669         boolean forceUserActivity;
670         boolean backdropShowing;
671         boolean wallpaperSupportsAmbientMode;
672         boolean notTouchable;
673         boolean bubblesShowing;
674         boolean bubbleExpanded;
675         boolean forceHasTopUi;
676 
677         /**
678          * The {@link StatusBar} state from the status bar.
679          */
680         int statusBarState;
681 
682         boolean remoteInputActive;
683         boolean forcePluginOpen;
684         boolean dozing;
685         int scrimsVisibility;
686 
isKeyguardShowingAndNotOccluded()687         private boolean isKeyguardShowingAndNotOccluded() {
688             return keyguardShowing && !keyguardOccluded;
689         }
690 
691         @Override
toString()692         public String toString() {
693             StringBuilder result = new StringBuilder();
694             String newLine = "\n";
695             result.append("Window State {");
696             result.append(newLine);
697 
698             Field[] fields = this.getClass().getDeclaredFields();
699 
700             // Print field names paired with their values
701             for (Field field : fields) {
702                 result.append("  ");
703                 try {
704                     result.append(field.getName());
705                     result.append(": ");
706                     //requires access to private field:
707                     result.append(field.get(this));
708                 } catch (IllegalAccessException ex) {
709                 }
710                 result.append(newLine);
711             }
712             result.append("}");
713 
714             return result.toString();
715         }
716     }
717 
718     private final StateListener mStateListener = new StateListener() {
719         @Override
720         public void onStateChanged(int newState) {
721             setStatusBarState(newState);
722         }
723 
724         @Override
725         public void onDozingChanged(boolean isDozing) {
726             setDozing(isDozing);
727         }
728     };
729 
730     /**
731      * Custom listener to pipe data back to plugins about whether or not the status bar would be
732      * collapsed if not for the plugin.
733      * TODO: Find cleaner way to do this.
734      */
735     public interface OtherwisedCollapsedListener {
setWouldOtherwiseCollapse(boolean otherwiseCollapse)736         void setWouldOtherwiseCollapse(boolean otherwiseCollapse);
737     }
738 
739     /**
740      * Listener to indicate forcePluginOpen has changed
741      */
742     public interface ForcePluginOpenListener {
743         /**
744          * Called when mState.forcePluginOpen is changed
745          */
onChange(boolean forceOpen)746         void onChange(boolean forceOpen);
747     }
748 }
749