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 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 
     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 
     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      */
     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 
    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 
    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 
    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 
    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 
    170     private boolean isExpanded(State state) {
    171         return !state.forceCollapsed && (state.isKeyguardShowingAndNotOccluded()
    172                 || state.panelVisible || state.keyguardFadingAway || state.bouncerShowing
    173                 || state.headsUpShowing);
    174     }
    175 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    257     private void applyHasTopUi(State state) {
    258         mHasTopUiChanged = isExpanded(state);
    259     }
    260 
    261     public void setKeyguardShowing(boolean showing) {
    262         mCurrentState.keyguardShowing = showing;
    263         apply(mCurrentState);
    264     }
    265 
    266     public void setKeyguardOccluded(boolean occluded) {
    267         mCurrentState.keyguardOccluded = occluded;
    268         apply(mCurrentState);
    269     }
    270 
    271     public void setKeyguardNeedsInput(boolean needsInput) {
    272         mCurrentState.keyguardNeedsInput = needsInput;
    273         apply(mCurrentState);
    274     }
    275 
    276     public void setPanelVisible(boolean visible) {
    277         mCurrentState.panelVisible = visible;
    278         mCurrentState.statusBarFocusable = visible;
    279         apply(mCurrentState);
    280     }
    281 
    282     public void setStatusBarFocusable(boolean focusable) {
    283         mCurrentState.statusBarFocusable = focusable;
    284         apply(mCurrentState);
    285     }
    286 
    287     public void setBouncerShowing(boolean showing) {
    288         mCurrentState.bouncerShowing = showing;
    289         apply(mCurrentState);
    290     }
    291 
    292     public void setBackdropShowing(boolean showing) {
    293         mCurrentState.backdropShowing = showing;
    294         apply(mCurrentState);
    295     }
    296 
    297     public void setKeyguardFadingAway(boolean keyguardFadingAway) {
    298         mCurrentState.keyguardFadingAway = keyguardFadingAway;
    299         apply(mCurrentState);
    300     }
    301 
    302     public void setQsExpanded(boolean expanded) {
    303         mCurrentState.qsExpanded = expanded;
    304         apply(mCurrentState);
    305     }
    306 
    307     public void setForceUserActivity(boolean forceUserActivity) {
    308         mCurrentState.forceUserActivity = forceUserActivity;
    309         apply(mCurrentState);
    310     }
    311 
    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      */
    320     public void setStatusBarState(int state) {
    321         mCurrentState.statusBarState = state;
    322         apply(mCurrentState);
    323     }
    324 
    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      */
    335     public void setForceWindowCollapsed(boolean force) {
    336         mCurrentState.forceCollapsed = force;
    337         apply(mCurrentState);
    338     }
    339 
    340     public void setPanelExpanded(boolean isExpanded) {
    341         mCurrentState.panelExpanded = isExpanded;
    342         apply(mCurrentState);
    343     }
    344 
    345     @Override
    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      */
    355     public void setForceDozeBrightness(boolean forceDozeBrightness) {
    356         mCurrentState.forceDozeBrightness = forceDozeBrightness;
    357         apply(mCurrentState);
    358     }
    359 
    360     public void setDozing(boolean dozing) {
    361         mCurrentState.dozing = dozing;
    362         apply(mCurrentState);
    363     }
    364 
    365     public void setBarHeight(int barHeight) {
    366         mBarHeight = barHeight;
    367         apply(mCurrentState);
    368     }
    369 
    370     public void setForcePluginOpen(boolean forcePluginOpen) {
    371         mCurrentState.forcePluginOpen = forcePluginOpen;
    372         apply(mCurrentState);
    373     }
    374 
    375     public void setStateListener(OtherwisedCollapsedListener listener) {
    376         mListener = listener;
    377     }
    378 
    379     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    380         pw.println("StatusBarWindowManager state:");
    381         pw.println(mCurrentState);
    382     }
    383 
    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 
    414         private boolean isKeyguardShowingAndNotOccluded() {
    415             return keyguardShowing && !keyguardOccluded;
    416         }
    417 
    418         @Override
    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 {
    451         void setWouldOtherwiseCollapse(boolean otherwiseCollapse);
    452     }
    453 }
    454