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