Home | History | Annotate | Download | only in policy
      1 /*
      2  * Copyright (C) 2013 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.server.policy;
     18 
     19 import android.app.StatusBarManager;
     20 import android.os.Handler;
     21 import android.os.Message;
     22 import android.os.SystemClock;
     23 import android.util.Slog;
     24 import android.view.View;
     25 import android.view.ViewGroup;
     26 import android.view.WindowManager;
     27 import android.view.WindowManagerPolicy.WindowState;
     28 
     29 import com.android.server.LocalServices;
     30 import com.android.server.statusbar.StatusBarManagerInternal;
     31 
     32 import java.io.PrintWriter;
     33 
     34 /**
     35  * Controls state/behavior specific to a system bar window.
     36  */
     37 public class BarController {
     38     private static final boolean DEBUG = false;
     39 
     40     private static final int TRANSIENT_BAR_NONE = 0;
     41     private static final int TRANSIENT_BAR_SHOW_REQUESTED = 1;
     42     private static final int TRANSIENT_BAR_SHOWING = 2;
     43     private static final int TRANSIENT_BAR_HIDING = 3;
     44 
     45     private static final int TRANSLUCENT_ANIMATION_DELAY_MS = 1000;
     46 
     47     private static final int MSG_NAV_BAR_VISIBILITY_CHANGED = 1;
     48 
     49     protected final String mTag;
     50     private final int mTransientFlag;
     51     private final int mUnhideFlag;
     52     private final int mTranslucentFlag;
     53     private final int mTransparentFlag;
     54     private final int mStatusBarManagerId;
     55     private final int mTranslucentWmFlag;
     56     protected final Handler mHandler;
     57     private final Object mServiceAquireLock = new Object();
     58     protected StatusBarManagerInternal mStatusBarInternal;
     59 
     60     protected WindowState mWin;
     61     private int mState = StatusBarManager.WINDOW_STATE_SHOWING;
     62     private int mTransientBarState;
     63     private boolean mPendingShow;
     64     private long mLastTranslucent;
     65     private boolean mShowTransparent;
     66     private boolean mSetUnHideFlagWhenNextTransparent;
     67     private boolean mNoAnimationOnNextShow;
     68 
     69     private OnBarVisibilityChangedListener mVisibilityChangeListener;
     70 
     71     public BarController(String tag, int transientFlag, int unhideFlag, int translucentFlag,
     72             int statusBarManagerId, int translucentWmFlag, int transparentFlag) {
     73         mTag = "BarController." + tag;
     74         mTransientFlag = transientFlag;
     75         mUnhideFlag = unhideFlag;
     76         mTranslucentFlag = translucentFlag;
     77         mStatusBarManagerId = statusBarManagerId;
     78         mTranslucentWmFlag = translucentWmFlag;
     79         mTransparentFlag = transparentFlag;
     80         mHandler = new BarHandler();
     81     }
     82 
     83     public void setWindow(WindowState win) {
     84         mWin = win;
     85     }
     86 
     87     public void setShowTransparent(boolean transparent) {
     88         if (transparent != mShowTransparent) {
     89             mShowTransparent = transparent;
     90             mSetUnHideFlagWhenNextTransparent = transparent;
     91             mNoAnimationOnNextShow = true;
     92         }
     93     }
     94 
     95     public void showTransient() {
     96         if (mWin != null) {
     97             setTransientBarState(TRANSIENT_BAR_SHOW_REQUESTED);
     98         }
     99     }
    100 
    101     public boolean isTransientShowing() {
    102         return mTransientBarState == TRANSIENT_BAR_SHOWING;
    103     }
    104 
    105     public boolean isTransientShowRequested() {
    106         return mTransientBarState == TRANSIENT_BAR_SHOW_REQUESTED;
    107     }
    108 
    109     public boolean wasRecentlyTranslucent() {
    110         return (SystemClock.uptimeMillis() - mLastTranslucent) < TRANSLUCENT_ANIMATION_DELAY_MS;
    111     }
    112 
    113     public void adjustSystemUiVisibilityLw(int oldVis, int vis) {
    114         if (mWin != null && mTransientBarState == TRANSIENT_BAR_SHOWING &&
    115                 (vis & mTransientFlag) == 0) {
    116             // sysui requests hide
    117             setTransientBarState(TRANSIENT_BAR_HIDING);
    118             setBarShowingLw(false);
    119         } else if (mWin != null && (oldVis & mUnhideFlag) != 0 && (vis & mUnhideFlag) == 0) {
    120             // sysui ready to unhide
    121             setBarShowingLw(true);
    122         }
    123     }
    124 
    125     public int applyTranslucentFlagLw(WindowState win, int vis, int oldVis) {
    126         if (mWin != null) {
    127             if (win != null && (win.getAttrs().privateFlags
    128                     & WindowManager.LayoutParams.PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR) == 0) {
    129                 int fl = PolicyControl.getWindowFlags(win, null);
    130                 if ((fl & mTranslucentWmFlag) != 0) {
    131                     vis |= mTranslucentFlag;
    132                 } else {
    133                     vis &= ~mTranslucentFlag;
    134                 }
    135                 if ((fl & WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0) {
    136                     vis |= mTransparentFlag;
    137                 } else {
    138                     vis &= ~mTransparentFlag;
    139                 }
    140             } else {
    141                 vis = (vis & ~mTranslucentFlag) | (oldVis & mTranslucentFlag);
    142                 vis = (vis & ~mTransparentFlag) | (oldVis & mTransparentFlag);
    143             }
    144         }
    145         return vis;
    146     }
    147 
    148     public boolean setBarShowingLw(final boolean show) {
    149         if (mWin == null) return false;
    150         if (show && mTransientBarState == TRANSIENT_BAR_HIDING) {
    151             mPendingShow = true;
    152             return false;
    153         }
    154         final boolean wasVis = mWin.isVisibleLw();
    155         final boolean wasAnim = mWin.isAnimatingLw();
    156         final boolean change = show ? mWin.showLw(!mNoAnimationOnNextShow && !skipAnimation())
    157                 : mWin.hideLw(!mNoAnimationOnNextShow && !skipAnimation());
    158         mNoAnimationOnNextShow = false;
    159         final int state = computeStateLw(wasVis, wasAnim, mWin, change);
    160         final boolean stateChanged = updateStateLw(state);
    161 
    162         if (change && (mVisibilityChangeListener != null)) {
    163             mHandler.obtainMessage(MSG_NAV_BAR_VISIBILITY_CHANGED, show ? 1 : 0, 0).sendToTarget();
    164         }
    165 
    166         return change || stateChanged;
    167     }
    168 
    169     void setOnBarVisibilityChangedListener(OnBarVisibilityChangedListener listener,
    170             boolean invokeWithState) {
    171         mVisibilityChangeListener = listener;
    172         if (invokeWithState) {
    173             // Optionally report the initial window state for initialization purposes
    174             mHandler.obtainMessage(MSG_NAV_BAR_VISIBILITY_CHANGED,
    175                     (mState == StatusBarManager.WINDOW_STATE_SHOWING) ? 1 : 0, 0).sendToTarget();
    176         }
    177     }
    178 
    179     protected boolean skipAnimation() {
    180         return false;
    181     }
    182 
    183     private int computeStateLw(boolean wasVis, boolean wasAnim, WindowState win, boolean change) {
    184         if (win.isDrawnLw()) {
    185             final boolean vis = win.isVisibleLw();
    186             final boolean anim = win.isAnimatingLw();
    187             if (mState == StatusBarManager.WINDOW_STATE_HIDING && !change && !vis) {
    188                 return StatusBarManager.WINDOW_STATE_HIDDEN;
    189             } else if (mState == StatusBarManager.WINDOW_STATE_HIDDEN && vis) {
    190                 return StatusBarManager.WINDOW_STATE_SHOWING;
    191             } else if (change) {
    192                 if (wasVis && vis && !wasAnim && anim) {
    193                     return StatusBarManager.WINDOW_STATE_HIDING;
    194                 } else {
    195                     return StatusBarManager.WINDOW_STATE_SHOWING;
    196                 }
    197             }
    198         }
    199         return mState;
    200     }
    201 
    202     private boolean updateStateLw(final int state) {
    203         if (state != mState) {
    204             mState = state;
    205             if (DEBUG) Slog.d(mTag, "mState: " + StatusBarManager.windowStateToString(state));
    206             mHandler.post(new Runnable() {
    207                 @Override
    208                 public void run() {
    209                     StatusBarManagerInternal statusbar = getStatusBarInternal();
    210                     if (statusbar != null) {
    211                         statusbar.setWindowState(mStatusBarManagerId, state);
    212                     }
    213                 }
    214             });
    215             return true;
    216         }
    217         return false;
    218     }
    219 
    220     public boolean checkHiddenLw() {
    221         if (mWin != null && mWin.isDrawnLw()) {
    222             if (!mWin.isVisibleLw() && !mWin.isAnimatingLw()) {
    223                 updateStateLw(StatusBarManager.WINDOW_STATE_HIDDEN);
    224             }
    225             if (mTransientBarState == TRANSIENT_BAR_HIDING && !mWin.isVisibleLw()) {
    226                 // Finished animating out, clean up and reset style
    227                 setTransientBarState(TRANSIENT_BAR_NONE);
    228                 if (mPendingShow) {
    229                     setBarShowingLw(true);
    230                     mPendingShow = false;
    231                 }
    232                 return true;
    233             }
    234         }
    235         return false;
    236     }
    237 
    238     public boolean checkShowTransientBarLw() {
    239         if (mTransientBarState == TRANSIENT_BAR_SHOWING) {
    240             if (DEBUG) Slog.d(mTag, "Not showing transient bar, already shown");
    241             return false;
    242         } else if (mTransientBarState == TRANSIENT_BAR_SHOW_REQUESTED) {
    243             if (DEBUG) Slog.d(mTag, "Not showing transient bar, already requested");
    244             return false;
    245         } else if (mWin == null) {
    246             if (DEBUG) Slog.d(mTag, "Not showing transient bar, bar doesn't exist");
    247             return false;
    248         } else if (mWin.isDisplayedLw()) {
    249             if (DEBUG) Slog.d(mTag, "Not showing transient bar, bar already visible");
    250             return false;
    251         } else {
    252             return true;
    253         }
    254     }
    255 
    256     public int updateVisibilityLw(boolean transientAllowed, int oldVis, int vis) {
    257         if (mWin == null) return vis;
    258         if (isTransientShowing() || isTransientShowRequested()) { // transient bar requested
    259             if (transientAllowed) {
    260                 vis |= mTransientFlag;
    261                 if ((oldVis & mTransientFlag) == 0) {
    262                     vis |= mUnhideFlag;  // tell sysui we're ready to unhide
    263                 }
    264                 setTransientBarState(TRANSIENT_BAR_SHOWING);  // request accepted
    265             } else {
    266                 setTransientBarState(TRANSIENT_BAR_NONE);  // request denied
    267             }
    268         }
    269         if (mShowTransparent) {
    270             vis |= mTransparentFlag;
    271             if (mSetUnHideFlagWhenNextTransparent) {
    272                 vis |= mUnhideFlag;
    273                 mSetUnHideFlagWhenNextTransparent = false;
    274             }
    275         }
    276         if (mTransientBarState != TRANSIENT_BAR_NONE) {
    277             vis |= mTransientFlag;  // ignore clear requests until transition completes
    278             vis &= ~View.SYSTEM_UI_FLAG_LOW_PROFILE;  // never show transient bars in low profile
    279         }
    280         if ((vis & mTranslucentFlag) != 0 || (oldVis & mTranslucentFlag) != 0 ||
    281                 ((vis | oldVis) & mTransparentFlag) != 0) {
    282             mLastTranslucent = SystemClock.uptimeMillis();
    283         }
    284         return vis;
    285     }
    286 
    287     private void setTransientBarState(int state) {
    288         if (mWin != null && state != mTransientBarState) {
    289             if (mTransientBarState == TRANSIENT_BAR_SHOWING || state == TRANSIENT_BAR_SHOWING) {
    290                 mLastTranslucent = SystemClock.uptimeMillis();
    291             }
    292             mTransientBarState = state;
    293             if (DEBUG) Slog.d(mTag, "mTransientBarState: " + transientBarStateToString(state));
    294         }
    295     }
    296 
    297     protected StatusBarManagerInternal getStatusBarInternal() {
    298         synchronized (mServiceAquireLock) {
    299             if (mStatusBarInternal == null) {
    300                 mStatusBarInternal = LocalServices.getService(StatusBarManagerInternal.class);
    301             }
    302             return mStatusBarInternal;
    303         }
    304     }
    305 
    306     private static String transientBarStateToString(int state) {
    307         if (state == TRANSIENT_BAR_HIDING) return "TRANSIENT_BAR_HIDING";
    308         if (state == TRANSIENT_BAR_SHOWING) return "TRANSIENT_BAR_SHOWING";
    309         if (state == TRANSIENT_BAR_SHOW_REQUESTED) return "TRANSIENT_BAR_SHOW_REQUESTED";
    310         if (state == TRANSIENT_BAR_NONE) return "TRANSIENT_BAR_NONE";
    311         throw new IllegalArgumentException("Unknown state " + state);
    312     }
    313 
    314     public void dump(PrintWriter pw, String prefix) {
    315         if (mWin != null) {
    316             pw.print(prefix); pw.println(mTag);
    317             pw.print(prefix); pw.print("  "); pw.print("mState"); pw.print('=');
    318             pw.println(StatusBarManager.windowStateToString(mState));
    319             pw.print(prefix); pw.print("  "); pw.print("mTransientBar"); pw.print('=');
    320             pw.println(transientBarStateToString(mTransientBarState));
    321         }
    322     }
    323 
    324     private class BarHandler extends Handler {
    325         @Override
    326         public void handleMessage(Message msg) {
    327             switch (msg.what) {
    328                 case MSG_NAV_BAR_VISIBILITY_CHANGED:
    329                     final boolean visible = msg.arg1 != 0;
    330                     if (mVisibilityChangeListener != null) {
    331                         mVisibilityChangeListener.onBarVisibilityChanged(visible);
    332                     }
    333                     break;
    334             }
    335         }
    336     }
    337 
    338     interface OnBarVisibilityChangedListener {
    339         void onBarVisibilityChanged(boolean visible);
    340     }
    341 }
    342