/* * Copyright (C) 2014 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.systemui.statusbar.phone; import android.app.ActivityManager; import android.app.IActivityManager; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.res.Resources; import android.graphics.PixelFormat; import android.os.Binder; import android.os.RemoteException; import android.os.SystemProperties; import android.util.Log; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.view.WindowManager.LayoutParams; import com.android.keyguard.R; import com.android.systemui.Dumpable; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.StatusBarState; import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.reflect.Field; /** * Encapsulates all logic for the status bar window state management. */ public class StatusBarWindowManager implements RemoteInputController.Callback, Dumpable { private static final String TAG = "StatusBarWindowManager"; private final Context mContext; private final WindowManager mWindowManager; private final IActivityManager mActivityManager; private View mStatusBarView; private WindowManager.LayoutParams mLp; private WindowManager.LayoutParams mLpChanged; private boolean mHasTopUi; private boolean mHasTopUiChanged; private int mBarHeight; private final boolean mKeyguardScreenRotation; private float mScreenBrightnessDoze; private final State mCurrentState = new State(); private OtherwisedCollapsedListener mListener; public StatusBarWindowManager(Context context) { mContext = context; mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); mActivityManager = ActivityManager.getService(); mKeyguardScreenRotation = shouldEnableKeyguardScreenRotation(); mScreenBrightnessDoze = mContext.getResources().getInteger( com.android.internal.R.integer.config_screenBrightnessDoze) / 255f; } private boolean shouldEnableKeyguardScreenRotation() { Resources res = mContext.getResources(); return SystemProperties.getBoolean("lockscreen.rot_override", false) || res.getBoolean(R.bool.config_enableLockScreenRotation); } /** * Adds the status bar view to the window manager. * * @param statusBarView The view to add. * @param barHeight The height of the status bar in collapsed state. */ public void add(View statusBarView, int barHeight) { // Now that the status bar window encompasses the sliding panel and its // translucent backdrop, the entire thing is made TRANSLUCENT and is // hardware-accelerated. mLp = new WindowManager.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, barHeight, WindowManager.LayoutParams.TYPE_STATUS_BAR, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, PixelFormat.TRANSLUCENT); mLp.token = new Binder(); mLp.gravity = Gravity.TOP; mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; mLp.setTitle("StatusBar"); mLp.packageName = mContext.getPackageName(); mStatusBarView = statusBarView; mBarHeight = barHeight; mWindowManager.addView(mStatusBarView, mLp); mLpChanged = new WindowManager.LayoutParams(); mLpChanged.copyFrom(mLp); } public void setDozeScreenBrightness(int value) { mScreenBrightnessDoze = value / 255f; } public void setKeyguardDark(boolean dark) { int vis = mStatusBarView.getSystemUiVisibility(); if (dark) { vis = vis | View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; vis = vis | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; } else { vis = vis & ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; vis = vis & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; } mStatusBarView.setSystemUiVisibility(vis); } private void applyKeyguardFlags(State state) { if (state.keyguardShowing) { mLpChanged.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; } else { mLpChanged.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; } if (state.keyguardShowing && !state.backdropShowing && !state.dozing) { mLpChanged.flags |= WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; } else { mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; } } private void adjustScreenOrientation(State state) { if (state.isKeyguardShowingAndNotOccluded() || state.dozing) { if (mKeyguardScreenRotation) { mLpChanged.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_USER; } else { mLpChanged.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; } } else { mLpChanged.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; } } private void applyFocusableFlag(State state) { boolean panelFocusable = state.statusBarFocusable && state.panelExpanded; if (state.bouncerShowing && (state.keyguardOccluded || state.keyguardNeedsInput) || StatusBar.ENABLE_REMOTE_INPUT && state.remoteInputActive) { mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; } else if (state.isKeyguardShowingAndNotOccluded() || panelFocusable) { mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; mLpChanged.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; } else { mLpChanged.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; } mLpChanged.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; } private void applyHeight(State state) { boolean expanded = isExpanded(state); if (state.forcePluginOpen) { mListener.setWouldOtherwiseCollapse(expanded); expanded = true; } if (expanded) { mLpChanged.height = ViewGroup.LayoutParams.MATCH_PARENT; } else { mLpChanged.height = mBarHeight; } } private boolean isExpanded(State state) { return !state.forceCollapsed && (state.isKeyguardShowingAndNotOccluded() || state.panelVisible || state.keyguardFadingAway || state.bouncerShowing || state.headsUpShowing || state.scrimsVisible); } private void applyFitsSystemWindows(State state) { boolean fitsSystemWindows = !state.isKeyguardShowingAndNotOccluded(); if (mStatusBarView.getFitsSystemWindows() != fitsSystemWindows) { mStatusBarView.setFitsSystemWindows(fitsSystemWindows); mStatusBarView.requestApplyInsets(); } } private void applyUserActivityTimeout(State state) { if (state.isKeyguardShowingAndNotOccluded() && state.statusBarState == StatusBarState.KEYGUARD && !state.qsExpanded) { mLpChanged.userActivityTimeout = KeyguardViewMediator.AWAKE_INTERVAL_DEFAULT_MS; } else { mLpChanged.userActivityTimeout = -1; } } private void applyInputFeatures(State state) { if (state.isKeyguardShowingAndNotOccluded() && state.statusBarState == StatusBarState.KEYGUARD && !state.qsExpanded && !state.forceUserActivity) { mLpChanged.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY; } else { mLpChanged.inputFeatures &= ~WindowManager.LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY; } } private void apply(State state) { applyKeyguardFlags(state); applyForceStatusBarVisibleFlag(state); applyFocusableFlag(state); adjustScreenOrientation(state); applyHeight(state); applyUserActivityTimeout(state); applyInputFeatures(state); applyFitsSystemWindows(state); applyModalFlag(state); applyBrightness(state); applyHasTopUi(state); applySleepToken(state); if (mLp.copyFrom(mLpChanged) != 0) { mWindowManager.updateViewLayout(mStatusBarView, mLp); } if (mHasTopUi != mHasTopUiChanged) { try { mActivityManager.setHasTopUi(mHasTopUiChanged); } catch (RemoteException e) { Log.e(TAG, "Failed to call setHasTopUi", e); } mHasTopUi = mHasTopUiChanged; } } private void applyForceStatusBarVisibleFlag(State state) { if (state.forceStatusBarVisible) { mLpChanged.privateFlags |= WindowManager .LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT; } else { mLpChanged.privateFlags &= ~WindowManager .LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT; } } private void applyModalFlag(State state) { if (state.headsUpShowing) { mLpChanged.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; } else { mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; } } private void applyBrightness(State state) { if (state.forceDozeBrightness) { mLpChanged.screenBrightness = mScreenBrightnessDoze; } else { mLpChanged.screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE; } } private void applyHasTopUi(State state) { mHasTopUiChanged = isExpanded(state); } private void applySleepToken(State state) { if (state.dozing) { mLpChanged.privateFlags |= LayoutParams.PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN; } else { mLpChanged.privateFlags &= ~LayoutParams.PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN; } } public void setKeyguardShowing(boolean showing) { mCurrentState.keyguardShowing = showing; apply(mCurrentState); } public void setKeyguardOccluded(boolean occluded) { mCurrentState.keyguardOccluded = occluded; apply(mCurrentState); } public void setKeyguardNeedsInput(boolean needsInput) { mCurrentState.keyguardNeedsInput = needsInput; apply(mCurrentState); } public void setPanelVisible(boolean visible) { mCurrentState.panelVisible = visible; mCurrentState.statusBarFocusable = visible; apply(mCurrentState); } public void setStatusBarFocusable(boolean focusable) { mCurrentState.statusBarFocusable = focusable; apply(mCurrentState); } public void setBouncerShowing(boolean showing) { mCurrentState.bouncerShowing = showing; apply(mCurrentState); } public void setBackdropShowing(boolean showing) { mCurrentState.backdropShowing = showing; apply(mCurrentState); } public void setKeyguardFadingAway(boolean keyguardFadingAway) { mCurrentState.keyguardFadingAway = keyguardFadingAway; apply(mCurrentState); } public void setQsExpanded(boolean expanded) { mCurrentState.qsExpanded = expanded; apply(mCurrentState); } public void setForceUserActivity(boolean forceUserActivity) { mCurrentState.forceUserActivity = forceUserActivity; apply(mCurrentState); } public void setScrimsVisible(boolean scrimsVisible) { mCurrentState.scrimsVisible = scrimsVisible; apply(mCurrentState); } public void setHeadsUpShowing(boolean showing) { mCurrentState.headsUpShowing = showing; apply(mCurrentState); } /** * @param state The {@link StatusBarState} of the status bar. */ public void setStatusBarState(int state) { mCurrentState.statusBarState = state; apply(mCurrentState); } public void setForceStatusBarVisible(boolean forceStatusBarVisible) { mCurrentState.forceStatusBarVisible = forceStatusBarVisible; apply(mCurrentState); } /** * Force the window to be collapsed, even if it should theoretically be expanded. * Used for when a heads-up comes in but we still need to wait for the touchable regions to * be computed. */ public void setForceWindowCollapsed(boolean force) { mCurrentState.forceCollapsed = force; apply(mCurrentState); } public void setPanelExpanded(boolean isExpanded) { mCurrentState.panelExpanded = isExpanded; apply(mCurrentState); } @Override public void onRemoteInputActive(boolean remoteInputActive) { mCurrentState.remoteInputActive = remoteInputActive; apply(mCurrentState); } /** * Set whether the screen brightness is forced to the value we use for doze mode by the status * bar window. */ public void setForceDozeBrightness(boolean forceDozeBrightness) { mCurrentState.forceDozeBrightness = forceDozeBrightness; apply(mCurrentState); } public void setDozing(boolean dozing) { mCurrentState.dozing = dozing; apply(mCurrentState); } public void setBarHeight(int barHeight) { mBarHeight = barHeight; apply(mCurrentState); } public void setForcePluginOpen(boolean forcePluginOpen) { mCurrentState.forcePluginOpen = forcePluginOpen; apply(mCurrentState); } public void setStateListener(OtherwisedCollapsedListener listener) { mListener = listener; } public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("StatusBarWindowManager state:"); pw.println(mCurrentState); } public boolean isShowingWallpaper() { return !mCurrentState.backdropShowing; } private static class State { boolean keyguardShowing; boolean keyguardOccluded; boolean keyguardNeedsInput; boolean panelVisible; boolean panelExpanded; boolean statusBarFocusable; boolean bouncerShowing; boolean keyguardFadingAway; boolean qsExpanded; boolean headsUpShowing; boolean forceStatusBarVisible; boolean forceCollapsed; boolean forceDozeBrightness; boolean forceUserActivity; boolean backdropShowing; /** * The {@link StatusBar} state from the status bar. */ int statusBarState; boolean remoteInputActive; boolean forcePluginOpen; boolean dozing; boolean scrimsVisible; private boolean isKeyguardShowingAndNotOccluded() { return keyguardShowing && !keyguardOccluded; } @Override public String toString() { StringBuilder result = new StringBuilder(); String newLine = "\n"; result.append("Window State {"); result.append(newLine); Field[] fields = this.getClass().getDeclaredFields(); // Print field names paired with their values for (Field field : fields) { result.append(" "); try { result.append(field.getName()); result.append(": "); //requires access to private field: result.append(field.get(this)); } catch (IllegalAccessException ex) { } result.append(newLine); } result.append("}"); return result.toString(); } } /** * Custom listener to pipe data back to plugins about whether or not the status bar would be * collapsed if not for the plugin. * TODO: Find cleaner way to do this. */ public interface OtherwisedCollapsedListener { void setWouldOtherwiseCollapse(boolean otherwiseCollapse); } }