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