/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.policy; import android.app.StatusBarManager; import android.os.Handler; import android.os.Message; import android.os.SystemClock; import android.util.Slog; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.view.WindowManagerPolicy.WindowState; import com.android.server.LocalServices; import com.android.server.statusbar.StatusBarManagerInternal; import java.io.PrintWriter; /** * Controls state/behavior specific to a system bar window. */ public class BarController { private static final boolean DEBUG = false; private static final int TRANSIENT_BAR_NONE = 0; private static final int TRANSIENT_BAR_SHOW_REQUESTED = 1; private static final int TRANSIENT_BAR_SHOWING = 2; private static final int TRANSIENT_BAR_HIDING = 3; private static final int TRANSLUCENT_ANIMATION_DELAY_MS = 1000; private static final int MSG_NAV_BAR_VISIBILITY_CHANGED = 1; protected final String mTag; private final int mTransientFlag; private final int mUnhideFlag; private final int mTranslucentFlag; private final int mTransparentFlag; private final int mStatusBarManagerId; private final int mTranslucentWmFlag; protected final Handler mHandler; private final Object mServiceAquireLock = new Object(); protected StatusBarManagerInternal mStatusBarInternal; protected WindowState mWin; private int mState = StatusBarManager.WINDOW_STATE_SHOWING; private int mTransientBarState; private boolean mPendingShow; private long mLastTranslucent; private boolean mShowTransparent; private boolean mSetUnHideFlagWhenNextTransparent; private boolean mNoAnimationOnNextShow; private OnBarVisibilityChangedListener mVisibilityChangeListener; public BarController(String tag, int transientFlag, int unhideFlag, int translucentFlag, int statusBarManagerId, int translucentWmFlag, int transparentFlag) { mTag = "BarController." + tag; mTransientFlag = transientFlag; mUnhideFlag = unhideFlag; mTranslucentFlag = translucentFlag; mStatusBarManagerId = statusBarManagerId; mTranslucentWmFlag = translucentWmFlag; mTransparentFlag = transparentFlag; mHandler = new BarHandler(); } public void setWindow(WindowState win) { mWin = win; } public void setShowTransparent(boolean transparent) { if (transparent != mShowTransparent) { mShowTransparent = transparent; mSetUnHideFlagWhenNextTransparent = transparent; mNoAnimationOnNextShow = true; } } public void showTransient() { if (mWin != null) { setTransientBarState(TRANSIENT_BAR_SHOW_REQUESTED); } } public boolean isTransientShowing() { return mTransientBarState == TRANSIENT_BAR_SHOWING; } public boolean isTransientShowRequested() { return mTransientBarState == TRANSIENT_BAR_SHOW_REQUESTED; } public boolean wasRecentlyTranslucent() { return (SystemClock.uptimeMillis() - mLastTranslucent) < TRANSLUCENT_ANIMATION_DELAY_MS; } public void adjustSystemUiVisibilityLw(int oldVis, int vis) { if (mWin != null && mTransientBarState == TRANSIENT_BAR_SHOWING && (vis & mTransientFlag) == 0) { // sysui requests hide setTransientBarState(TRANSIENT_BAR_HIDING); setBarShowingLw(false); } else if (mWin != null && (oldVis & mUnhideFlag) != 0 && (vis & mUnhideFlag) == 0) { // sysui ready to unhide setBarShowingLw(true); } } public int applyTranslucentFlagLw(WindowState win, int vis, int oldVis) { if (mWin != null) { if (win != null && (win.getAttrs().privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR) == 0) { int fl = PolicyControl.getWindowFlags(win, null); if ((fl & mTranslucentWmFlag) != 0) { vis |= mTranslucentFlag; } else { vis &= ~mTranslucentFlag; } if ((fl & WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0) { vis |= mTransparentFlag; } else { vis &= ~mTransparentFlag; } } else { vis = (vis & ~mTranslucentFlag) | (oldVis & mTranslucentFlag); vis = (vis & ~mTransparentFlag) | (oldVis & mTransparentFlag); } } return vis; } public boolean setBarShowingLw(final boolean show) { if (mWin == null) return false; if (show && mTransientBarState == TRANSIENT_BAR_HIDING) { mPendingShow = true; return false; } final boolean wasVis = mWin.isVisibleLw(); final boolean wasAnim = mWin.isAnimatingLw(); final boolean change = show ? mWin.showLw(!mNoAnimationOnNextShow && !skipAnimation()) : mWin.hideLw(!mNoAnimationOnNextShow && !skipAnimation()); mNoAnimationOnNextShow = false; final int state = computeStateLw(wasVis, wasAnim, mWin, change); final boolean stateChanged = updateStateLw(state); if (change && (mVisibilityChangeListener != null)) { mHandler.obtainMessage(MSG_NAV_BAR_VISIBILITY_CHANGED, show ? 1 : 0, 0).sendToTarget(); } return change || stateChanged; } void setOnBarVisibilityChangedListener(OnBarVisibilityChangedListener listener, boolean invokeWithState) { mVisibilityChangeListener = listener; if (invokeWithState) { // Optionally report the initial window state for initialization purposes mHandler.obtainMessage(MSG_NAV_BAR_VISIBILITY_CHANGED, (mState == StatusBarManager.WINDOW_STATE_SHOWING) ? 1 : 0, 0).sendToTarget(); } } protected boolean skipAnimation() { return false; } private int computeStateLw(boolean wasVis, boolean wasAnim, WindowState win, boolean change) { if (win.isDrawnLw()) { final boolean vis = win.isVisibleLw(); final boolean anim = win.isAnimatingLw(); if (mState == StatusBarManager.WINDOW_STATE_HIDING && !change && !vis) { return StatusBarManager.WINDOW_STATE_HIDDEN; } else if (mState == StatusBarManager.WINDOW_STATE_HIDDEN && vis) { return StatusBarManager.WINDOW_STATE_SHOWING; } else if (change) { if (wasVis && vis && !wasAnim && anim) { return StatusBarManager.WINDOW_STATE_HIDING; } else { return StatusBarManager.WINDOW_STATE_SHOWING; } } } return mState; } private boolean updateStateLw(final int state) { if (state != mState) { mState = state; if (DEBUG) Slog.d(mTag, "mState: " + StatusBarManager.windowStateToString(state)); mHandler.post(new Runnable() { @Override public void run() { StatusBarManagerInternal statusbar = getStatusBarInternal(); if (statusbar != null) { statusbar.setWindowState(mStatusBarManagerId, state); } } }); return true; } return false; } public boolean checkHiddenLw() { if (mWin != null && mWin.isDrawnLw()) { if (!mWin.isVisibleLw() && !mWin.isAnimatingLw()) { updateStateLw(StatusBarManager.WINDOW_STATE_HIDDEN); } if (mTransientBarState == TRANSIENT_BAR_HIDING && !mWin.isVisibleLw()) { // Finished animating out, clean up and reset style setTransientBarState(TRANSIENT_BAR_NONE); if (mPendingShow) { setBarShowingLw(true); mPendingShow = false; } return true; } } return false; } public boolean checkShowTransientBarLw() { if (mTransientBarState == TRANSIENT_BAR_SHOWING) { if (DEBUG) Slog.d(mTag, "Not showing transient bar, already shown"); return false; } else if (mTransientBarState == TRANSIENT_BAR_SHOW_REQUESTED) { if (DEBUG) Slog.d(mTag, "Not showing transient bar, already requested"); return false; } else if (mWin == null) { if (DEBUG) Slog.d(mTag, "Not showing transient bar, bar doesn't exist"); return false; } else if (mWin.isDisplayedLw()) { if (DEBUG) Slog.d(mTag, "Not showing transient bar, bar already visible"); return false; } else { return true; } } public int updateVisibilityLw(boolean transientAllowed, int oldVis, int vis) { if (mWin == null) return vis; if (isTransientShowing() || isTransientShowRequested()) { // transient bar requested if (transientAllowed) { vis |= mTransientFlag; if ((oldVis & mTransientFlag) == 0) { vis |= mUnhideFlag; // tell sysui we're ready to unhide } setTransientBarState(TRANSIENT_BAR_SHOWING); // request accepted } else { setTransientBarState(TRANSIENT_BAR_NONE); // request denied } } if (mShowTransparent) { vis |= mTransparentFlag; if (mSetUnHideFlagWhenNextTransparent) { vis |= mUnhideFlag; mSetUnHideFlagWhenNextTransparent = false; } } if (mTransientBarState != TRANSIENT_BAR_NONE) { vis |= mTransientFlag; // ignore clear requests until transition completes vis &= ~View.SYSTEM_UI_FLAG_LOW_PROFILE; // never show transient bars in low profile } if ((vis & mTranslucentFlag) != 0 || (oldVis & mTranslucentFlag) != 0 || ((vis | oldVis) & mTransparentFlag) != 0) { mLastTranslucent = SystemClock.uptimeMillis(); } return vis; } private void setTransientBarState(int state) { if (mWin != null && state != mTransientBarState) { if (mTransientBarState == TRANSIENT_BAR_SHOWING || state == TRANSIENT_BAR_SHOWING) { mLastTranslucent = SystemClock.uptimeMillis(); } mTransientBarState = state; if (DEBUG) Slog.d(mTag, "mTransientBarState: " + transientBarStateToString(state)); } } protected StatusBarManagerInternal getStatusBarInternal() { synchronized (mServiceAquireLock) { if (mStatusBarInternal == null) { mStatusBarInternal = LocalServices.getService(StatusBarManagerInternal.class); } return mStatusBarInternal; } } private static String transientBarStateToString(int state) { if (state == TRANSIENT_BAR_HIDING) return "TRANSIENT_BAR_HIDING"; if (state == TRANSIENT_BAR_SHOWING) return "TRANSIENT_BAR_SHOWING"; if (state == TRANSIENT_BAR_SHOW_REQUESTED) return "TRANSIENT_BAR_SHOW_REQUESTED"; if (state == TRANSIENT_BAR_NONE) return "TRANSIENT_BAR_NONE"; throw new IllegalArgumentException("Unknown state " + state); } public void dump(PrintWriter pw, String prefix) { if (mWin != null) { pw.print(prefix); pw.println(mTag); pw.print(prefix); pw.print(" "); pw.print("mState"); pw.print('='); pw.println(StatusBarManager.windowStateToString(mState)); pw.print(prefix); pw.print(" "); pw.print("mTransientBar"); pw.print('='); pw.println(transientBarStateToString(mTransientBarState)); } } private class BarHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_NAV_BAR_VISIBILITY_CHANGED: final boolean visible = msg.arg1 != 0; if (mVisibilityChangeListener != null) { mVisibilityChangeListener.onBarVisibilityChanged(visible); } break; } } } interface OnBarVisibilityChangedListener { void onBarVisibilityChanged(boolean visible); } }