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