Home | History | Annotate | Download | only in phone
      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 
     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 
     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      */
     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 
    120     public void setDozeScreenBrightness(int value) {
    121         mScreenBrightnessDoze = value / 255f;
    122     }
    123 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    285     private void applyHasTopUi(State state) {
    286         mHasTopUiChanged = isExpanded(state);
    287     }
    288 
    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 
    297     public void setKeyguardShowing(boolean showing) {
    298         mCurrentState.keyguardShowing = showing;
    299         apply(mCurrentState);
    300     }
    301 
    302     public void setKeyguardOccluded(boolean occluded) {
    303         mCurrentState.keyguardOccluded = occluded;
    304         apply(mCurrentState);
    305     }
    306 
    307     public void setKeyguardNeedsInput(boolean needsInput) {
    308         mCurrentState.keyguardNeedsInput = needsInput;
    309         apply(mCurrentState);
    310     }
    311 
    312     public void setPanelVisible(boolean visible) {
    313         mCurrentState.panelVisible = visible;
    314         mCurrentState.statusBarFocusable = visible;
    315         apply(mCurrentState);
    316     }
    317 
    318     public void setStatusBarFocusable(boolean focusable) {
    319         mCurrentState.statusBarFocusable = focusable;
    320         apply(mCurrentState);
    321     }
    322 
    323     public void setBouncerShowing(boolean showing) {
    324         mCurrentState.bouncerShowing = showing;
    325         apply(mCurrentState);
    326     }
    327 
    328     public void setBackdropShowing(boolean showing) {
    329         mCurrentState.backdropShowing = showing;
    330         apply(mCurrentState);
    331     }
    332 
    333     public void setKeyguardFadingAway(boolean keyguardFadingAway) {
    334         mCurrentState.keyguardFadingAway = keyguardFadingAway;
    335         apply(mCurrentState);
    336     }
    337 
    338     public void setQsExpanded(boolean expanded) {
    339         mCurrentState.qsExpanded = expanded;
    340         apply(mCurrentState);
    341     }
    342 
    343     public void setForceUserActivity(boolean forceUserActivity) {
    344         mCurrentState.forceUserActivity = forceUserActivity;
    345         apply(mCurrentState);
    346     }
    347 
    348     public void setScrimsVisibility(int scrimsVisibility) {
    349         mCurrentState.scrimsVisibility = scrimsVisibility;
    350         apply(mCurrentState);
    351     }
    352 
    353     public void setHeadsUpShowing(boolean showing) {
    354         mCurrentState.headsUpShowing = showing;
    355         apply(mCurrentState);
    356     }
    357 
    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      */
    366     public void setStatusBarState(int state) {
    367         mCurrentState.statusBarState = state;
    368         apply(mCurrentState);
    369     }
    370 
    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      */
    381     public void setForceWindowCollapsed(boolean force) {
    382         mCurrentState.forceCollapsed = force;
    383         apply(mCurrentState);
    384     }
    385 
    386     public void setPanelExpanded(boolean isExpanded) {
    387         mCurrentState.panelExpanded = isExpanded;
    388         apply(mCurrentState);
    389     }
    390 
    391     @Override
    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      */
    401     public void setForceDozeBrightness(boolean forceDozeBrightness) {
    402         mCurrentState.forceDozeBrightness = forceDozeBrightness;
    403         apply(mCurrentState);
    404     }
    405 
    406     public void setDozing(boolean dozing) {
    407         mCurrentState.dozing = dozing;
    408         apply(mCurrentState);
    409     }
    410 
    411     public void setBarHeight(int barHeight) {
    412         mBarHeight = barHeight;
    413         apply(mCurrentState);
    414     }
    415 
    416     public void setForcePluginOpen(boolean forcePluginOpen) {
    417         mCurrentState.forcePluginOpen = forcePluginOpen;
    418         apply(mCurrentState);
    419     }
    420 
    421     public void setStateListener(OtherwisedCollapsedListener listener) {
    422         mListener = listener;
    423     }
    424 
    425     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    426         pw.println("StatusBarWindowManager state:");
    427         pw.println(mCurrentState);
    428     }
    429 
    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 
    462         private boolean isKeyguardShowingAndNotOccluded() {
    463             return keyguardShowing && !keyguardOccluded;
    464         }
    465 
    466         @Override
    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 {
    499         void setWouldOtherwiseCollapse(boolean otherwiseCollapse);
    500     }
    501 }
    502