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