StatusBar.java revision 32343e6bd91d3e1527c1f77ef63595cd8b8ea920
1/* 2 * Copyright (C) 2010 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 17package com.android.systemui.statusbar.phone; 18 19import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN; 20import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; 21import static android.app.StatusBarManager.windowStateToString; 22import static android.content.res.Configuration.UI_MODE_TYPE_CAR; 23 24import static com.android.systemui.statusbar.notification.NotificationInflater.InflationCallback; 25import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT; 26import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT; 27import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE; 28import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT; 29import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCENT; 30import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT; 31import static com.android.systemui.statusbar.phone.BarTransitions.MODE_WARNING; 32 33import android.animation.Animator; 34import android.animation.AnimatorListenerAdapter; 35import android.annotation.NonNull; 36import android.annotation.Nullable; 37import android.app.ActivityManager; 38import android.app.ActivityManager.StackId; 39import android.app.ActivityOptions; 40import android.app.INotificationManager; 41import android.app.KeyguardManager; 42import android.app.Notification; 43import android.app.NotificationChannel; 44import android.app.NotificationManager; 45import android.app.PendingIntent; 46import android.app.RemoteInput; 47import android.app.StatusBarManager; 48import android.app.TaskStackBuilder; 49import android.app.WallpaperColors; 50import android.app.WallpaperManager; 51import android.app.admin.DevicePolicyManager; 52import android.content.BroadcastReceiver; 53import android.content.ComponentCallbacks2; 54import android.content.ComponentName; 55import android.content.Context; 56import android.content.Intent; 57import android.content.IntentFilter; 58import android.content.IntentSender; 59import android.content.om.IOverlayManager; 60import android.content.om.OverlayInfo; 61import android.content.pm.ApplicationInfo; 62import android.content.pm.IPackageManager; 63import android.content.pm.PackageManager; 64import android.content.pm.PackageManager.NameNotFoundException; 65import android.content.pm.UserInfo; 66import android.content.res.Configuration; 67import android.content.res.Resources; 68import android.database.ContentObserver; 69import android.graphics.Bitmap; 70import android.graphics.Canvas; 71import android.graphics.ColorFilter; 72import android.graphics.PixelFormat; 73import android.graphics.Point; 74import android.graphics.PointF; 75import android.graphics.PorterDuff; 76import android.graphics.PorterDuffXfermode; 77import android.graphics.Rect; 78import android.graphics.drawable.BitmapDrawable; 79import android.graphics.drawable.ColorDrawable; 80import android.graphics.drawable.Drawable; 81import android.media.AudioAttributes; 82import android.media.MediaMetadata; 83import android.media.session.MediaController; 84import android.media.session.MediaSession; 85import android.media.session.MediaSessionManager; 86import android.media.session.PlaybackState; 87import android.metrics.LogMaker; 88import android.net.Uri; 89import android.os.AsyncTask; 90import android.os.Build; 91import android.os.Bundle; 92import android.os.Handler; 93import android.os.IBinder; 94import android.os.Message; 95import android.os.PowerManager; 96import android.os.RemoteException; 97import android.os.ServiceManager; 98import android.os.SystemClock; 99import android.os.SystemProperties; 100import android.os.Trace; 101import android.os.UserHandle; 102import android.os.UserManager; 103import android.os.Vibrator; 104import android.provider.Settings; 105import android.service.notification.NotificationListenerService; 106import android.service.notification.NotificationListenerService.RankingMap; 107import android.service.notification.StatusBarNotification; 108import android.service.vr.IVrManager; 109import android.service.vr.IVrStateCallbacks; 110import android.text.TextUtils; 111import android.util.ArraySet; 112import android.util.DisplayMetrics; 113import android.util.EventLog; 114import android.util.Log; 115import android.util.Slog; 116import android.util.SparseArray; 117import android.util.SparseBooleanArray; 118import android.view.Display; 119import android.view.IWindowManager; 120import android.view.KeyEvent; 121import android.view.LayoutInflater; 122import android.view.MotionEvent; 123import android.view.ThreadedRenderer; 124import android.view.View; 125import android.view.ViewAnimationUtils; 126import android.view.ViewGroup; 127import android.view.ViewParent; 128import android.view.ViewStub; 129import android.view.ViewTreeObserver; 130import android.view.WindowManager; 131import android.view.WindowManagerGlobal; 132import android.view.accessibility.AccessibilityManager; 133import android.view.animation.AccelerateInterpolator; 134import android.view.animation.Interpolator; 135import android.widget.DateTimeView; 136import android.widget.ImageView; 137import android.widget.RemoteViews; 138import android.widget.TextView; 139import android.widget.Toast; 140 141import com.android.internal.colorextraction.ColorExtractor; 142import com.android.internal.graphics.ColorUtils; 143import com.android.internal.logging.MetricsLogger; 144import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 145import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; 146import com.android.internal.statusbar.IStatusBarService; 147import com.android.internal.statusbar.NotificationVisibility; 148import com.android.internal.statusbar.StatusBarIcon; 149import com.android.internal.util.NotificationMessagingUtil; 150import com.android.internal.widget.LockPatternUtils; 151import com.android.keyguard.KeyguardHostView.OnDismissAction; 152import com.android.keyguard.KeyguardUpdateMonitor; 153import com.android.keyguard.KeyguardUpdateMonitorCallback; 154import com.android.keyguard.ViewMediatorCallback; 155import com.android.systemui.ActivityStarterDelegate; 156import com.android.systemui.DejankUtils; 157import com.android.systemui.DemoMode; 158import com.android.systemui.Dependency; 159import com.android.systemui.EventLogTags; 160import com.android.systemui.ForegroundServiceController; 161import com.android.systemui.Interpolators; 162import com.android.systemui.Prefs; 163import com.android.systemui.R; 164import com.android.systemui.RecentsComponent; 165import com.android.systemui.SwipeHelper; 166import com.android.systemui.SystemUI; 167import com.android.systemui.SystemUIFactory; 168import com.android.systemui.UiOffloadThread; 169import com.android.systemui.assist.AssistManager; 170import com.android.systemui.classifier.FalsingLog; 171import com.android.systemui.classifier.FalsingManager; 172import com.android.systemui.colorextraction.SysuiColorExtractor; 173import com.android.systemui.doze.DozeHost; 174import com.android.systemui.doze.DozeLog; 175import com.android.systemui.fragments.ExtensionFragmentListener; 176import com.android.systemui.fragments.FragmentHostManager; 177import com.android.systemui.keyguard.KeyguardViewMediator; 178import com.android.systemui.plugins.ActivityStarter; 179import com.android.systemui.plugins.qs.QS; 180import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem; 181import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption; 182import com.android.systemui.qs.QSFragment; 183import com.android.systemui.qs.QSPanel; 184import com.android.systemui.qs.QSTileHost; 185import com.android.systemui.recents.Recents; 186import com.android.systemui.recents.ScreenPinningRequest; 187import com.android.systemui.recents.events.EventBus; 188import com.android.systemui.recents.events.activity.AppTransitionFinishedEvent; 189import com.android.systemui.recents.events.activity.UndockingTaskEvent; 190import com.android.systemui.recents.misc.SystemServicesProxy; 191import com.android.systemui.stackdivider.Divider; 192import com.android.systemui.stackdivider.WindowManagerProxy; 193import com.android.systemui.statusbar.ActivatableNotificationView; 194import com.android.systemui.statusbar.BackDropView; 195import com.android.systemui.statusbar.CommandQueue; 196import com.android.systemui.statusbar.DismissView; 197import com.android.systemui.statusbar.DragDownHelper; 198import com.android.systemui.statusbar.EmptyShadeView; 199import com.android.systemui.statusbar.ExpandableNotificationRow; 200import com.android.systemui.statusbar.GestureRecorder; 201import com.android.systemui.statusbar.KeyboardShortcuts; 202import com.android.systemui.statusbar.KeyguardIndicationController; 203import com.android.systemui.statusbar.NotificationData; 204import com.android.systemui.statusbar.NotificationData.Entry; 205import com.android.systemui.statusbar.NotificationGuts; 206import com.android.systemui.statusbar.NotificationInfo; 207import com.android.systemui.statusbar.NotificationShelf; 208import com.android.systemui.statusbar.NotificationSnooze; 209import com.android.systemui.statusbar.RemoteInputController; 210import com.android.systemui.statusbar.ScrimView; 211import com.android.systemui.statusbar.SignalClusterView; 212import com.android.systemui.statusbar.StatusBarState; 213import com.android.systemui.statusbar.notification.AboveShelfObserver; 214import com.android.systemui.statusbar.notification.InflationException; 215import com.android.systemui.statusbar.notification.RowInflaterTask; 216import com.android.systemui.statusbar.notification.VisualStabilityManager; 217import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener; 218import com.android.systemui.statusbar.policy.BatteryController; 219import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback; 220import com.android.systemui.statusbar.policy.BrightnessMirrorController; 221import com.android.systemui.statusbar.policy.ConfigurationController; 222import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; 223import com.android.systemui.statusbar.policy.DarkIconDispatcher; 224import com.android.systemui.statusbar.policy.DeviceProvisionedController; 225import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; 226import com.android.systemui.statusbar.policy.ExtensionController; 227import com.android.systemui.statusbar.policy.HeadsUpManager; 228import com.android.systemui.statusbar.policy.KeyguardMonitor; 229import com.android.systemui.statusbar.policy.KeyguardMonitorImpl; 230import com.android.systemui.statusbar.policy.KeyguardUserSwitcher; 231import com.android.systemui.statusbar.policy.NetworkController; 232import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; 233import com.android.systemui.statusbar.policy.PreviewInflater; 234import com.android.systemui.statusbar.policy.RemoteInputView; 235import com.android.systemui.statusbar.policy.UserInfoController; 236import com.android.systemui.statusbar.policy.UserInfoControllerImpl; 237import com.android.systemui.statusbar.policy.UserSwitcherController; 238import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; 239import com.android.systemui.statusbar.stack.NotificationStackScrollLayout 240 .OnChildLocationsChangedListener; 241import com.android.systemui.statusbar.stack.StackStateAnimator; 242import com.android.systemui.util.NotificationChannels; 243import com.android.systemui.util.leak.LeakDetector; 244import com.android.systemui.volume.VolumeComponent; 245 246import java.io.FileDescriptor; 247import java.io.PrintWriter; 248import java.io.StringWriter; 249import java.util.ArrayList; 250import java.util.Collection; 251import java.util.Collections; 252import java.util.HashMap; 253import java.util.HashSet; 254import java.util.List; 255import java.util.Locale; 256import java.util.Map; 257import java.util.Set; 258import java.util.Stack; 259 260public class StatusBar extends SystemUI implements DemoMode, 261 DragDownHelper.DragDownCallback, ActivityStarter, OnUnlockMethodChangedListener, 262 OnHeadsUpChangedListener, VisualStabilityManager.Callback, CommandQueue.Callbacks, 263 ActivatableNotificationView.OnActivatedListener, 264 ExpandableNotificationRow.ExpansionLogger, NotificationData.Environment, 265 ExpandableNotificationRow.OnExpandClickListener, InflationCallback, 266 ColorExtractor.OnColorsChangedListener, ConfigurationListener { 267 public static final boolean MULTIUSER_DEBUG = false; 268 269 public static final boolean ENABLE_REMOTE_INPUT = 270 SystemProperties.getBoolean("debug.enable_remote_input", true); 271 public static final boolean ENABLE_CHILD_NOTIFICATIONS 272 = SystemProperties.getBoolean("debug.child_notifs", true); 273 public static final boolean FORCE_REMOTE_INPUT_HISTORY = 274 SystemProperties.getBoolean("debug.force_remoteinput_history", false); 275 private static boolean ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT = false; 276 277 protected static final int MSG_SHOW_RECENT_APPS = 1019; 278 protected static final int MSG_HIDE_RECENT_APPS = 1020; 279 protected static final int MSG_TOGGLE_RECENTS_APPS = 1021; 280 protected static final int MSG_PRELOAD_RECENT_APPS = 1022; 281 protected static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 1023; 282 protected static final int MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU = 1026; 283 protected static final int MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU = 1027; 284 285 protected static final boolean ENABLE_HEADS_UP = true; 286 protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up"; 287 288 // Must match constant in Settings. Used to highlight preferences when linking to Settings. 289 private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key"; 290 291 private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF"; 292 293 // Should match the values in PhoneWindowManager 294 public static final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey"; 295 public static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps"; 296 297 private static final String BANNER_ACTION_CANCEL = 298 "com.android.systemui.statusbar.banner_action_cancel"; 299 private static final String BANNER_ACTION_SETUP = 300 "com.android.systemui.statusbar.banner_action_setup"; 301 private static final String NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION 302 = "com.android.systemui.statusbar.work_challenge_unlocked_notification_action"; 303 public static final String TAG = "StatusBar"; 304 public static final boolean DEBUG = false; 305 public static final boolean SPEW = false; 306 public static final boolean DUMPTRUCK = true; // extra dumpsys info 307 public static final boolean DEBUG_GESTURES = false; 308 public static final boolean DEBUG_MEDIA = false; 309 public static final boolean DEBUG_MEDIA_FAKE_ARTWORK = false; 310 public static final boolean DEBUG_CAMERA_LIFT = false; 311 312 public static final boolean DEBUG_WINDOW_STATE = false; 313 314 // additional instrumentation for testing purposes; intended to be left on during development 315 public static final boolean CHATTY = DEBUG; 316 317 public static final boolean SHOW_LOCKSCREEN_MEDIA_ARTWORK = true; 318 319 public static final String ACTION_FAKE_ARTWORK = "fake_artwork"; 320 321 private static final int MSG_OPEN_NOTIFICATION_PANEL = 1000; 322 private static final int MSG_CLOSE_PANELS = 1001; 323 private static final int MSG_OPEN_SETTINGS_PANEL = 1002; 324 private static final int MSG_LAUNCH_TRANSITION_TIMEOUT = 1003; 325 // 1020-1040 reserved for BaseStatusBar 326 327 // Time after we abort the launch transition. 328 private static final long LAUNCH_TRANSITION_TIMEOUT_MS = 5000; 329 330 private static final boolean CLOSE_PANEL_WHEN_EMPTIED = true; 331 332 private static final int STATUS_OR_NAV_TRANSIENT = 333 View.STATUS_BAR_TRANSIENT | View.NAVIGATION_BAR_TRANSIENT; 334 private static final long AUTOHIDE_TIMEOUT_MS = 3000; 335 336 /** The minimum delay in ms between reports of notification visibility. */ 337 private static final int VISIBILITY_REPORT_MIN_DELAY_MS = 500; 338 339 /** 340 * The delay to reset the hint text when the hint animation is finished running. 341 */ 342 private static final int HINT_RESET_DELAY_MS = 1200; 343 344 private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder() 345 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) 346 .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) 347 .build(); 348 349 public static final int FADE_KEYGUARD_START_DELAY = 100; 350 public static final int FADE_KEYGUARD_DURATION = 300; 351 public static final int FADE_KEYGUARD_DURATION_PULSING = 96; 352 353 /** If true, the system is in the half-boot-to-decryption-screen state. 354 * Prudently disable QS and notifications. */ 355 private static final boolean ONLY_CORE_APPS; 356 357 /** If true, the lockscreen will show a distinct wallpaper */ 358 private static final boolean ENABLE_LOCKSCREEN_WALLPAPER = true; 359 360 /* If true, the device supports freeform window management. 361 * This affects the status bar UI. */ 362 private static final boolean FREEFORM_WINDOW_MANAGEMENT; 363 364 /** 365 * How long to wait before auto-dismissing a notification that was kept for remote input, and 366 * has now sent a remote input. We auto-dismiss, because the app may not see a reason to cancel 367 * these given that they technically don't exist anymore. We wait a bit in case the app issues 368 * an update. 369 */ 370 private static final int REMOTE_INPUT_KEPT_ENTRY_AUTO_CANCEL_DELAY = 200; 371 372 /** 373 * Never let the alpha become zero for surfaces that draw with SRC - otherwise the RenderNode 374 * won't draw anything and uninitialized memory will show through 375 * if mScrimSrcModeEnabled. Note that 0.001 is rounded down to 0 in 376 * libhwui. 377 */ 378 private static final float SRC_MIN_ALPHA = 0.002f; 379 380 static { 381 boolean onlyCoreApps; 382 boolean freeformWindowManagement; 383 try { 384 IPackageManager packageManager = 385 IPackageManager.Stub.asInterface(ServiceManager.getService("package")); 386 onlyCoreApps = packageManager.isOnlyCoreApps(); 387 freeformWindowManagement = packageManager.hasSystemFeature( 388 PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT, 0); 389 } catch (RemoteException e) { 390 onlyCoreApps = false; 391 freeformWindowManagement = false; 392 } 393 ONLY_CORE_APPS = onlyCoreApps; 394 FREEFORM_WINDOW_MANAGEMENT = freeformWindowManagement; 395 } 396 397 /** 398 * The {@link StatusBarState} of the status bar. 399 */ 400 protected int mState; 401 protected boolean mBouncerShowing; 402 protected boolean mShowLockscreenNotifications; 403 protected boolean mAllowLockscreenRemoteInput; 404 405 PhoneStatusBarPolicy mIconPolicy; 406 407 VolumeComponent mVolumeComponent; 408 BrightnessMirrorController mBrightnessMirrorController; 409 protected FingerprintUnlockController mFingerprintUnlockController; 410 LightBarController mLightBarController; 411 protected LockscreenWallpaper mLockscreenWallpaper; 412 413 int mNaturalBarHeight = -1; 414 415 Point mCurrentDisplaySize = new Point(); 416 417 protected StatusBarWindowView mStatusBarWindow; 418 protected PhoneStatusBarView mStatusBarView; 419 private int mStatusBarWindowState = WINDOW_STATE_SHOWING; 420 protected StatusBarWindowManager mStatusBarWindowManager; 421 protected UnlockMethodCache mUnlockMethodCache; 422 private DozeServiceHost mDozeServiceHost; 423 private boolean mWakeUpComingFromTouch; 424 private PointF mWakeUpTouchLocation; 425 private boolean mScreenTurningOn; 426 private boolean mScreenFullyOff; 427 428 int mPixelFormat; 429 Object mQueueLock = new Object(); 430 431 protected StatusBarIconController mIconController; 432 433 // expanded notifications 434 protected NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window 435 View mExpandedContents; 436 TextView mNotificationPanelDebugText; 437 438 /** 439 * {@code true} if notifications not part of a group should by default be rendered in their 440 * expanded state. If {@code false}, then only the first notification will be expanded if 441 * possible. 442 */ 443 private boolean mAlwaysExpandNonGroupedNotification; 444 445 // settings 446 private QSPanel mQSPanel; 447 448 // top bar 449 protected KeyguardStatusBarView mKeyguardStatusBar; 450 boolean mLeaveOpenOnKeyguardHide; 451 KeyguardIndicationController mKeyguardIndicationController; 452 453 // Keyguard is going away soon. 454 private boolean mKeyguardGoingAway; 455 // Keyguard is actually fading away now. 456 protected boolean mKeyguardFadingAway; 457 protected long mKeyguardFadingAwayDelay; 458 protected long mKeyguardFadingAwayDuration; 459 460 // RemoteInputView to be activated after unlock 461 private View mPendingRemoteInputView; 462 private View mPendingWorkRemoteInputView; 463 464 private View mReportRejectedTouch; 465 466 int mMaxAllowedKeyguardNotifications; 467 468 boolean mExpandedVisible; 469 470 // the tracker view 471 int mTrackingPosition; // the position of the top of the tracking view. 472 473 // Tracking finger for opening/closing. 474 boolean mTracking; 475 476 int[] mAbsPos = new int[2]; 477 ArrayList<Runnable> mPostCollapseRunnables = new ArrayList<>(); 478 479 // for disabling the status bar 480 int mDisabled1 = 0; 481 int mDisabled2 = 0; 482 483 // tracking calls to View.setSystemUiVisibility() 484 int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE; 485 private final Rect mLastFullscreenStackBounds = new Rect(); 486 private final Rect mLastDockedStackBounds = new Rect(); 487 private final Rect mTmpRect = new Rect(); 488 489 // last value sent to window manager 490 private int mLastDispatchedSystemUiVisibility = ~View.SYSTEM_UI_FLAG_VISIBLE; 491 492 DisplayMetrics mDisplayMetrics = new DisplayMetrics(); 493 494 // XXX: gesture research 495 private final GestureRecorder mGestureRec = DEBUG_GESTURES 496 ? new GestureRecorder("/sdcard/statusbar_gestures.dat") 497 : null; 498 499 private ScreenPinningRequest mScreenPinningRequest; 500 501 private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class); 502 503 // ensure quick settings is disabled until the current user makes it through the setup wizard 504 private boolean mUserSetup = false; 505 private DeviceProvisionedListener mUserSetupObserver = new DeviceProvisionedListener() { 506 @Override 507 public void onUserSetupChanged() { 508 final boolean userSetup = mDeviceProvisionedController.isUserSetup( 509 mDeviceProvisionedController.getCurrentUser()); 510 if (MULTIUSER_DEBUG) Log.d(TAG, String.format("User setup changed: " + 511 "userSetup=%s mUserSetup=%s", userSetup, mUserSetup)); 512 513 if (userSetup != mUserSetup) { 514 mUserSetup = userSetup; 515 if (!mUserSetup && mStatusBarView != null) 516 animateCollapseQuickSettings(); 517 if (mNotificationPanel != null) { 518 mNotificationPanel.setUserSetupComplete(mUserSetup); 519 } 520 updateQsExpansionEnabled(); 521 } 522 } 523 }; 524 525 protected H mHandler = createHandler(); 526 final private ContentObserver mHeadsUpObserver = new ContentObserver(mHandler) { 527 @Override 528 public void onChange(boolean selfChange) { 529 boolean wasUsing = mUseHeadsUp; 530 mUseHeadsUp = ENABLE_HEADS_UP && !mDisableNotificationAlerts 531 && Settings.Global.HEADS_UP_OFF != Settings.Global.getInt( 532 mContext.getContentResolver(), Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED, 533 Settings.Global.HEADS_UP_OFF); 534 mHeadsUpTicker = mUseHeadsUp && 0 != Settings.Global.getInt( 535 mContext.getContentResolver(), SETTING_HEADS_UP_TICKER, 0); 536 Log.d(TAG, "heads up is " + (mUseHeadsUp ? "enabled" : "disabled")); 537 if (wasUsing != mUseHeadsUp) { 538 if (!mUseHeadsUp) { 539 Log.d(TAG, "dismissing any existing heads up notification on disable event"); 540 mHeadsUpManager.releaseAllImmediately(); 541 } 542 } 543 } 544 }; 545 546 private int mInteractingWindows; 547 private boolean mAutohideSuspended; 548 private int mStatusBarMode; 549 private int mMaxKeyguardNotifications; 550 551 private ViewMediatorCallback mKeyguardViewMediatorCallback; 552 protected ScrimController mScrimController; 553 protected DozeScrimController mDozeScrimController; 554 private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class); 555 556 private final Runnable mAutohide = new Runnable() { 557 @Override 558 public void run() { 559 int requested = mSystemUiVisibility & ~STATUS_OR_NAV_TRANSIENT; 560 if (mSystemUiVisibility != requested) { 561 notifyUiVisibilityChanged(requested); 562 } 563 }}; 564 565 private boolean mWaitingForKeyguardExit; 566 protected boolean mDozing; 567 private boolean mDozingRequested; 568 protected boolean mScrimSrcModeEnabled; 569 570 public static final Interpolator ALPHA_IN = Interpolators.ALPHA_IN; 571 public static final Interpolator ALPHA_OUT = Interpolators.ALPHA_OUT; 572 573 protected BackDropView mBackdrop; 574 protected ImageView mBackdropFront, mBackdropBack; 575 protected PorterDuffXfermode mSrcXferMode = new PorterDuffXfermode(PorterDuff.Mode.SRC); 576 protected PorterDuffXfermode mSrcOverXferMode = 577 new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER); 578 579 private MediaSessionManager mMediaSessionManager; 580 private MediaController mMediaController; 581 private String mMediaNotificationKey; 582 private MediaMetadata mMediaMetadata; 583 private MediaController.Callback mMediaListener 584 = new MediaController.Callback() { 585 @Override 586 public void onPlaybackStateChanged(PlaybackState state) { 587 super.onPlaybackStateChanged(state); 588 if (DEBUG_MEDIA) Log.v(TAG, "DEBUG_MEDIA: onPlaybackStateChanged: " + state); 589 if (state != null) { 590 if (!isPlaybackActive(state.getState())) { 591 clearCurrentMediaNotification(); 592 updateMediaMetaData(true, true); 593 } 594 } 595 } 596 597 @Override 598 public void onMetadataChanged(MediaMetadata metadata) { 599 super.onMetadataChanged(metadata); 600 if (DEBUG_MEDIA) Log.v(TAG, "DEBUG_MEDIA: onMetadataChanged: " + metadata); 601 mMediaMetadata = metadata; 602 updateMediaMetaData(true, true); 603 } 604 }; 605 606 private final OnChildLocationsChangedListener mOnChildLocationsChangedListener = 607 new OnChildLocationsChangedListener() { 608 @Override 609 public void onChildLocationsChanged(NotificationStackScrollLayout stackScrollLayout) { 610 userActivity(); 611 } 612 }; 613 614 private int mDisabledUnmodified1; 615 private int mDisabledUnmodified2; 616 617 /** Keys of notifications currently visible to the user. */ 618 private final ArraySet<NotificationVisibility> mCurrentlyVisibleNotifications = 619 new ArraySet<>(); 620 private long mLastVisibilityReportUptimeMs; 621 622 private Runnable mLaunchTransitionEndRunnable; 623 protected boolean mLaunchTransitionFadingAway; 624 private ExpandableNotificationRow mDraggedDownRow; 625 private boolean mLaunchCameraOnScreenTurningOn; 626 private boolean mLaunchCameraOnFinishedGoingToSleep; 627 private int mLastCameraLaunchSource; 628 private PowerManager.WakeLock mGestureWakeLock; 629 private Vibrator mVibrator; 630 private long[] mCameraLaunchGestureVibePattern; 631 632 private final int[] mTmpInt2 = new int[2]; 633 634 // Fingerprint (as computed by getLoggingFingerprint() of the last logged state. 635 private int mLastLoggedStateFingerprint; 636 637 /** 638 * If set, the device has started going to sleep but isn't fully non-interactive yet. 639 */ 640 protected boolean mStartedGoingToSleep; 641 642 private final OnChildLocationsChangedListener mNotificationLocationsChangedListener = 643 new OnChildLocationsChangedListener() { 644 @Override 645 public void onChildLocationsChanged( 646 NotificationStackScrollLayout stackScrollLayout) { 647 if (mHandler.hasCallbacks(mVisibilityReporter)) { 648 // Visibilities will be reported when the existing 649 // callback is executed. 650 return; 651 } 652 // Calculate when we're allowed to run the visibility 653 // reporter. Note that this timestamp might already have 654 // passed. That's OK, the callback will just be executed 655 // ASAP. 656 long nextReportUptimeMs = 657 mLastVisibilityReportUptimeMs + VISIBILITY_REPORT_MIN_DELAY_MS; 658 mHandler.postAtTime(mVisibilityReporter, nextReportUptimeMs); 659 } 660 }; 661 662 // Tracks notifications currently visible in mNotificationStackScroller and 663 // emits visibility events via NoMan on changes. 664 protected final Runnable mVisibilityReporter = new Runnable() { 665 private final ArraySet<NotificationVisibility> mTmpNewlyVisibleNotifications = 666 new ArraySet<>(); 667 private final ArraySet<NotificationVisibility> mTmpCurrentlyVisibleNotifications = 668 new ArraySet<>(); 669 private final ArraySet<NotificationVisibility> mTmpNoLongerVisibleNotifications = 670 new ArraySet<>(); 671 672 @Override 673 public void run() { 674 mLastVisibilityReportUptimeMs = SystemClock.uptimeMillis(); 675 final String mediaKey = getCurrentMediaNotificationKey(); 676 677 // 1. Loop over mNotificationData entries: 678 // A. Keep list of visible notifications. 679 // B. Keep list of previously hidden, now visible notifications. 680 // 2. Compute no-longer visible notifications by removing currently 681 // visible notifications from the set of previously visible 682 // notifications. 683 // 3. Report newly visible and no-longer visible notifications. 684 // 4. Keep currently visible notifications for next report. 685 ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications(); 686 int N = activeNotifications.size(); 687 for (int i = 0; i < N; i++) { 688 Entry entry = activeNotifications.get(i); 689 String key = entry.notification.getKey(); 690 boolean isVisible = mStackScroller.isInVisibleLocation(entry.row); 691 NotificationVisibility visObj = NotificationVisibility.obtain(key, i, isVisible); 692 boolean previouslyVisible = mCurrentlyVisibleNotifications.contains(visObj); 693 if (isVisible) { 694 // Build new set of visible notifications. 695 mTmpCurrentlyVisibleNotifications.add(visObj); 696 if (!previouslyVisible) { 697 mTmpNewlyVisibleNotifications.add(visObj); 698 } 699 } else { 700 // release object 701 visObj.recycle(); 702 } 703 } 704 mTmpNoLongerVisibleNotifications.addAll(mCurrentlyVisibleNotifications); 705 mTmpNoLongerVisibleNotifications.removeAll(mTmpCurrentlyVisibleNotifications); 706 707 logNotificationVisibilityChanges( 708 mTmpNewlyVisibleNotifications, mTmpNoLongerVisibleNotifications); 709 710 recycleAllVisibilityObjects(mCurrentlyVisibleNotifications); 711 mCurrentlyVisibleNotifications.addAll(mTmpCurrentlyVisibleNotifications); 712 713 recycleAllVisibilityObjects(mTmpNoLongerVisibleNotifications); 714 mTmpCurrentlyVisibleNotifications.clear(); 715 mTmpNewlyVisibleNotifications.clear(); 716 mTmpNoLongerVisibleNotifications.clear(); 717 } 718 }; 719 720 private NotificationMessagingUtil mMessagingUtil; 721 private KeyguardUserSwitcher mKeyguardUserSwitcher; 722 private UserSwitcherController mUserSwitcherController; 723 private NetworkController mNetworkController; 724 private KeyguardMonitorImpl mKeyguardMonitor; 725 private BatteryController mBatteryController; 726 protected boolean mPanelExpanded; 727 private IOverlayManager mOverlayManager; 728 private boolean mKeyguardRequested; 729 private boolean mIsKeyguard; 730 private LogMaker mStatusBarStateLog; 731 private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger(); 732 private NotificationIconAreaController mNotificationIconAreaController; 733 private boolean mReinflateNotificationsOnUserSwitched; 734 private HashMap<String, Entry> mPendingNotifications = new HashMap<>(); 735 private boolean mClearAllEnabled; 736 @Nullable private View mAmbientIndicationContainer; 737 private String mKeyToRemoveOnGutsClosed; 738 private SysuiColorExtractor mColorExtractor; 739 private ForegroundServiceController mForegroundServiceController; 740 741 private void recycleAllVisibilityObjects(ArraySet<NotificationVisibility> array) { 742 final int N = array.size(); 743 for (int i = 0 ; i < N; i++) { 744 array.valueAt(i).recycle(); 745 } 746 array.clear(); 747 } 748 749 private final View.OnClickListener mGoToLockedShadeListener = v -> { 750 if (mState == StatusBarState.KEYGUARD) { 751 wakeUpIfDozing(SystemClock.uptimeMillis(), v); 752 goToLockedShade(null); 753 } 754 }; 755 private HashMap<ExpandableNotificationRow, List<ExpandableNotificationRow>> mTmpChildOrderMap 756 = new HashMap<>(); 757 private RankingMap mLatestRankingMap; 758 private boolean mNoAnimationOnNextBarModeChange; 759 private FalsingManager mFalsingManager; 760 761 private KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() { 762 @Override 763 public void onDreamingStateChanged(boolean dreaming) { 764 if (dreaming) { 765 maybeEscalateHeadsUp(); 766 } 767 } 768 }; 769 770 private NavigationBarFragment mNavigationBar; 771 private View mNavigationBarView; 772 773 @Override 774 public void start() { 775 mNetworkController = Dependency.get(NetworkController.class); 776 mUserSwitcherController = Dependency.get(UserSwitcherController.class); 777 mKeyguardMonitor = (KeyguardMonitorImpl) Dependency.get(KeyguardMonitor.class); 778 mBatteryController = Dependency.get(BatteryController.class); 779 mAssistManager = Dependency.get(AssistManager.class); 780 mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class); 781 mSystemServicesProxy = SystemServicesProxy.getInstance(mContext); 782 mOverlayManager = IOverlayManager.Stub.asInterface( 783 ServiceManager.getService(Context.OVERLAY_SERVICE)); 784 785 mColorExtractor = Dependency.get(SysuiColorExtractor.class); 786 mColorExtractor.addOnColorsChangedListener(this); 787 788 mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); 789 790 mForegroundServiceController = Dependency.get(ForegroundServiceController.class); 791 792 mDisplay = mWindowManager.getDefaultDisplay(); 793 updateDisplaySize(); 794 795 Resources res = mContext.getResources(); 796 mScrimSrcModeEnabled = res.getBoolean(R.bool.config_status_bar_scrim_behind_use_src); 797 mClearAllEnabled = res.getBoolean(R.bool.config_enableNotificationsClearAll); 798 mAlwaysExpandNonGroupedNotification = 799 res.getBoolean(R.bool.config_alwaysExpandNonGroupedNotifications); 800 801 DateTimeView.setReceiverHandler(Dependency.get(Dependency.TIME_TICK_HANDLER)); 802 putComponent(StatusBar.class, this); 803 804 // start old BaseStatusBar.start(). 805 mWindowManagerService = WindowManagerGlobal.getWindowManagerService(); 806 mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService( 807 Context.DEVICE_POLICY_SERVICE); 808 809 mNotificationData = new NotificationData(this); 810 mMessagingUtil = new NotificationMessagingUtil(mContext); 811 812 mAccessibilityManager = (AccessibilityManager) 813 mContext.getSystemService(Context.ACCESSIBILITY_SERVICE); 814 815 mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); 816 817 mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class); 818 mDeviceProvisionedController.addCallback(mDeviceProvisionedListener); 819 mContext.getContentResolver().registerContentObserver( 820 Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false, 821 mSettingsObserver); 822 mContext.getContentResolver().registerContentObserver( 823 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false, 824 mLockscreenSettingsObserver, 825 UserHandle.USER_ALL); 826 if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) { 827 mContext.getContentResolver().registerContentObserver( 828 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT), 829 false, 830 mSettingsObserver, 831 UserHandle.USER_ALL); 832 } 833 834 mContext.getContentResolver().registerContentObserver( 835 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS), 836 true, 837 mLockscreenSettingsObserver, 838 UserHandle.USER_ALL); 839 840 mBarService = IStatusBarService.Stub.asInterface( 841 ServiceManager.getService(Context.STATUS_BAR_SERVICE)); 842 843 mRecents = getComponent(Recents.class); 844 845 final Configuration currentConfig = res.getConfiguration(); 846 mLocale = currentConfig.locale; 847 mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale); 848 849 mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); 850 mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE); 851 mLockPatternUtils = new LockPatternUtils(mContext); 852 853 // Connect in to the status bar manager service 854 mCommandQueue = getComponent(CommandQueue.class); 855 mCommandQueue.addCallbacks(this); 856 857 int[] switches = new int[9]; 858 ArrayList<IBinder> binders = new ArrayList<>(); 859 ArrayList<String> iconSlots = new ArrayList<>(); 860 ArrayList<StatusBarIcon> icons = new ArrayList<>(); 861 Rect fullscreenStackBounds = new Rect(); 862 Rect dockedStackBounds = new Rect(); 863 try { 864 mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders, 865 fullscreenStackBounds, dockedStackBounds); 866 } catch (RemoteException ex) { 867 // If the system process isn't there we're doomed anyway. 868 } 869 870 createAndAddWindows(); 871 872 mSettingsObserver.onChange(false); // set up 873 mCommandQueue.disable(switches[0], switches[6], false /* animate */); 874 setSystemUiVisibility(switches[1], switches[7], switches[8], 0xffffffff, 875 fullscreenStackBounds, dockedStackBounds); 876 topAppWindowChanged(switches[2] != 0); 877 // StatusBarManagerService has a back up of IME token and it's restored here. 878 setImeWindowStatus(binders.get(0), switches[3], switches[4], switches[5] != 0); 879 880 // Set up the initial icon state 881 int N = iconSlots.size(); 882 for (int i=0; i < N; i++) { 883 mCommandQueue.setIcon(iconSlots.get(i), icons.get(i)); 884 } 885 886 // Set up the initial notification state. 887 try { 888 mNotificationListener.registerAsSystemService(mContext, 889 new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()), 890 UserHandle.USER_ALL); 891 } catch (RemoteException e) { 892 Log.e(TAG, "Unable to register notification listener", e); 893 } 894 895 896 if (DEBUG) { 897 Log.d(TAG, String.format( 898 "init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x", 899 icons.size(), 900 switches[0], 901 switches[1], 902 switches[2], 903 switches[3] 904 )); 905 } 906 907 mCurrentUserId = ActivityManager.getCurrentUser(); 908 setHeadsUpUser(mCurrentUserId); 909 910 IntentFilter filter = new IntentFilter(); 911 filter.addAction(Intent.ACTION_USER_SWITCHED); 912 filter.addAction(Intent.ACTION_USER_ADDED); 913 filter.addAction(Intent.ACTION_USER_PRESENT); 914 mContext.registerReceiver(mBaseBroadcastReceiver, filter); 915 916 IntentFilter internalFilter = new IntentFilter(); 917 internalFilter.addAction(NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION); 918 internalFilter.addAction(BANNER_ACTION_CANCEL); 919 internalFilter.addAction(BANNER_ACTION_SETUP); 920 mContext.registerReceiver(mBaseBroadcastReceiver, internalFilter, PERMISSION_SELF, null); 921 922 IntentFilter allUsersFilter = new IntentFilter(); 923 allUsersFilter.addAction( 924 DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); 925 allUsersFilter.addAction(Intent.ACTION_DEVICE_LOCKED_CHANGED); 926 mContext.registerReceiverAsUser(mAllUsersReceiver, UserHandle.ALL, allUsersFilter, 927 null, null); 928 updateCurrentProfilesCache(); 929 930 IVrManager vrManager = IVrManager.Stub.asInterface(ServiceManager.getService( 931 Context.VR_SERVICE)); 932 try { 933 vrManager.registerListener(mVrStateCallbacks); 934 } catch (RemoteException e) { 935 Slog.e(TAG, "Failed to register VR mode state listener: " + e); 936 } 937 938 mNonBlockablePkgs = new HashSet<>(); 939 Collections.addAll(mNonBlockablePkgs, res.getStringArray( 940 com.android.internal.R.array.config_nonBlockableNotificationPackages)); 941 // end old BaseStatusBar.start(). 942 943 mMediaSessionManager 944 = (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE); 945 // TODO: use MediaSessionManager.SessionListener to hook us up to future updates 946 // in session state 947 948 // Lastly, call to the icon policy to install/update all the icons. 949 mIconPolicy = new PhoneStatusBarPolicy(mContext, mIconController); 950 mSettingsObserver.onChange(false); // set up 951 952 mHeadsUpObserver.onChange(true); // set up 953 if (ENABLE_HEADS_UP) { 954 mContext.getContentResolver().registerContentObserver( 955 Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED), true, 956 mHeadsUpObserver); 957 mContext.getContentResolver().registerContentObserver( 958 Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true, 959 mHeadsUpObserver); 960 } 961 mUnlockMethodCache = UnlockMethodCache.getInstance(mContext); 962 mUnlockMethodCache.addListener(this); 963 startKeyguard(); 964 965 KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateCallback); 966 mDozeServiceHost = new DozeServiceHost(); 967 putComponent(DozeHost.class, mDozeServiceHost); 968 969 notifyUserAboutHiddenNotifications(); 970 971 mScreenPinningRequest = new ScreenPinningRequest(mContext); 972 mFalsingManager = FalsingManager.getInstance(mContext); 973 974 Dependency.get(ActivityStarterDelegate.class).setActivityStarterImpl(this); 975 976 Dependency.get(ConfigurationController.class).addCallback(this); 977 978 // Make sure that we're using the correct theme 979 onOverlayChanged(); 980 } 981 982 protected void createIconController() { 983 } 984 985 // ================================================================================ 986 // Constructing the view 987 // ================================================================================ 988 protected void makeStatusBarView() { 989 final Context context = mContext; 990 updateDisplaySize(); // populates mDisplayMetrics 991 updateResources(); 992 993 inflateStatusBarWindow(context); 994 mStatusBarWindow.setService(this); 995 mStatusBarWindow.setOnTouchListener(getStatusBarWindowTouchListener()); 996 997 // TODO: Deal with the ugliness that comes from having some of the statusbar broken out 998 // into fragments, but the rest here, it leaves some awkward lifecycle and whatnot. 999 mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById( 1000 R.id.notification_panel); 1001 mStackScroller = (NotificationStackScrollLayout) mStatusBarWindow.findViewById( 1002 R.id.notification_stack_scroller); 1003 mNotificationPanel.setStatusBar(this); 1004 mNotificationPanel.setGroupManager(mGroupManager); 1005 mAboveShelfObserver = new AboveShelfObserver(mStackScroller); 1006 mAboveShelfObserver.setListener(mStatusBarWindow.findViewById( 1007 R.id.notification_container_parent)); 1008 mKeyguardStatusBar = (KeyguardStatusBarView) mStatusBarWindow.findViewById(R.id.keyguard_header); 1009 1010 mNotificationIconAreaController = SystemUIFactory.getInstance() 1011 .createNotificationIconAreaController(context, this); 1012 inflateShelf(); 1013 mNotificationIconAreaController.setupShelf(mNotificationShelf); 1014 Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mNotificationIconAreaController); 1015 FragmentHostManager.get(mStatusBarWindow) 1016 .addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> { 1017 CollapsedStatusBarFragment statusBarFragment = 1018 (CollapsedStatusBarFragment) fragment; 1019 statusBarFragment.initNotificationIconArea(mNotificationIconAreaController); 1020 mStatusBarView = (PhoneStatusBarView) fragment.getView(); 1021 mStatusBarView.setBar(this); 1022 mStatusBarView.setPanel(mNotificationPanel); 1023 mStatusBarView.setScrimController(mScrimController); 1024 mStatusBarView.setBouncerShowing(mBouncerShowing); 1025 setAreThereNotifications(); 1026 checkBarModes(); 1027 }).getFragmentManager() 1028 .beginTransaction() 1029 .replace(R.id.status_bar_container, new CollapsedStatusBarFragment(), 1030 CollapsedStatusBarFragment.TAG) 1031 .commit(); 1032 mIconController = Dependency.get(StatusBarIconController.class); 1033 1034 mHeadsUpManager = new HeadsUpManager(context, mStatusBarWindow, mGroupManager); 1035 mHeadsUpManager.setBar(this); 1036 mHeadsUpManager.addListener(this); 1037 mHeadsUpManager.addListener(mNotificationPanel); 1038 mHeadsUpManager.addListener(mGroupManager); 1039 mHeadsUpManager.addListener(mVisualStabilityManager); 1040 mNotificationPanel.setHeadsUpManager(mHeadsUpManager); 1041 mNotificationData.setHeadsUpManager(mHeadsUpManager); 1042 mGroupManager.setHeadsUpManager(mHeadsUpManager); 1043 mHeadsUpManager.setVisualStabilityManager(mVisualStabilityManager); 1044 1045 if (MULTIUSER_DEBUG) { 1046 mNotificationPanelDebugText = (TextView) mNotificationPanel.findViewById( 1047 R.id.header_debug_info); 1048 mNotificationPanelDebugText.setVisibility(View.VISIBLE); 1049 } 1050 1051 try { 1052 boolean showNav = mWindowManagerService.hasNavigationBar(); 1053 if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav); 1054 if (showNav) { 1055 createNavigationBar(); 1056 } 1057 } catch (RemoteException ex) { 1058 // no window manager? good luck with that 1059 } 1060 1061 // figure out which pixel-format to use for the status bar. 1062 mPixelFormat = PixelFormat.OPAQUE; 1063 1064 mStackScroller.setLongPressListener(getNotificationLongClicker()); 1065 mStackScroller.setStatusBar(this); 1066 mStackScroller.setGroupManager(mGroupManager); 1067 mStackScroller.setHeadsUpManager(mHeadsUpManager); 1068 mGroupManager.setOnGroupChangeListener(mStackScroller); 1069 mVisualStabilityManager.setVisibilityLocationProvider(mStackScroller); 1070 1071 inflateEmptyShadeView(); 1072 inflateDismissView(); 1073 mExpandedContents = mStackScroller; 1074 1075 mBackdrop = (BackDropView) mStatusBarWindow.findViewById(R.id.backdrop); 1076 mBackdropFront = (ImageView) mBackdrop.findViewById(R.id.backdrop_front); 1077 mBackdropBack = (ImageView) mBackdrop.findViewById(R.id.backdrop_back); 1078 1079 if (ENABLE_LOCKSCREEN_WALLPAPER) { 1080 mLockscreenWallpaper = new LockscreenWallpaper(mContext, this, mHandler); 1081 } 1082 1083 mKeyguardIndicationController = 1084 SystemUIFactory.getInstance().createKeyguardIndicationController(mContext, 1085 (ViewGroup) mStatusBarWindow.findViewById(R.id.keyguard_indication_area), 1086 mNotificationPanel.getLockIcon()); 1087 mNotificationPanel.setKeyguardIndicationController(mKeyguardIndicationController); 1088 1089 1090 mAmbientIndicationContainer = mStatusBarWindow.findViewById( 1091 R.id.ambient_indication_container); 1092 1093 // set the initial view visibility 1094 setAreThereNotifications(); 1095 1096 // TODO: Find better place for this callback. 1097 mBatteryController.addCallback(new BatteryStateChangeCallback() { 1098 @Override 1099 public void onPowerSaveChanged(boolean isPowerSave) { 1100 mHandler.post(mCheckBarModes); 1101 if (mDozeServiceHost != null) { 1102 mDozeServiceHost.firePowerSaveChanged(isPowerSave); 1103 } 1104 } 1105 1106 @Override 1107 public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) { 1108 // noop 1109 } 1110 }); 1111 1112 mLightBarController = new LightBarController(context); 1113 if (mNavigationBar != null) { 1114 mNavigationBar.setLightBarController(mLightBarController); 1115 } 1116 1117 ScrimView scrimBehind = (ScrimView) mStatusBarWindow.findViewById(R.id.scrim_behind); 1118 ScrimView scrimInFront = (ScrimView) mStatusBarWindow.findViewById(R.id.scrim_in_front); 1119 View headsUpScrim = mStatusBarWindow.findViewById(R.id.heads_up_scrim); 1120 mScrimController = SystemUIFactory.getInstance().createScrimController(mLightBarController, 1121 scrimBehind, scrimInFront, headsUpScrim, mLockscreenWallpaper); 1122 if (mScrimSrcModeEnabled) { 1123 Runnable runnable = new Runnable() { 1124 @Override 1125 public void run() { 1126 boolean asSrc = mBackdrop.getVisibility() != View.VISIBLE; 1127 mScrimController.setDrawBehindAsSrc(asSrc); 1128 mStackScroller.setDrawBackgroundAsSrc(asSrc); 1129 } 1130 }; 1131 mBackdrop.setOnVisibilityChangedRunnable(runnable); 1132 runnable.run(); 1133 } 1134 mHeadsUpManager.addListener(mScrimController); 1135 mStackScroller.setScrimController(mScrimController); 1136 mDozeScrimController = new DozeScrimController(mScrimController, context); 1137 1138 // Other icons 1139 mVolumeComponent = getComponent(VolumeComponent.class); 1140 1141 mNotificationPanel.setUserSetupComplete(mUserSetup); 1142 if (UserManager.get(mContext).isUserSwitcherEnabled()) { 1143 createUserSwitcher(); 1144 } 1145 1146 // Set up the quick settings tile panel 1147 View container = mStatusBarWindow.findViewById(R.id.qs_frame); 1148 if (container != null) { 1149 FragmentHostManager fragmentHostManager = FragmentHostManager.get(container); 1150 ExtensionFragmentListener.attachExtensonToFragment(container, QS.TAG, R.id.qs_frame, 1151 Dependency.get(ExtensionController.class).newExtension(QS.class) 1152 .withPlugin(QS.class) 1153 .withDefault(() -> new QSFragment()) 1154 .build()); 1155 final QSTileHost qsh = SystemUIFactory.getInstance().createQSTileHost(mContext, this, 1156 mIconController); 1157 mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow, 1158 mScrimController); 1159 fragmentHostManager.addTagListener(QS.TAG, (tag, f) -> { 1160 QS qs = (QS) f; 1161 if (qs instanceof QSFragment) { 1162 ((QSFragment) qs).setHost(qsh); 1163 mQSPanel = ((QSFragment) qs).getQsPanel(); 1164 mQSPanel.setBrightnessMirror(mBrightnessMirrorController); 1165 mKeyguardStatusBar.setQSPanel(mQSPanel); 1166 } 1167 }); 1168 } 1169 1170 mReportRejectedTouch = mStatusBarWindow.findViewById(R.id.report_rejected_touch); 1171 if (mReportRejectedTouch != null) { 1172 updateReportRejectedTouchVisibility(); 1173 mReportRejectedTouch.setOnClickListener(v -> { 1174 Uri session = mFalsingManager.reportRejectedTouch(); 1175 if (session == null) { return; } 1176 1177 StringWriter message = new StringWriter(); 1178 message.write("Build info: "); 1179 message.write(SystemProperties.get("ro.build.description")); 1180 message.write("\nSerial number: "); 1181 message.write(SystemProperties.get("ro.serialno")); 1182 message.write("\n"); 1183 1184 PrintWriter falsingPw = new PrintWriter(message); 1185 FalsingLog.dump(falsingPw); 1186 falsingPw.flush(); 1187 1188 startActivityDismissingKeyguard(Intent.createChooser(new Intent(Intent.ACTION_SEND) 1189 .setType("*/*") 1190 .putExtra(Intent.EXTRA_SUBJECT, "Rejected touch report") 1191 .putExtra(Intent.EXTRA_STREAM, session) 1192 .putExtra(Intent.EXTRA_TEXT, message.toString()), 1193 "Share rejected touch report") 1194 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), 1195 true /* onlyProvisioned */, true /* dismissShade */); 1196 }); 1197 } 1198 1199 1200 PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); 1201 if (!pm.isScreenOn()) { 1202 mBroadcastReceiver.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF)); 1203 } 1204 mGestureWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, 1205 "GestureWakeLock"); 1206 mVibrator = mContext.getSystemService(Vibrator.class); 1207 int[] pattern = mContext.getResources().getIntArray( 1208 R.array.config_cameraLaunchGestureVibePattern); 1209 mCameraLaunchGestureVibePattern = new long[pattern.length]; 1210 for (int i = 0; i < pattern.length; i++) { 1211 mCameraLaunchGestureVibePattern[i] = pattern[i]; 1212 } 1213 1214 // receive broadcasts 1215 IntentFilter filter = new IntentFilter(); 1216 filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); 1217 filter.addAction(Intent.ACTION_SCREEN_OFF); 1218 filter.addAction(DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG); 1219 context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null); 1220 1221 IntentFilter demoFilter = new IntentFilter(); 1222 if (DEBUG_MEDIA_FAKE_ARTWORK) { 1223 demoFilter.addAction(ACTION_FAKE_ARTWORK); 1224 } 1225 demoFilter.addAction(ACTION_DEMO); 1226 context.registerReceiverAsUser(mDemoReceiver, UserHandle.ALL, demoFilter, 1227 android.Manifest.permission.DUMP, null); 1228 1229 // listen for USER_SETUP_COMPLETE setting (per-user) 1230 mDeviceProvisionedController.addCallback(mUserSetupObserver); 1231 mUserSetupObserver.onUserSetupChanged(); 1232 1233 // disable profiling bars, since they overlap and clutter the output on app windows 1234 ThreadedRenderer.overrideProperty("disableProfileBars", "true"); 1235 1236 // Private API call to make the shadows look better for Recents 1237 ThreadedRenderer.overrideProperty("ambientRatio", String.valueOf(1.5f)); 1238 } 1239 1240 protected void createNavigationBar() { 1241 mNavigationBarView = NavigationBarFragment.create(mContext, (tag, fragment) -> { 1242 mNavigationBar = (NavigationBarFragment) fragment; 1243 if (mLightBarController != null) { 1244 mNavigationBar.setLightBarController(mLightBarController); 1245 } 1246 mNavigationBar.setCurrentSysuiVisibility(mSystemUiVisibility); 1247 }); 1248 } 1249 1250 /** 1251 * Returns the {@link android.view.View.OnTouchListener} that will be invoked when the 1252 * background window of the status bar is clicked. 1253 */ 1254 protected View.OnTouchListener getStatusBarWindowTouchListener() { 1255 return (v, event) -> { 1256 checkUserAutohide(v, event); 1257 checkRemoteInputOutside(event); 1258 if (event.getAction() == MotionEvent.ACTION_DOWN) { 1259 if (mExpandedVisible) { 1260 animateCollapsePanels(); 1261 } 1262 } 1263 return mStatusBarWindow.onTouchEvent(event); 1264 }; 1265 } 1266 1267 private void inflateShelf() { 1268 mNotificationShelf = 1269 (NotificationShelf) LayoutInflater.from(mContext).inflate( 1270 R.layout.status_bar_notification_shelf, mStackScroller, false); 1271 mNotificationShelf.setOnActivatedListener(this); 1272 mStackScroller.setShelf(mNotificationShelf); 1273 mNotificationShelf.setOnClickListener(mGoToLockedShadeListener); 1274 mNotificationShelf.setStatusBarState(mState); 1275 } 1276 1277 public void onDensityOrFontScaleChanged() { 1278 // start old BaseStatusBar.onDensityOrFontScaleChanged(). 1279 if (!KeyguardUpdateMonitor.getInstance(mContext).isSwitchingUser()) { 1280 updateNotificationsOnDensityOrFontScaleChanged(); 1281 } else { 1282 mReinflateNotificationsOnUserSwitched = true; 1283 } 1284 // end old BaseStatusBar.onDensityOrFontScaleChanged(). 1285 mScrimController.onDensityOrFontScaleChanged(); 1286 // TODO: Remove this. 1287 if (mStatusBarView != null) mStatusBarView.onDensityOrFontScaleChanged(); 1288 if (mBrightnessMirrorController != null) { 1289 mBrightnessMirrorController.onDensityOrFontScaleChanged(); 1290 } 1291 mStatusBarKeyguardViewManager.onDensityOrFontScaleChanged(); 1292 // TODO: Bring these out of StatusBar. 1293 ((UserInfoControllerImpl) Dependency.get(UserInfoController.class)) 1294 .onDensityOrFontScaleChanged(); 1295 Dependency.get(UserSwitcherController.class).onDensityOrFontScaleChanged(); 1296 if (mKeyguardUserSwitcher != null) { 1297 mKeyguardUserSwitcher.onDensityOrFontScaleChanged(); 1298 } 1299 mNotificationIconAreaController.onDensityOrFontScaleChanged(mContext); 1300 1301 reevaluateStyles(); 1302 } 1303 1304 public void onOverlayChanged() { 1305 reevaluateStyles(); 1306 1307 // Clock and bottom icons 1308 mNotificationPanel.onOverlayChanged(); 1309 1310 // Recreate Indication controller because internal references changed 1311 // TODO: unregister callbacks before recreating 1312 mKeyguardIndicationController = 1313 SystemUIFactory.getInstance().createKeyguardIndicationController(mContext, 1314 mStatusBarWindow.findViewById(R.id.keyguard_indication_area), 1315 mNotificationPanel.getLockIcon()); 1316 mNotificationPanel.setKeyguardIndicationController(mKeyguardIndicationController); 1317 mKeyguardIndicationController 1318 .setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager); 1319 mKeyguardIndicationController.setVisible(mState == StatusBarState.KEYGUARD); 1320 mKeyguardIndicationController.setDozing(mDozing); 1321 if (mBrightnessMirrorController != null) { 1322 mBrightnessMirrorController.onOverlayChanged(); 1323 } 1324 if (mStatusBarKeyguardViewManager != null) { 1325 mStatusBarKeyguardViewManager.onOverlayChanged(); 1326 } 1327 } 1328 1329 protected void reevaluateStyles() { 1330 inflateSignalClusters(); 1331 inflateDismissView(); 1332 updateClearAll(); 1333 inflateEmptyShadeView(); 1334 updateEmptyShadeView(); 1335 } 1336 1337 private void updateNotificationsOnDensityOrFontScaleChanged() { 1338 ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications(); 1339 for (int i = 0; i < activeNotifications.size(); i++) { 1340 Entry entry = activeNotifications.get(i); 1341 boolean exposedGuts = mNotificationGutsExposed != null 1342 && entry.row.getGuts() == mNotificationGutsExposed; 1343 entry.row.onDensityOrFontScaleChanged(); 1344 if (exposedGuts) { 1345 mNotificationGutsExposed = entry.row.getGuts(); 1346 bindGuts(entry.row, mGutsMenuItem); 1347 } 1348 } 1349 } 1350 1351 private void inflateSignalClusters() { 1352 reinflateSignalCluster(mKeyguardStatusBar); 1353 } 1354 1355 public static SignalClusterView reinflateSignalCluster(View view) { 1356 Context context = view.getContext(); 1357 SignalClusterView signalCluster = 1358 (SignalClusterView) view.findViewById(R.id.signal_cluster); 1359 if (signalCluster != null) { 1360 ViewParent parent = signalCluster.getParent(); 1361 if (parent instanceof ViewGroup) { 1362 ViewGroup viewParent = (ViewGroup) parent; 1363 int index = viewParent.indexOfChild(signalCluster); 1364 viewParent.removeView(signalCluster); 1365 SignalClusterView newCluster = (SignalClusterView) LayoutInflater.from(context) 1366 .inflate(R.layout.signal_cluster_view, viewParent, false); 1367 ViewGroup.MarginLayoutParams layoutParams = 1368 (ViewGroup.MarginLayoutParams) viewParent.getLayoutParams(); 1369 layoutParams.setMarginsRelative( 1370 context.getResources().getDimensionPixelSize( 1371 R.dimen.signal_cluster_margin_start), 1372 0, 0, 0); 1373 newCluster.setLayoutParams(layoutParams); 1374 viewParent.addView(newCluster, index); 1375 return newCluster; 1376 } 1377 return signalCluster; 1378 } 1379 return null; 1380 } 1381 1382 private void inflateEmptyShadeView() { 1383 if (mStackScroller == null) { 1384 return; 1385 } 1386 mEmptyShadeView = (EmptyShadeView) LayoutInflater.from(mContext).inflate( 1387 R.layout.status_bar_no_notifications, mStackScroller, false); 1388 mStackScroller.setEmptyShadeView(mEmptyShadeView); 1389 } 1390 1391 private void inflateDismissView() { 1392 if (!mClearAllEnabled || mStackScroller == null) { 1393 return; 1394 } 1395 1396 mDismissView = (DismissView) LayoutInflater.from(mContext).inflate( 1397 R.layout.status_bar_notification_dismiss_all, mStackScroller, false); 1398 mDismissView.setOnButtonClickListener(new View.OnClickListener() { 1399 @Override 1400 public void onClick(View v) { 1401 mMetricsLogger.action(MetricsEvent.ACTION_DISMISS_ALL_NOTES); 1402 clearAllNotifications(); 1403 } 1404 }); 1405 mStackScroller.setDismissView(mDismissView); 1406 } 1407 1408 protected void createUserSwitcher() { 1409 mKeyguardUserSwitcher = new KeyguardUserSwitcher(mContext, 1410 (ViewStub) mStatusBarWindow.findViewById(R.id.keyguard_user_switcher), 1411 mKeyguardStatusBar, mNotificationPanel); 1412 } 1413 1414 protected void inflateStatusBarWindow(Context context) { 1415 mStatusBarWindow = (StatusBarWindowView) View.inflate(context, 1416 R.layout.super_status_bar, null); 1417 } 1418 1419 public void clearAllNotifications() { 1420 1421 // animate-swipe all dismissable notifications, then animate the shade closed 1422 int numChildren = mStackScroller.getChildCount(); 1423 1424 final ArrayList<View> viewsToHide = new ArrayList<View>(numChildren); 1425 final ArrayList<ExpandableNotificationRow> viewsToRemove = new ArrayList<>(numChildren); 1426 for (int i = 0; i < numChildren; i++) { 1427 final View child = mStackScroller.getChildAt(i); 1428 if (child instanceof ExpandableNotificationRow) { 1429 ExpandableNotificationRow row = (ExpandableNotificationRow) child; 1430 boolean parentVisible = false; 1431 boolean hasClipBounds = child.getClipBounds(mTmpRect); 1432 if (mStackScroller.canChildBeDismissed(child)) { 1433 viewsToRemove.add(row); 1434 if (child.getVisibility() == View.VISIBLE 1435 && (!hasClipBounds || mTmpRect.height() > 0)) { 1436 viewsToHide.add(child); 1437 parentVisible = true; 1438 } 1439 } else if (child.getVisibility() == View.VISIBLE 1440 && (!hasClipBounds || mTmpRect.height() > 0)) { 1441 parentVisible = true; 1442 } 1443 List<ExpandableNotificationRow> children = row.getNotificationChildren(); 1444 if (children != null) { 1445 for (ExpandableNotificationRow childRow : children) { 1446 viewsToRemove.add(childRow); 1447 if (parentVisible && row.areChildrenExpanded() 1448 && mStackScroller.canChildBeDismissed(childRow)) { 1449 hasClipBounds = childRow.getClipBounds(mTmpRect); 1450 if (childRow.getVisibility() == View.VISIBLE 1451 && (!hasClipBounds || mTmpRect.height() > 0)) { 1452 viewsToHide.add(childRow); 1453 } 1454 } 1455 } 1456 } 1457 } 1458 } 1459 if (viewsToRemove.isEmpty()) { 1460 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); 1461 return; 1462 } 1463 1464 addPostCollapseAction(new Runnable() { 1465 @Override 1466 public void run() { 1467 mStackScroller.setDismissAllInProgress(false); 1468 for (ExpandableNotificationRow rowToRemove : viewsToRemove) { 1469 if (mStackScroller.canChildBeDismissed(rowToRemove)) { 1470 removeNotification(rowToRemove.getEntry().key, null); 1471 } else { 1472 rowToRemove.resetTranslation(); 1473 } 1474 } 1475 try { 1476 mBarService.onClearAllNotifications(mCurrentUserId); 1477 } catch (Exception ex) { } 1478 } 1479 }); 1480 1481 performDismissAllAnimations(viewsToHide); 1482 1483 } 1484 1485 private void performDismissAllAnimations(ArrayList<View> hideAnimatedList) { 1486 Runnable animationFinishAction = new Runnable() { 1487 @Override 1488 public void run() { 1489 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); 1490 } 1491 }; 1492 1493 // let's disable our normal animations 1494 mStackScroller.setDismissAllInProgress(true); 1495 1496 // Decrease the delay for every row we animate to give the sense of 1497 // accelerating the swipes 1498 int rowDelayDecrement = 10; 1499 int currentDelay = 140; 1500 int totalDelay = 180; 1501 int numItems = hideAnimatedList.size(); 1502 for (int i = numItems - 1; i >= 0; i--) { 1503 View view = hideAnimatedList.get(i); 1504 Runnable endRunnable = null; 1505 if (i == 0) { 1506 endRunnable = animationFinishAction; 1507 } 1508 mStackScroller.dismissViewAnimated(view, endRunnable, totalDelay, 260); 1509 currentDelay = Math.max(50, currentDelay - rowDelayDecrement); 1510 totalDelay += currentDelay; 1511 } 1512 } 1513 1514 protected void setZenMode(int mode) { 1515 // start old BaseStatusBar.setZenMode(). 1516 if (isDeviceProvisioned()) { 1517 mZenMode = mode; 1518 updateNotifications(); 1519 } 1520 // end old BaseStatusBar.setZenMode(). 1521 } 1522 1523 protected void startKeyguard() { 1524 Trace.beginSection("StatusBar#startKeyguard"); 1525 KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class); 1526 mFingerprintUnlockController = new FingerprintUnlockController(mContext, 1527 mDozeScrimController, keyguardViewMediator, 1528 mScrimController, this, UnlockMethodCache.getInstance(mContext)); 1529 mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this, 1530 getBouncerContainer(), mScrimController, 1531 mFingerprintUnlockController); 1532 mKeyguardIndicationController 1533 .setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager); 1534 mFingerprintUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager); 1535 mRemoteInputController.addCallback(mStatusBarKeyguardViewManager); 1536 1537 mRemoteInputController.addCallback(new RemoteInputController.Callback() { 1538 @Override 1539 public void onRemoteInputSent(Entry entry) { 1540 if (FORCE_REMOTE_INPUT_HISTORY && mKeysKeptForRemoteInput.contains(entry.key)) { 1541 removeNotification(entry.key, null); 1542 } else if (mRemoteInputEntriesToRemoveOnCollapse.contains(entry)) { 1543 // We're currently holding onto this notification, but from the apps point of 1544 // view it is already canceled, so we'll need to cancel it on the apps behalf 1545 // after sending - unless the app posts an update in the mean time, so wait a 1546 // bit. 1547 mHandler.postDelayed(() -> { 1548 if (mRemoteInputEntriesToRemoveOnCollapse.remove(entry)) { 1549 removeNotification(entry.key, null); 1550 } 1551 }, REMOTE_INPUT_KEPT_ENTRY_AUTO_CANCEL_DELAY); 1552 } 1553 } 1554 }); 1555 1556 mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback(); 1557 mLightBarController.setFingerprintUnlockController(mFingerprintUnlockController); 1558 Trace.endSection(); 1559 } 1560 1561 protected View getStatusBarView() { 1562 return mStatusBarView; 1563 } 1564 1565 public StatusBarWindowView getStatusBarWindow() { 1566 return mStatusBarWindow; 1567 } 1568 1569 protected ViewGroup getBouncerContainer() { 1570 return mStatusBarWindow; 1571 } 1572 1573 public int getStatusBarHeight() { 1574 if (mNaturalBarHeight < 0) { 1575 final Resources res = mContext.getResources(); 1576 mNaturalBarHeight = 1577 res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); 1578 } 1579 return mNaturalBarHeight; 1580 } 1581 1582 protected boolean toggleSplitScreenMode(int metricsDockAction, int metricsUndockAction) { 1583 if (mRecents == null) { 1584 return false; 1585 } 1586 int dockSide = WindowManagerProxy.getInstance().getDockSide(); 1587 if (dockSide == WindowManager.DOCKED_INVALID) { 1588 return mRecents.dockTopTask(NavigationBarGestureHelper.DRAG_MODE_NONE, 1589 ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, null, metricsDockAction); 1590 } else { 1591 Divider divider = getComponent(Divider.class); 1592 if (divider != null && divider.isMinimized() && !divider.isHomeStackResizable()) { 1593 // Undocking from the minimized state is not supported 1594 return false; 1595 } else { 1596 EventBus.getDefault().send(new UndockingTaskEvent()); 1597 if (metricsUndockAction != -1) { 1598 mMetricsLogger.action(metricsUndockAction); 1599 } 1600 } 1601 } 1602 return true; 1603 } 1604 1605 void awakenDreams() { 1606 SystemServicesProxy.getInstance(mContext).awakenDreamsAsync(); 1607 } 1608 1609 public UserHandle getCurrentUserHandle() { 1610 return new UserHandle(mCurrentUserId); 1611 } 1612 1613 public void addNotification(StatusBarNotification notification, RankingMap ranking) 1614 throws InflationException { 1615 String key = notification.getKey(); 1616 if (DEBUG) Log.d(TAG, "addNotification key=" + key); 1617 1618 mNotificationData.updateRanking(ranking); 1619 Entry shadeEntry = createNotificationViews(notification); 1620 boolean isHeadsUped = shouldPeek(shadeEntry); 1621 if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) { 1622 if (shouldSuppressFullScreenIntent(key)) { 1623 if (DEBUG) { 1624 Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + key); 1625 } 1626 } else if (mNotificationData.getImportance(key) 1627 < NotificationManager.IMPORTANCE_HIGH) { 1628 if (DEBUG) { 1629 Log.d(TAG, "No Fullscreen intent: not important enough: " 1630 + key); 1631 } 1632 } else { 1633 // Stop screensaver if the notification has a full-screen intent. 1634 // (like an incoming phone call) 1635 awakenDreams(); 1636 1637 // not immersive & a full-screen alert should be shown 1638 if (DEBUG) 1639 Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent"); 1640 try { 1641 EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION, 1642 key); 1643 notification.getNotification().fullScreenIntent.send(); 1644 shadeEntry.notifyFullScreenIntentLaunched(); 1645 mMetricsLogger.count("note_fullscreen", 1); 1646 } catch (PendingIntent.CanceledException e) { 1647 } 1648 } 1649 } 1650 abortExistingInflation(key); 1651 1652 mForegroundServiceController.addNotification(notification, 1653 mNotificationData.getImportance(key)); 1654 1655 mPendingNotifications.put(key, shadeEntry); 1656 } 1657 1658 private void abortExistingInflation(String key) { 1659 if (mPendingNotifications.containsKey(key)) { 1660 Entry entry = mPendingNotifications.get(key); 1661 entry.abortTask(); 1662 mPendingNotifications.remove(key); 1663 } 1664 Entry addedEntry = mNotificationData.get(key); 1665 if (addedEntry != null) { 1666 addedEntry.abortTask(); 1667 } 1668 } 1669 1670 private void addEntry(Entry shadeEntry) { 1671 boolean isHeadsUped = shouldPeek(shadeEntry); 1672 if (isHeadsUped) { 1673 mHeadsUpManager.showNotification(shadeEntry); 1674 // Mark as seen immediately 1675 setNotificationShown(shadeEntry.notification); 1676 } 1677 addNotificationViews(shadeEntry); 1678 // Recalculate the position of the sliding windows and the titles. 1679 setAreThereNotifications(); 1680 } 1681 1682 @Override 1683 public void handleInflationException(StatusBarNotification notification, Exception e) { 1684 handleNotificationError(notification, e.getMessage()); 1685 } 1686 1687 @Override 1688 public void onAsyncInflationFinished(Entry entry) { 1689 mPendingNotifications.remove(entry.key); 1690 // If there was an async task started after the removal, we don't want to add it back to 1691 // the list, otherwise we might get leaks. 1692 boolean isNew = mNotificationData.get(entry.key) == null; 1693 if (isNew && !entry.row.isRemoved()) { 1694 addEntry(entry); 1695 } else if (!isNew && entry.row.hasLowPriorityStateUpdated()) { 1696 mVisualStabilityManager.onLowPriorityUpdated(entry); 1697 updateNotificationShade(); 1698 } 1699 entry.row.setLowPriorityStateUpdated(false); 1700 } 1701 1702 private boolean shouldSuppressFullScreenIntent(String key) { 1703 if (isDeviceInVrMode()) { 1704 return true; 1705 } 1706 1707 if (mPowerManager.isInteractive()) { 1708 return mNotificationData.shouldSuppressScreenOn(key); 1709 } else { 1710 return mNotificationData.shouldSuppressScreenOff(key); 1711 } 1712 } 1713 1714 protected void updateNotificationRanking(RankingMap ranking) { 1715 mNotificationData.updateRanking(ranking); 1716 updateNotifications(); 1717 } 1718 1719 public void removeNotification(String key, RankingMap ranking) { 1720 boolean deferRemoval = false; 1721 abortExistingInflation(key); 1722 if (mHeadsUpManager.isHeadsUp(key)) { 1723 // A cancel() in repsonse to a remote input shouldn't be delayed, as it makes the 1724 // sending look longer than it takes. 1725 // Also we should not defer the removal if reordering isn't allowed since otherwise 1726 // some notifications can't disappear before the panel is closed. 1727 boolean ignoreEarliestRemovalTime = mRemoteInputController.isSpinning(key) 1728 && !FORCE_REMOTE_INPUT_HISTORY 1729 || !mVisualStabilityManager.isReorderingAllowed(); 1730 deferRemoval = !mHeadsUpManager.removeNotification(key, ignoreEarliestRemovalTime); 1731 } 1732 if (key.equals(mMediaNotificationKey)) { 1733 clearCurrentMediaNotification(); 1734 updateMediaMetaData(true, true); 1735 } 1736 if (FORCE_REMOTE_INPUT_HISTORY && mRemoteInputController.isSpinning(key)) { 1737 Entry entry = mNotificationData.get(key); 1738 StatusBarNotification sbn = entry.notification; 1739 1740 Notification.Builder b = Notification.Builder 1741 .recoverBuilder(mContext, sbn.getNotification().clone()); 1742 CharSequence[] oldHistory = sbn.getNotification().extras 1743 .getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY); 1744 CharSequence[] newHistory; 1745 if (oldHistory == null) { 1746 newHistory = new CharSequence[1]; 1747 } else { 1748 newHistory = new CharSequence[oldHistory.length + 1]; 1749 for (int i = 0; i < oldHistory.length; i++) { 1750 newHistory[i + 1] = oldHistory[i]; 1751 } 1752 } 1753 newHistory[0] = String.valueOf(entry.remoteInputText); 1754 b.setRemoteInputHistory(newHistory); 1755 1756 Notification newNotification = b.build(); 1757 1758 // Undo any compatibility view inflation 1759 newNotification.contentView = sbn.getNotification().contentView; 1760 newNotification.bigContentView = sbn.getNotification().bigContentView; 1761 newNotification.headsUpContentView = sbn.getNotification().headsUpContentView; 1762 1763 StatusBarNotification newSbn = new StatusBarNotification(sbn.getPackageName(), 1764 sbn.getOpPkg(), 1765 sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(), 1766 newNotification, sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime()); 1767 boolean updated = false; 1768 try { 1769 updateNotification(newSbn, null); 1770 updated = true; 1771 } catch (InflationException e) { 1772 deferRemoval = false; 1773 } 1774 if (updated) { 1775 mKeysKeptForRemoteInput.add(entry.key); 1776 return; 1777 } 1778 } 1779 if (deferRemoval) { 1780 mLatestRankingMap = ranking; 1781 mHeadsUpEntriesToRemoveOnSwitch.add(mHeadsUpManager.getEntry(key)); 1782 return; 1783 } 1784 Entry entry = mNotificationData.get(key); 1785 1786 if (entry != null && mRemoteInputController.isRemoteInputActive(entry) 1787 && (entry.row != null && !entry.row.isDismissed())) { 1788 mLatestRankingMap = ranking; 1789 mRemoteInputEntriesToRemoveOnCollapse.add(entry); 1790 return; 1791 } 1792 if (entry != null && mNotificationGutsExposed != null 1793 && mNotificationGutsExposed == entry.row.getGuts() && entry.row.getGuts() != null 1794 && !entry.row.getGuts().isLeavebehind()) { 1795 Log.w(TAG, "Keeping notification because it's showing guts. " + key); 1796 mLatestRankingMap = ranking; 1797 mKeyToRemoveOnGutsClosed = key; 1798 return; 1799 } 1800 1801 if (entry != null) { 1802 mForegroundServiceController.removeNotification(entry.notification); 1803 } 1804 1805 if (entry != null && entry.row != null) { 1806 entry.row.setRemoved(); 1807 mStackScroller.cleanUpViewState(entry.row); 1808 } 1809 // Let's remove the children if this was a summary 1810 handleGroupSummaryRemoved(key, ranking); 1811 StatusBarNotification old = removeNotificationViews(key, ranking); 1812 if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old); 1813 1814 if (old != null) { 1815 if (CLOSE_PANEL_WHEN_EMPTIED && !hasActiveNotifications() 1816 && !mNotificationPanel.isTracking() && !mNotificationPanel.isQsExpanded()) { 1817 if (mState == StatusBarState.SHADE) { 1818 animateCollapsePanels(); 1819 } else if (mState == StatusBarState.SHADE_LOCKED && !isCollapsing()) { 1820 goToKeyguard(); 1821 } 1822 } 1823 } 1824 setAreThereNotifications(); 1825 } 1826 1827 /** 1828 * Ensures that the group children are cancelled immediately when the group summary is cancelled 1829 * instead of waiting for the notification manager to send all cancels. Otherwise this could 1830 * lead to flickers. 1831 * 1832 * This also ensures that the animation looks nice and only consists of a single disappear 1833 * animation instead of multiple. 1834 * 1835 * @param key the key of the notification was removed 1836 * @param ranking the current ranking 1837 */ 1838 private void handleGroupSummaryRemoved(String key, 1839 RankingMap ranking) { 1840 Entry entry = mNotificationData.get(key); 1841 if (entry != null && entry.row != null 1842 && entry.row.isSummaryWithChildren()) { 1843 if (entry.notification.getOverrideGroupKey() != null && !entry.row.isDismissed()) { 1844 // We don't want to remove children for autobundled notifications as they are not 1845 // always cancelled. We only remove them if they were dismissed by the user. 1846 return; 1847 } 1848 List<ExpandableNotificationRow> notificationChildren = 1849 entry.row.getNotificationChildren(); 1850 ArrayList<ExpandableNotificationRow> toRemove = new ArrayList<>(); 1851 for (int i = 0; i < notificationChildren.size(); i++) { 1852 ExpandableNotificationRow row = notificationChildren.get(i); 1853 if ((row.getStatusBarNotification().getNotification().flags 1854 & Notification.FLAG_FOREGROUND_SERVICE) != 0) { 1855 // the child is a forground service notification which we can't remove! 1856 continue; 1857 } 1858 toRemove.add(row); 1859 row.setKeepInParent(true); 1860 // we need to set this state earlier as otherwise we might generate some weird 1861 // animations 1862 row.setRemoved(); 1863 } 1864 } 1865 } 1866 1867 protected void performRemoveNotification(StatusBarNotification n) { 1868 Entry entry = mNotificationData.get(n.getKey()); 1869 if (mRemoteInputController.isRemoteInputActive(entry)) { 1870 mRemoteInputController.removeRemoteInput(entry, null); 1871 } 1872 // start old BaseStatusBar.performRemoveNotification. 1873 final String pkg = n.getPackageName(); 1874 final String tag = n.getTag(); 1875 final int id = n.getId(); 1876 final int userId = n.getUserId(); 1877 try { 1878 mBarService.onNotificationClear(pkg, tag, id, userId); 1879 if (FORCE_REMOTE_INPUT_HISTORY 1880 && mKeysKeptForRemoteInput.contains(n.getKey())) { 1881 mKeysKeptForRemoteInput.remove(n.getKey()); 1882 } 1883 removeNotification(n.getKey(), null); 1884 1885 } catch (RemoteException ex) { 1886 // system process is dead if we're here. 1887 } 1888 if (mStackScroller.hasPulsingNotifications() && mHeadsUpManager.getAllEntries().isEmpty()) { 1889 // We were showing a pulse for a notification, but no notifications are pulsing anymore. 1890 // Finish the pulse. 1891 mDozeScrimController.pulseOutNow(); 1892 } 1893 // end old BaseStatusBar.performRemoveNotification. 1894 } 1895 1896 private void updateNotificationShade() { 1897 if (mStackScroller == null) return; 1898 1899 // Do not modify the notifications during collapse. 1900 if (isCollapsing()) { 1901 addPostCollapseAction(new Runnable() { 1902 @Override 1903 public void run() { 1904 updateNotificationShade(); 1905 } 1906 }); 1907 return; 1908 } 1909 1910 ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications(); 1911 ArrayList<ExpandableNotificationRow> toShow = new ArrayList<>(activeNotifications.size()); 1912 final int N = activeNotifications.size(); 1913 for (int i=0; i<N; i++) { 1914 Entry ent = activeNotifications.get(i); 1915 if (ent.row.isDismissed() || ent.row.isRemoved()) { 1916 // we don't want to update removed notifications because they could 1917 // temporarily become children if they were isolated before. 1918 continue; 1919 } 1920 int userId = ent.notification.getUserId(); 1921 1922 // Display public version of the notification if we need to redact. 1923 boolean devicePublic = isLockscreenPublicMode(mCurrentUserId); 1924 boolean userPublic = devicePublic || isLockscreenPublicMode(userId); 1925 boolean needsRedaction = needsRedaction(ent); 1926 boolean sensitive = userPublic && needsRedaction; 1927 boolean deviceSensitive = devicePublic 1928 && !userAllowsPrivateNotificationsInPublic(mCurrentUserId); 1929 if (sensitive) { 1930 updatePublicContentView(ent, ent.notification); 1931 } 1932 ent.row.setSensitive(sensitive, deviceSensitive); 1933 ent.row.setNeedsRedaction(needsRedaction); 1934 if (mGroupManager.isChildInGroupWithSummary(ent.row.getStatusBarNotification())) { 1935 ExpandableNotificationRow summary = mGroupManager.getGroupSummary( 1936 ent.row.getStatusBarNotification()); 1937 List<ExpandableNotificationRow> orderedChildren = 1938 mTmpChildOrderMap.get(summary); 1939 if (orderedChildren == null) { 1940 orderedChildren = new ArrayList<>(); 1941 mTmpChildOrderMap.put(summary, orderedChildren); 1942 } 1943 orderedChildren.add(ent.row); 1944 } else { 1945 toShow.add(ent.row); 1946 } 1947 1948 } 1949 1950 ArrayList<ExpandableNotificationRow> toRemove = new ArrayList<>(); 1951 for (int i=0; i< mStackScroller.getChildCount(); i++) { 1952 View child = mStackScroller.getChildAt(i); 1953 if (!toShow.contains(child) && child instanceof ExpandableNotificationRow) { 1954 toRemove.add((ExpandableNotificationRow) child); 1955 } 1956 } 1957 1958 for (ExpandableNotificationRow remove : toRemove) { 1959 if (mGroupManager.isChildInGroupWithSummary(remove.getStatusBarNotification())) { 1960 // we are only transfering this notification to its parent, don't generate an animation 1961 mStackScroller.setChildTransferInProgress(true); 1962 } 1963 if (remove.isSummaryWithChildren()) { 1964 remove.removeAllChildren(); 1965 } 1966 mStackScroller.removeView(remove); 1967 mStackScroller.setChildTransferInProgress(false); 1968 } 1969 1970 removeNotificationChildren(); 1971 1972 for (int i=0; i<toShow.size(); i++) { 1973 View v = toShow.get(i); 1974 if (v.getParent() == null) { 1975 mVisualStabilityManager.notifyViewAddition(v); 1976 mStackScroller.addView(v); 1977 } 1978 } 1979 1980 addNotificationChildrenAndSort(); 1981 1982 // So after all this work notifications still aren't sorted correctly. 1983 // Let's do that now by advancing through toShow and mStackScroller in 1984 // lock-step, making sure mStackScroller matches what we see in toShow. 1985 int j = 0; 1986 for (int i = 0; i < mStackScroller.getChildCount(); i++) { 1987 View child = mStackScroller.getChildAt(i); 1988 if (!(child instanceof ExpandableNotificationRow)) { 1989 // We don't care about non-notification views. 1990 continue; 1991 } 1992 1993 ExpandableNotificationRow targetChild = toShow.get(j); 1994 if (child != targetChild) { 1995 // Oops, wrong notification at this position. Put the right one 1996 // here and advance both lists. 1997 if (mVisualStabilityManager.canReorderNotification(targetChild)) { 1998 mStackScroller.changeViewPosition(targetChild, i); 1999 } else { 2000 mVisualStabilityManager.addReorderingAllowedCallback(this); 2001 } 2002 } 2003 j++; 2004 2005 } 2006 2007 mVisualStabilityManager.onReorderingFinished(); 2008 // clear the map again for the next usage 2009 mTmpChildOrderMap.clear(); 2010 2011 updateRowStates(); 2012 updateSpeedBumpIndex(); 2013 updateClearAll(); 2014 updateEmptyShadeView(); 2015 2016 updateQsExpansionEnabled(); 2017 2018 // Let's also update the icons 2019 mNotificationIconAreaController.updateNotificationIcons(mNotificationData); 2020 } 2021 2022 /** @return true if the entry needs redaction when on the lockscreen. */ 2023 private boolean needsRedaction(Entry ent) { 2024 int userId = ent.notification.getUserId(); 2025 2026 boolean currentUserWantsRedaction = !userAllowsPrivateNotificationsInPublic(mCurrentUserId); 2027 boolean notiUserWantsRedaction = !userAllowsPrivateNotificationsInPublic(userId); 2028 boolean redactedLockscreen = currentUserWantsRedaction || notiUserWantsRedaction; 2029 2030 boolean notificationRequestsRedaction = 2031 ent.notification.getNotification().visibility == Notification.VISIBILITY_PRIVATE; 2032 boolean userForcesRedaction = packageHasVisibilityOverride(ent.notification.getKey()); 2033 2034 return userForcesRedaction || notificationRequestsRedaction && redactedLockscreen; 2035 } 2036 2037 /** 2038 * Disable QS if device not provisioned. 2039 * If the user switcher is simple then disable QS during setup because 2040 * the user intends to use the lock screen user switcher, QS in not needed. 2041 */ 2042 private void updateQsExpansionEnabled() { 2043 mNotificationPanel.setQsExpansionEnabled(isDeviceProvisioned() 2044 && (mUserSetup || mUserSwitcherController == null 2045 || !mUserSwitcherController.isSimpleUserSwitcher()) 2046 && ((mDisabled2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) == 0) 2047 && !mDozing 2048 && !ONLY_CORE_APPS); 2049 } 2050 2051 private void addNotificationChildrenAndSort() { 2052 // Let's now add all notification children which are missing 2053 boolean orderChanged = false; 2054 for (int i = 0; i < mStackScroller.getChildCount(); i++) { 2055 View view = mStackScroller.getChildAt(i); 2056 if (!(view instanceof ExpandableNotificationRow)) { 2057 // We don't care about non-notification views. 2058 continue; 2059 } 2060 2061 ExpandableNotificationRow parent = (ExpandableNotificationRow) view; 2062 List<ExpandableNotificationRow> children = parent.getNotificationChildren(); 2063 List<ExpandableNotificationRow> orderedChildren = mTmpChildOrderMap.get(parent); 2064 2065 for (int childIndex = 0; orderedChildren != null && childIndex < orderedChildren.size(); 2066 childIndex++) { 2067 ExpandableNotificationRow childView = orderedChildren.get(childIndex); 2068 if (children == null || !children.contains(childView)) { 2069 if (childView.getParent() != null) { 2070 Log.wtf(TAG, "trying to add a notification child that already has " + 2071 "a parent. class:" + childView.getParent().getClass() + 2072 "\n child: " + childView); 2073 // This shouldn't happen. We can recover by removing it though. 2074 ((ViewGroup) childView.getParent()).removeView(childView); 2075 } 2076 mVisualStabilityManager.notifyViewAddition(childView); 2077 parent.addChildNotification(childView, childIndex); 2078 mStackScroller.notifyGroupChildAdded(childView); 2079 } 2080 } 2081 2082 // Finally after removing and adding has been beformed we can apply the order. 2083 orderChanged |= parent.applyChildOrder(orderedChildren, mVisualStabilityManager, this); 2084 } 2085 if (orderChanged) { 2086 mStackScroller.generateChildOrderChangedEvent(); 2087 } 2088 } 2089 2090 private void removeNotificationChildren() { 2091 // First let's remove all children which don't belong in the parents 2092 ArrayList<ExpandableNotificationRow> toRemove = new ArrayList<>(); 2093 for (int i = 0; i < mStackScroller.getChildCount(); i++) { 2094 View view = mStackScroller.getChildAt(i); 2095 if (!(view instanceof ExpandableNotificationRow)) { 2096 // We don't care about non-notification views. 2097 continue; 2098 } 2099 2100 ExpandableNotificationRow parent = (ExpandableNotificationRow) view; 2101 List<ExpandableNotificationRow> children = parent.getNotificationChildren(); 2102 List<ExpandableNotificationRow> orderedChildren = mTmpChildOrderMap.get(parent); 2103 2104 if (children != null) { 2105 toRemove.clear(); 2106 for (ExpandableNotificationRow childRow : children) { 2107 if ((orderedChildren == null 2108 || !orderedChildren.contains(childRow)) 2109 && !childRow.keepInParent()) { 2110 toRemove.add(childRow); 2111 } 2112 } 2113 for (ExpandableNotificationRow remove : toRemove) { 2114 parent.removeChildNotification(remove); 2115 if (mNotificationData.get(remove.getStatusBarNotification().getKey()) == null) { 2116 // We only want to add an animation if the view is completely removed 2117 // otherwise it's just a transfer 2118 mStackScroller.notifyGroupChildRemoved(remove, 2119 parent.getChildrenContainer()); 2120 } 2121 } 2122 } 2123 } 2124 } 2125 2126 public void addQsTile(ComponentName tile) { 2127 mQSPanel.getHost().addTile(tile); 2128 } 2129 2130 public void remQsTile(ComponentName tile) { 2131 mQSPanel.getHost().removeTile(tile); 2132 } 2133 2134 public void clickTile(ComponentName tile) { 2135 mQSPanel.clickTile(tile); 2136 } 2137 2138 private boolean packageHasVisibilityOverride(String key) { 2139 return mNotificationData.getVisibilityOverride(key) == Notification.VISIBILITY_PRIVATE; 2140 } 2141 2142 private void updateClearAll() { 2143 if (!mClearAllEnabled) { 2144 return; 2145 } 2146 boolean showDismissView = mState != StatusBarState.KEYGUARD 2147 && hasActiveClearableNotifications(); 2148 mStackScroller.updateDismissView(showDismissView); 2149 } 2150 2151 /** 2152 * Return whether there are any clearable notifications 2153 */ 2154 private boolean hasActiveClearableNotifications() { 2155 int childCount = mStackScroller.getChildCount(); 2156 for (int i = 0; i < childCount; i++) { 2157 View child = mStackScroller.getChildAt(i); 2158 if (!(child instanceof ExpandableNotificationRow)) { 2159 continue; 2160 } 2161 if (((ExpandableNotificationRow) child).canViewBeDismissed()) { 2162 return true; 2163 } 2164 } 2165 return false; 2166 } 2167 2168 private void updateEmptyShadeView() { 2169 boolean showEmptyShadeView = 2170 mState != StatusBarState.KEYGUARD && 2171 mNotificationData.getActiveNotifications().size() == 0; 2172 mNotificationPanel.showEmptyShadeView(showEmptyShadeView); 2173 } 2174 2175 private void updateSpeedBumpIndex() { 2176 int speedBumpIndex = 0; 2177 int currentIndex = 0; 2178 final int N = mStackScroller.getChildCount(); 2179 for (int i = 0; i < N; i++) { 2180 View view = mStackScroller.getChildAt(i); 2181 if (view.getVisibility() == View.GONE || !(view instanceof ExpandableNotificationRow)) { 2182 continue; 2183 } 2184 ExpandableNotificationRow row = (ExpandableNotificationRow) view; 2185 currentIndex++; 2186 if (!mNotificationData.isAmbient(row.getStatusBarNotification().getKey())) { 2187 speedBumpIndex = currentIndex; 2188 } 2189 } 2190 boolean noAmbient = speedBumpIndex == N; 2191 mStackScroller.updateSpeedBumpIndex(speedBumpIndex, noAmbient); 2192 } 2193 2194 public static boolean isTopLevelChild(Entry entry) { 2195 return entry.row.getParent() instanceof NotificationStackScrollLayout; 2196 } 2197 2198 protected void updateNotifications() { 2199 mNotificationData.filterAndSort(); 2200 2201 updateNotificationShade(); 2202 } 2203 2204 public void requestNotificationUpdate() { 2205 updateNotifications(); 2206 } 2207 2208 protected void setAreThereNotifications() { 2209 2210 if (SPEW) { 2211 final boolean clearable = hasActiveNotifications() && 2212 hasActiveClearableNotifications(); 2213 Log.d(TAG, "setAreThereNotifications: N=" + 2214 mNotificationData.getActiveNotifications().size() + " any=" + 2215 hasActiveNotifications() + " clearable=" + clearable); 2216 } 2217 2218 if (mStatusBarView != null) { 2219 final View nlo = mStatusBarView.findViewById(R.id.notification_lights_out); 2220 final boolean showDot = hasActiveNotifications() && !areLightsOn(); 2221 if (showDot != (nlo.getAlpha() == 1.0f)) { 2222 if (showDot) { 2223 nlo.setAlpha(0f); 2224 nlo.setVisibility(View.VISIBLE); 2225 } 2226 nlo.animate() 2227 .alpha(showDot ? 1 : 0) 2228 .setDuration(showDot ? 750 : 250) 2229 .setInterpolator(new AccelerateInterpolator(2.0f)) 2230 .setListener(showDot ? null : new AnimatorListenerAdapter() { 2231 @Override 2232 public void onAnimationEnd(Animator _a) { 2233 nlo.setVisibility(View.GONE); 2234 } 2235 }) 2236 .start(); 2237 } 2238 } 2239 2240 findAndUpdateMediaNotifications(); 2241 } 2242 2243 public void findAndUpdateMediaNotifications() { 2244 boolean metaDataChanged = false; 2245 2246 synchronized (mNotificationData) { 2247 ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications(); 2248 final int N = activeNotifications.size(); 2249 2250 // Promote the media notification with a controller in 'playing' state, if any. 2251 Entry mediaNotification = null; 2252 MediaController controller = null; 2253 for (int i = 0; i < N; i++) { 2254 final Entry entry = activeNotifications.get(i); 2255 if (isMediaNotification(entry)) { 2256 final MediaSession.Token token = 2257 entry.notification.getNotification().extras 2258 .getParcelable(Notification.EXTRA_MEDIA_SESSION); 2259 if (token != null) { 2260 MediaController aController = new MediaController(mContext, token); 2261 if (PlaybackState.STATE_PLAYING == 2262 getMediaControllerPlaybackState(aController)) { 2263 if (DEBUG_MEDIA) { 2264 Log.v(TAG, "DEBUG_MEDIA: found mediastyle controller matching " 2265 + entry.notification.getKey()); 2266 } 2267 mediaNotification = entry; 2268 controller = aController; 2269 break; 2270 } 2271 } 2272 } 2273 } 2274 if (mediaNotification == null) { 2275 // Still nothing? OK, let's just look for live media sessions and see if they match 2276 // one of our notifications. This will catch apps that aren't (yet!) using media 2277 // notifications. 2278 2279 if (mMediaSessionManager != null) { 2280 final List<MediaController> sessions 2281 = mMediaSessionManager.getActiveSessionsForUser( 2282 null, 2283 UserHandle.USER_ALL); 2284 2285 for (MediaController aController : sessions) { 2286 if (PlaybackState.STATE_PLAYING == 2287 getMediaControllerPlaybackState(aController)) { 2288 // now to see if we have one like this 2289 final String pkg = aController.getPackageName(); 2290 2291 for (int i = 0; i < N; i++) { 2292 final Entry entry = activeNotifications.get(i); 2293 if (entry.notification.getPackageName().equals(pkg)) { 2294 if (DEBUG_MEDIA) { 2295 Log.v(TAG, "DEBUG_MEDIA: found controller matching " 2296 + entry.notification.getKey()); 2297 } 2298 controller = aController; 2299 mediaNotification = entry; 2300 break; 2301 } 2302 } 2303 } 2304 } 2305 } 2306 } 2307 2308 if (controller != null && !sameSessions(mMediaController, controller)) { 2309 // We have a new media session 2310 clearCurrentMediaNotification(); 2311 mMediaController = controller; 2312 mMediaController.registerCallback(mMediaListener); 2313 mMediaMetadata = mMediaController.getMetadata(); 2314 if (DEBUG_MEDIA) { 2315 Log.v(TAG, "DEBUG_MEDIA: insert listener, receive metadata: " 2316 + mMediaMetadata); 2317 } 2318 2319 if (mediaNotification != null) { 2320 mMediaNotificationKey = mediaNotification.notification.getKey(); 2321 if (DEBUG_MEDIA) { 2322 Log.v(TAG, "DEBUG_MEDIA: Found new media notification: key=" 2323 + mMediaNotificationKey + " controller=" + mMediaController); 2324 } 2325 } 2326 metaDataChanged = true; 2327 } 2328 } 2329 2330 if (metaDataChanged) { 2331 updateNotifications(); 2332 } 2333 updateMediaMetaData(metaDataChanged, true); 2334 } 2335 2336 private int getMediaControllerPlaybackState(MediaController controller) { 2337 if (controller != null) { 2338 final PlaybackState playbackState = controller.getPlaybackState(); 2339 if (playbackState != null) { 2340 return playbackState.getState(); 2341 } 2342 } 2343 return PlaybackState.STATE_NONE; 2344 } 2345 2346 private boolean isPlaybackActive(int state) { 2347 if (state != PlaybackState.STATE_STOPPED 2348 && state != PlaybackState.STATE_ERROR 2349 && state != PlaybackState.STATE_NONE) { 2350 return true; 2351 } 2352 return false; 2353 } 2354 2355 private void clearCurrentMediaNotification() { 2356 mMediaNotificationKey = null; 2357 mMediaMetadata = null; 2358 if (mMediaController != null) { 2359 if (DEBUG_MEDIA) { 2360 Log.v(TAG, "DEBUG_MEDIA: Disconnecting from old controller: " 2361 + mMediaController.getPackageName()); 2362 } 2363 mMediaController.unregisterCallback(mMediaListener); 2364 } 2365 mMediaController = null; 2366 } 2367 2368 private boolean sameSessions(MediaController a, MediaController b) { 2369 if (a == b) return true; 2370 if (a == null) return false; 2371 return a.controlsSameSession(b); 2372 } 2373 2374 /** 2375 * Hide the album artwork that is fading out and release its bitmap. 2376 */ 2377 protected Runnable mHideBackdropFront = new Runnable() { 2378 @Override 2379 public void run() { 2380 if (DEBUG_MEDIA) { 2381 Log.v(TAG, "DEBUG_MEDIA: removing fade layer"); 2382 } 2383 mBackdropFront.setVisibility(View.INVISIBLE); 2384 mBackdropFront.animate().cancel(); 2385 mBackdropFront.setImageDrawable(null); 2386 } 2387 }; 2388 2389 /** 2390 * Refresh or remove lockscreen artwork from media metadata or the lockscreen wallpaper. 2391 */ 2392 public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) { 2393 Trace.beginSection("StatusBar#updateMediaMetaData"); 2394 if (!SHOW_LOCKSCREEN_MEDIA_ARTWORK) { 2395 Trace.endSection(); 2396 return; 2397 } 2398 2399 if (mBackdrop == null) { 2400 Trace.endSection(); 2401 return; // called too early 2402 } 2403 2404 if (mLaunchTransitionFadingAway) { 2405 mBackdrop.setVisibility(View.INVISIBLE); 2406 Trace.endSection(); 2407 return; 2408 } 2409 2410 if (DEBUG_MEDIA) { 2411 Log.v(TAG, "DEBUG_MEDIA: updating album art for notification " + mMediaNotificationKey 2412 + " metadata=" + mMediaMetadata 2413 + " metaDataChanged=" + metaDataChanged 2414 + " state=" + mState); 2415 } 2416 2417 Drawable artworkDrawable = null; 2418 if (mMediaMetadata != null) { 2419 Bitmap artworkBitmap = null; 2420 artworkBitmap = mMediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART); 2421 if (artworkBitmap == null) { 2422 artworkBitmap = mMediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART); 2423 // might still be null 2424 } 2425 if (artworkBitmap != null) { 2426 artworkDrawable = new BitmapDrawable(mBackdropBack.getResources(), artworkBitmap); 2427 } 2428 } 2429 boolean allowWhenShade = false; 2430 if (ENABLE_LOCKSCREEN_WALLPAPER && artworkDrawable == null) { 2431 Bitmap lockWallpaper = mLockscreenWallpaper.getBitmap(); 2432 if (lockWallpaper != null) { 2433 artworkDrawable = new LockscreenWallpaper.WallpaperDrawable( 2434 mBackdropBack.getResources(), lockWallpaper); 2435 // We're in the SHADE mode on the SIM screen - yet we still need to show 2436 // the lockscreen wallpaper in that mode. 2437 allowWhenShade = mStatusBarKeyguardViewManager != null 2438 && mStatusBarKeyguardViewManager.isShowing(); 2439 } 2440 } 2441 2442 boolean hideBecauseOccluded = mStatusBarKeyguardViewManager != null 2443 && mStatusBarKeyguardViewManager.isOccluded(); 2444 2445 final boolean hasArtwork = artworkDrawable != null; 2446 2447 if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK) 2448 && (mState != StatusBarState.SHADE || allowWhenShade) 2449 && mFingerprintUnlockController.getMode() 2450 != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING 2451 && !hideBecauseOccluded) { 2452 // time to show some art! 2453 if (mBackdrop.getVisibility() != View.VISIBLE) { 2454 mBackdrop.setVisibility(View.VISIBLE); 2455 if (allowEnterAnimation) { 2456 mBackdrop.setAlpha(SRC_MIN_ALPHA); 2457 mBackdrop.animate().alpha(1f); 2458 } else { 2459 mBackdrop.animate().cancel(); 2460 mBackdrop.setAlpha(1f); 2461 } 2462 mStatusBarWindowManager.setBackdropShowing(true); 2463 metaDataChanged = true; 2464 if (DEBUG_MEDIA) { 2465 Log.v(TAG, "DEBUG_MEDIA: Fading in album artwork"); 2466 } 2467 } 2468 if (metaDataChanged) { 2469 if (mBackdropBack.getDrawable() != null) { 2470 Drawable drawable = 2471 mBackdropBack.getDrawable().getConstantState() 2472 .newDrawable(mBackdropFront.getResources()).mutate(); 2473 mBackdropFront.setImageDrawable(drawable); 2474 if (mScrimSrcModeEnabled) { 2475 mBackdropFront.getDrawable().mutate().setXfermode(mSrcOverXferMode); 2476 } 2477 mBackdropFront.setAlpha(1f); 2478 mBackdropFront.setVisibility(View.VISIBLE); 2479 } else { 2480 mBackdropFront.setVisibility(View.INVISIBLE); 2481 } 2482 2483 if (DEBUG_MEDIA_FAKE_ARTWORK) { 2484 final int c = 0xFF000000 | (int)(Math.random() * 0xFFFFFF); 2485 Log.v(TAG, String.format("DEBUG_MEDIA: setting new color: 0x%08x", c)); 2486 mBackdropBack.setBackgroundColor(0xFFFFFFFF); 2487 mBackdropBack.setImageDrawable(new ColorDrawable(c)); 2488 } else { 2489 mBackdropBack.setImageDrawable(artworkDrawable); 2490 } 2491 if (mScrimSrcModeEnabled) { 2492 mBackdropBack.getDrawable().mutate().setXfermode(mSrcXferMode); 2493 } 2494 2495 if (mBackdropFront.getVisibility() == View.VISIBLE) { 2496 if (DEBUG_MEDIA) { 2497 Log.v(TAG, "DEBUG_MEDIA: Crossfading album artwork from " 2498 + mBackdropFront.getDrawable() 2499 + " to " 2500 + mBackdropBack.getDrawable()); 2501 } 2502 mBackdropFront.animate() 2503 .setDuration(250) 2504 .alpha(0f).withEndAction(mHideBackdropFront); 2505 } 2506 } 2507 } else { 2508 // need to hide the album art, either because we are unlocked or because 2509 // the metadata isn't there to support it 2510 if (mBackdrop.getVisibility() != View.GONE) { 2511 if (DEBUG_MEDIA) { 2512 Log.v(TAG, "DEBUG_MEDIA: Fading out album artwork"); 2513 } 2514 if (mFingerprintUnlockController.getMode() 2515 == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING 2516 || hideBecauseOccluded) { 2517 2518 // We are unlocking directly - no animation! 2519 mBackdrop.setVisibility(View.GONE); 2520 mBackdropBack.setImageDrawable(null); 2521 mStatusBarWindowManager.setBackdropShowing(false); 2522 } else { 2523 mStatusBarWindowManager.setBackdropShowing(false); 2524 mBackdrop.animate() 2525 .alpha(SRC_MIN_ALPHA) 2526 .setInterpolator(Interpolators.ACCELERATE_DECELERATE) 2527 .setDuration(300) 2528 .setStartDelay(0) 2529 .withEndAction(new Runnable() { 2530 @Override 2531 public void run() { 2532 mBackdrop.setVisibility(View.GONE); 2533 mBackdropFront.animate().cancel(); 2534 mBackdropBack.setImageDrawable(null); 2535 mHandler.post(mHideBackdropFront); 2536 } 2537 }); 2538 if (mKeyguardFadingAway) { 2539 mBackdrop.animate() 2540 // Make it disappear faster, as the focus should be on the activity 2541 // behind. 2542 .setDuration(mKeyguardFadingAwayDuration / 2) 2543 .setStartDelay(mKeyguardFadingAwayDelay) 2544 .setInterpolator(Interpolators.LINEAR) 2545 .start(); 2546 } 2547 } 2548 } 2549 } 2550 Trace.endSection(); 2551 } 2552 2553 private void updateReportRejectedTouchVisibility() { 2554 if (mReportRejectedTouch == null) { 2555 return; 2556 } 2557 mReportRejectedTouch.setVisibility(mState == StatusBarState.KEYGUARD 2558 && mFalsingManager.isReportingEnabled() ? View.VISIBLE : View.INVISIBLE); 2559 } 2560 2561 /** 2562 * State is one or more of the DISABLE constants from StatusBarManager. 2563 */ 2564 @Override 2565 public void disable(int state1, int state2, boolean animate) { 2566 animate &= mStatusBarWindowState != WINDOW_STATE_HIDDEN; 2567 mDisabledUnmodified1 = state1; 2568 mDisabledUnmodified2 = state2; 2569 final int old1 = mDisabled1; 2570 final int diff1 = state1 ^ old1; 2571 mDisabled1 = state1; 2572 2573 final int old2 = mDisabled2; 2574 final int diff2 = state2 ^ old2; 2575 mDisabled2 = state2; 2576 2577 if (DEBUG) { 2578 Log.d(TAG, String.format("disable1: 0x%08x -> 0x%08x (diff1: 0x%08x)", 2579 old1, state1, diff1)); 2580 Log.d(TAG, String.format("disable2: 0x%08x -> 0x%08x (diff2: 0x%08x)", 2581 old2, state2, diff2)); 2582 } 2583 2584 StringBuilder flagdbg = new StringBuilder(); 2585 flagdbg.append("disable<"); 2586 flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_EXPAND)) ? 'E' : 'e'); 2587 flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_EXPAND)) ? '!' : ' '); 2588 flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_NOTIFICATION_ICONS)) ? 'I' : 'i'); 2589 flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ICONS)) ? '!' : ' '); 2590 flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS)) ? 'A' : 'a'); 2591 flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS)) ? '!' : ' '); 2592 flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_SYSTEM_INFO)) ? 'S' : 's'); 2593 flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_SYSTEM_INFO)) ? '!' : ' '); 2594 flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_BACK)) ? 'B' : 'b'); 2595 flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_BACK)) ? '!' : ' '); 2596 flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_HOME)) ? 'H' : 'h'); 2597 flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_HOME)) ? '!' : ' '); 2598 flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_RECENT)) ? 'R' : 'r'); 2599 flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_RECENT)) ? '!' : ' '); 2600 flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_CLOCK)) ? 'C' : 'c'); 2601 flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_CLOCK)) ? '!' : ' '); 2602 flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_SEARCH)) ? 'S' : 's'); 2603 flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_SEARCH)) ? '!' : ' '); 2604 flagdbg.append(0 != ((state2 & StatusBarManager.DISABLE2_QUICK_SETTINGS)) ? 'Q' : 'q'); 2605 flagdbg.append(0 != ((diff2 & StatusBarManager.DISABLE2_QUICK_SETTINGS)) ? '!' : ' '); 2606 flagdbg.append('>'); 2607 Log.d(TAG, flagdbg.toString()); 2608 2609 if ((diff1 & StatusBarManager.DISABLE_EXPAND) != 0) { 2610 if ((state1 & StatusBarManager.DISABLE_EXPAND) != 0) { 2611 animateCollapsePanels(); 2612 } 2613 } 2614 2615 if ((diff1 & StatusBarManager.DISABLE_RECENT) != 0) { 2616 if ((state1 & StatusBarManager.DISABLE_RECENT) != 0) { 2617 // close recents if it's visible 2618 mHandler.removeMessages(MSG_HIDE_RECENT_APPS); 2619 mHandler.sendEmptyMessage(MSG_HIDE_RECENT_APPS); 2620 } 2621 } 2622 2623 if ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) { 2624 mDisableNotificationAlerts = 2625 (state1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0; 2626 mHeadsUpObserver.onChange(true); 2627 } 2628 2629 if ((diff2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) != 0) { 2630 updateQsExpansionEnabled(); 2631 } 2632 } 2633 2634 /** 2635 * Reapplies the disable flags as last requested by StatusBarManager. 2636 * 2637 * This needs to be called if state used by {@link #adjustDisableFlags} changes. 2638 */ 2639 public void recomputeDisableFlags(boolean animate) { 2640 mCommandQueue.recomputeDisableFlags(animate); 2641 } 2642 2643 protected H createHandler() { 2644 return new StatusBar.H(); 2645 } 2646 2647 @Override 2648 public void startActivity(Intent intent, boolean dismissShade) { 2649 startActivityDismissingKeyguard(intent, false, dismissShade); 2650 } 2651 2652 @Override 2653 public void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade) { 2654 startActivityDismissingKeyguard(intent, onlyProvisioned, dismissShade); 2655 } 2656 2657 @Override 2658 public void startActivity(Intent intent, boolean dismissShade, Callback callback) { 2659 startActivityDismissingKeyguard(intent, false, dismissShade, callback); 2660 } 2661 2662 public void setQsExpanded(boolean expanded) { 2663 mStatusBarWindowManager.setQsExpanded(expanded); 2664 mNotificationPanel.setStatusAccessibilityImportance(expanded 2665 ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS 2666 : View.IMPORTANT_FOR_ACCESSIBILITY_AUTO); 2667 } 2668 2669 public boolean isGoingToNotificationShade() { 2670 return mLeaveOpenOnKeyguardHide; 2671 } 2672 2673 public boolean isWakeUpComingFromTouch() { 2674 return mWakeUpComingFromTouch; 2675 } 2676 2677 public boolean isFalsingThresholdNeeded() { 2678 return getBarState() == StatusBarState.KEYGUARD; 2679 } 2680 2681 public boolean isDozing() { 2682 return mDozing; 2683 } 2684 2685 @Override // NotificationData.Environment 2686 public String getCurrentMediaNotificationKey() { 2687 return mMediaNotificationKey; 2688 } 2689 2690 public boolean isScrimSrcModeEnabled() { 2691 return mScrimSrcModeEnabled; 2692 } 2693 2694 /** 2695 * To be called when there's a state change in StatusBarKeyguardViewManager. 2696 */ 2697 public void onKeyguardViewManagerStatesUpdated() { 2698 logStateToEventlog(); 2699 } 2700 2701 @Override // UnlockMethodCache.OnUnlockMethodChangedListener 2702 public void onUnlockMethodStateChanged() { 2703 logStateToEventlog(); 2704 } 2705 2706 @Override 2707 public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) { 2708 if (inPinnedMode) { 2709 mStatusBarWindowManager.setHeadsUpShowing(true); 2710 mStatusBarWindowManager.setForceStatusBarVisible(true); 2711 if (mNotificationPanel.isFullyCollapsed()) { 2712 // We need to ensure that the touchable region is updated before the window will be 2713 // resized, in order to not catch any touches. A layout will ensure that 2714 // onComputeInternalInsets will be called and after that we can resize the layout. Let's 2715 // make sure that the window stays small for one frame until the touchableRegion is set. 2716 mNotificationPanel.requestLayout(); 2717 mStatusBarWindowManager.setForceWindowCollapsed(true); 2718 mNotificationPanel.post(new Runnable() { 2719 @Override 2720 public void run() { 2721 mStatusBarWindowManager.setForceWindowCollapsed(false); 2722 } 2723 }); 2724 } 2725 } else { 2726 if (!mNotificationPanel.isFullyCollapsed() || mNotificationPanel.isTracking()) { 2727 // We are currently tracking or is open and the shade doesn't need to be kept 2728 // open artificially. 2729 mStatusBarWindowManager.setHeadsUpShowing(false); 2730 } else { 2731 // we need to keep the panel open artificially, let's wait until the animation 2732 // is finished. 2733 mHeadsUpManager.setHeadsUpGoingAway(true); 2734 mStackScroller.runAfterAnimationFinished(new Runnable() { 2735 @Override 2736 public void run() { 2737 if (!mHeadsUpManager.hasPinnedHeadsUp()) { 2738 mStatusBarWindowManager.setHeadsUpShowing(false); 2739 mHeadsUpManager.setHeadsUpGoingAway(false); 2740 } 2741 removeRemoteInputEntriesKeptUntilCollapsed(); 2742 } 2743 }); 2744 } 2745 } 2746 } 2747 2748 @Override 2749 public void onHeadsUpPinned(ExpandableNotificationRow headsUp) { 2750 dismissVolumeDialog(); 2751 } 2752 2753 @Override 2754 public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) { 2755 } 2756 2757 @Override 2758 public void onHeadsUpStateChanged(Entry entry, boolean isHeadsUp) { 2759 if (!isHeadsUp && mHeadsUpEntriesToRemoveOnSwitch.contains(entry)) { 2760 removeNotification(entry.key, mLatestRankingMap); 2761 mHeadsUpEntriesToRemoveOnSwitch.remove(entry); 2762 if (mHeadsUpEntriesToRemoveOnSwitch.isEmpty()) { 2763 mLatestRankingMap = null; 2764 } 2765 } else { 2766 updateNotificationRanking(null); 2767 if (isHeadsUp) { 2768 mDozeServiceHost.fireNotificationHeadsUp(); 2769 } 2770 } 2771 2772 } 2773 2774 protected void updateHeadsUp(String key, Entry entry, boolean shouldPeek, 2775 boolean alertAgain) { 2776 final boolean wasHeadsUp = isHeadsUp(key); 2777 if (wasHeadsUp) { 2778 if (!shouldPeek) { 2779 // We don't want this to be interrupting anymore, lets remove it 2780 mHeadsUpManager.removeNotification(key, false /* ignoreEarliestRemovalTime */); 2781 } else { 2782 mHeadsUpManager.updateNotification(entry, alertAgain); 2783 } 2784 } else if (shouldPeek && alertAgain) { 2785 // This notification was updated to be a heads-up, show it! 2786 mHeadsUpManager.showNotification(entry); 2787 } 2788 } 2789 2790 protected void setHeadsUpUser(int newUserId) { 2791 if (mHeadsUpManager != null) { 2792 mHeadsUpManager.setUser(newUserId); 2793 } 2794 } 2795 2796 public boolean isHeadsUp(String key) { 2797 return mHeadsUpManager.isHeadsUp(key); 2798 } 2799 2800 protected boolean isSnoozedPackage(StatusBarNotification sbn) { 2801 return mHeadsUpManager.isSnoozed(sbn.getPackageName()); 2802 } 2803 2804 public boolean isKeyguardCurrentlySecure() { 2805 return !mUnlockMethodCache.canSkipBouncer(); 2806 } 2807 2808 public void setPanelExpanded(boolean isExpanded) { 2809 mPanelExpanded = isExpanded; 2810 mStatusBarWindowManager.setPanelExpanded(isExpanded); 2811 mVisualStabilityManager.setPanelExpanded(isExpanded); 2812 if (isExpanded && getBarState() != StatusBarState.KEYGUARD) { 2813 if (DEBUG) { 2814 Log.v(TAG, "clearing notification effects from setPanelExpanded"); 2815 } 2816 clearNotificationEffects(); 2817 } 2818 2819 if (!isExpanded) { 2820 removeRemoteInputEntriesKeptUntilCollapsed(); 2821 } 2822 } 2823 2824 private void removeRemoteInputEntriesKeptUntilCollapsed() { 2825 for (int i = 0; i < mRemoteInputEntriesToRemoveOnCollapse.size(); i++) { 2826 Entry entry = mRemoteInputEntriesToRemoveOnCollapse.valueAt(i); 2827 mRemoteInputController.removeRemoteInput(entry, null); 2828 removeNotification(entry.key, mLatestRankingMap); 2829 } 2830 mRemoteInputEntriesToRemoveOnCollapse.clear(); 2831 } 2832 2833 public void onScreenTurnedOff() { 2834 mScreenFullyOff = true; 2835 mFalsingManager.onScreenOff(); 2836 updateIsKeyguard(); 2837 } 2838 2839 public NotificationStackScrollLayout getNotificationScrollLayout() { 2840 return mStackScroller; 2841 } 2842 2843 public boolean isPulsing() { 2844 return mDozeScrimController.isPulsing(); 2845 } 2846 2847 @Override 2848 public void onReorderingAllowed() { 2849 updateNotifications(); 2850 } 2851 2852 public boolean isLaunchTransitionFadingAway() { 2853 return mLaunchTransitionFadingAway; 2854 } 2855 2856 public boolean hideStatusBarIconsWhenExpanded() { 2857 return mNotificationPanel.hideStatusBarIconsWhenExpanded(); 2858 } 2859 2860 @Override 2861 public void onColorsChanged(ColorExtractor extractor, int which) { 2862 updateTheme(); 2863 } 2864 2865 public boolean isUsingDarkText() { 2866 OverlayInfo themeInfo = null; 2867 try { 2868 themeInfo = mOverlayManager.getOverlayInfo("com.android.systemui.theme.lightwallpaper", 2869 mCurrentUserId); 2870 } catch (RemoteException e) { 2871 e.printStackTrace(); 2872 } 2873 return themeInfo != null && themeInfo.isEnabled(); 2874 } 2875 2876 public boolean isUsingDarkTheme() { 2877 OverlayInfo themeInfo = null; 2878 try { 2879 themeInfo = mOverlayManager.getOverlayInfo("com.android.systemui.theme.dark", 2880 mCurrentUserId); 2881 } catch (RemoteException e) { 2882 e.printStackTrace(); 2883 } 2884 return themeInfo != null && themeInfo.isEnabled(); 2885 } 2886 2887 @Nullable 2888 public View getAmbientIndicationContainer() { 2889 return mAmbientIndicationContainer; 2890 } 2891 2892 /** 2893 * All changes to the status bar and notifications funnel through here and are batched. 2894 */ 2895 protected class H extends Handler { 2896 @Override 2897 public void handleMessage(Message m) { 2898 switch (m.what) { 2899 case MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU: 2900 toggleKeyboardShortcuts(m.arg1); 2901 break; 2902 case MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU: 2903 dismissKeyboardShortcuts(); 2904 break; 2905 // End old BaseStatusBar.H handling. 2906 case MSG_OPEN_NOTIFICATION_PANEL: 2907 animateExpandNotificationsPanel(); 2908 break; 2909 case MSG_OPEN_SETTINGS_PANEL: 2910 animateExpandSettingsPanel((String) m.obj); 2911 break; 2912 case MSG_CLOSE_PANELS: 2913 animateCollapsePanels(); 2914 break; 2915 case MSG_LAUNCH_TRANSITION_TIMEOUT: 2916 onLaunchTransitionTimeout(); 2917 break; 2918 } 2919 } 2920 } 2921 2922 public void maybeEscalateHeadsUp() { 2923 Collection<HeadsUpManager.HeadsUpEntry> entries = mHeadsUpManager.getAllEntries(); 2924 for (HeadsUpManager.HeadsUpEntry entry : entries) { 2925 final StatusBarNotification sbn = entry.entry.notification; 2926 final Notification notification = sbn.getNotification(); 2927 if (notification.fullScreenIntent != null) { 2928 if (DEBUG) { 2929 Log.d(TAG, "converting a heads up to fullScreen"); 2930 } 2931 try { 2932 EventLog.writeEvent(EventLogTags.SYSUI_HEADS_UP_ESCALATION, 2933 sbn.getKey()); 2934 notification.fullScreenIntent.send(); 2935 entry.entry.notifyFullScreenIntentLaunched(); 2936 } catch (PendingIntent.CanceledException e) { 2937 } 2938 } 2939 } 2940 mHeadsUpManager.releaseAllImmediately(); 2941 } 2942 2943 /** 2944 * Called for system navigation gestures. First action opens the panel, second opens 2945 * settings. Down action closes the entire panel. 2946 */ 2947 @Override 2948 public void handleSystemNavigationKey(int key) { 2949 if (SPEW) Log.d(TAG, "handleSystemNavigationKey: " + key); 2950 if (!panelsEnabled() || !mKeyguardMonitor.isDeviceInteractive() 2951 || mKeyguardMonitor.isShowing() && !mKeyguardMonitor.isOccluded()) { 2952 return; 2953 } 2954 2955 // Panels are not available in setup 2956 if (!mUserSetup) return; 2957 2958 if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP == key) { 2959 mMetricsLogger.action(MetricsEvent.ACTION_SYSTEM_NAVIGATION_KEY_UP); 2960 mNotificationPanel.collapse(false /* delayed */, 1.0f /* speedUpFactor */); 2961 } else if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN == key) { 2962 mMetricsLogger.action(MetricsEvent.ACTION_SYSTEM_NAVIGATION_KEY_DOWN); 2963 if (mNotificationPanel.isFullyCollapsed()) { 2964 mNotificationPanel.expand(true /* animate */); 2965 mMetricsLogger.count(NotificationPanelView.COUNTER_PANEL_OPEN, 1); 2966 } else if (!mNotificationPanel.isInSettings() && !mNotificationPanel.isExpanding()){ 2967 mNotificationPanel.flingSettings(0 /* velocity */, true /* expand */); 2968 mMetricsLogger.count(NotificationPanelView.COUNTER_PANEL_OPEN_QS, 1); 2969 } 2970 } 2971 2972 } 2973 2974 boolean panelsEnabled() { 2975 return (mDisabled1 & StatusBarManager.DISABLE_EXPAND) == 0 && !ONLY_CORE_APPS; 2976 } 2977 2978 void makeExpandedVisible(boolean force) { 2979 if (SPEW) Log.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible); 2980 if (!force && (mExpandedVisible || !panelsEnabled())) { 2981 return; 2982 } 2983 2984 mExpandedVisible = true; 2985 2986 // Expand the window to encompass the full screen in anticipation of the drag. 2987 // This is only possible to do atomically because the status bar is at the top of the screen! 2988 mStatusBarWindowManager.setPanelVisible(true); 2989 2990 visibilityChanged(true); 2991 mWaitingForKeyguardExit = false; 2992 recomputeDisableFlags(!force /* animate */); 2993 setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true); 2994 } 2995 2996 public void animateCollapsePanels() { 2997 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); 2998 } 2999 3000 private final Runnable mAnimateCollapsePanels = new Runnable() { 3001 @Override 3002 public void run() { 3003 animateCollapsePanels(); 3004 } 3005 }; 3006 3007 public void postAnimateCollapsePanels() { 3008 mHandler.post(mAnimateCollapsePanels); 3009 } 3010 3011 public void postAnimateForceCollapsePanels() { 3012 mHandler.post(new Runnable() { 3013 @Override 3014 public void run() { 3015 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */); 3016 } 3017 }); 3018 } 3019 3020 public void postAnimateOpenPanels() { 3021 mHandler.sendEmptyMessage(MSG_OPEN_SETTINGS_PANEL); 3022 } 3023 3024 @Override 3025 public void animateCollapsePanels(int flags) { 3026 animateCollapsePanels(flags, false /* force */, false /* delayed */, 3027 1.0f /* speedUpFactor */); 3028 } 3029 3030 public void animateCollapsePanels(int flags, boolean force) { 3031 animateCollapsePanels(flags, force, false /* delayed */, 1.0f /* speedUpFactor */); 3032 } 3033 3034 public void animateCollapsePanels(int flags, boolean force, boolean delayed) { 3035 animateCollapsePanels(flags, force, delayed, 1.0f /* speedUpFactor */); 3036 } 3037 3038 public void animateCollapsePanels(int flags, boolean force, boolean delayed, 3039 float speedUpFactor) { 3040 if (!force && mState != StatusBarState.SHADE) { 3041 runPostCollapseRunnables(); 3042 return; 3043 } 3044 if (SPEW) { 3045 Log.d(TAG, "animateCollapse():" 3046 + " mExpandedVisible=" + mExpandedVisible 3047 + " flags=" + flags); 3048 } 3049 3050 if ((flags & CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL) == 0) { 3051 if (!mHandler.hasMessages(MSG_HIDE_RECENT_APPS)) { 3052 mHandler.removeMessages(MSG_HIDE_RECENT_APPS); 3053 mHandler.sendEmptyMessage(MSG_HIDE_RECENT_APPS); 3054 } 3055 } 3056 3057 if (mStatusBarWindow != null && mNotificationPanel.canPanelBeCollapsed()) { 3058 // release focus immediately to kick off focus change transition 3059 mStatusBarWindowManager.setStatusBarFocusable(false); 3060 3061 mStatusBarWindow.cancelExpandHelper(); 3062 mStatusBarView.collapsePanel(true /* animate */, delayed, speedUpFactor); 3063 } 3064 } 3065 3066 private void runPostCollapseRunnables() { 3067 ArrayList<Runnable> clonedList = new ArrayList<>(mPostCollapseRunnables); 3068 mPostCollapseRunnables.clear(); 3069 int size = clonedList.size(); 3070 for (int i = 0; i < size; i++) { 3071 clonedList.get(i).run(); 3072 } 3073 mStatusBarKeyguardViewManager.readyForKeyguardDone(); 3074 } 3075 3076 @Override 3077 public void animateExpandNotificationsPanel() { 3078 if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible); 3079 if (!panelsEnabled()) { 3080 return ; 3081 } 3082 3083 mNotificationPanel.expand(true /* animate */); 3084 3085 if (false) postStartTracing(); 3086 } 3087 3088 @Override 3089 public void animateExpandSettingsPanel(String subPanel) { 3090 if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible); 3091 if (!panelsEnabled()) { 3092 return; 3093 } 3094 3095 // Settings are not available in setup 3096 if (!mUserSetup) return; 3097 3098 3099 if (subPanel != null) { 3100 mQSPanel.openDetails(subPanel); 3101 } 3102 mNotificationPanel.expandWithQs(); 3103 3104 if (false) postStartTracing(); 3105 } 3106 3107 public void animateCollapseQuickSettings() { 3108 if (mState == StatusBarState.SHADE) { 3109 mStatusBarView.collapsePanel(true, false /* delayed */, 1.0f /* speedUpFactor */); 3110 } 3111 } 3112 3113 void makeExpandedInvisible() { 3114 if (SPEW) Log.d(TAG, "makeExpandedInvisible: mExpandedVisible=" + mExpandedVisible 3115 + " mExpandedVisible=" + mExpandedVisible); 3116 3117 if (!mExpandedVisible || mStatusBarWindow == null) { 3118 return; 3119 } 3120 3121 // Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868) 3122 mStatusBarView.collapsePanel(/*animate=*/ false, false /* delayed*/, 3123 1.0f /* speedUpFactor */); 3124 3125 mNotificationPanel.closeQs(); 3126 3127 mExpandedVisible = false; 3128 visibilityChanged(false); 3129 3130 // Shrink the window to the size of the status bar only 3131 mStatusBarWindowManager.setPanelVisible(false); 3132 mStatusBarWindowManager.setForceStatusBarVisible(false); 3133 3134 // Close any guts that might be visible 3135 closeAndSaveGuts(true /* removeLeavebehind */, true /* force */, true /* removeControls */, 3136 -1 /* x */, -1 /* y */, true /* resetMenu */); 3137 3138 runPostCollapseRunnables(); 3139 setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false); 3140 showBouncerIfKeyguard(); 3141 recomputeDisableFlags(mNotificationPanel.hideStatusBarIconsWhenExpanded() /* animate */); 3142 3143 // Trimming will happen later if Keyguard is showing - doing it here might cause a jank in 3144 // the bouncer appear animation. 3145 if (!mStatusBarKeyguardViewManager.isShowing()) { 3146 WindowManagerGlobal.getInstance().trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN); 3147 } 3148 } 3149 3150 public boolean interceptTouchEvent(MotionEvent event) { 3151 if (DEBUG_GESTURES) { 3152 if (event.getActionMasked() != MotionEvent.ACTION_MOVE) { 3153 EventLog.writeEvent(EventLogTags.SYSUI_STATUSBAR_TOUCH, 3154 event.getActionMasked(), (int) event.getX(), (int) event.getY(), 3155 mDisabled1, mDisabled2); 3156 } 3157 3158 } 3159 3160 if (SPEW) { 3161 Log.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled1=" 3162 + mDisabled1 + " mDisabled2=" + mDisabled2 + " mTracking=" + mTracking); 3163 } else if (CHATTY) { 3164 if (event.getAction() != MotionEvent.ACTION_MOVE) { 3165 Log.d(TAG, String.format( 3166 "panel: %s at (%f, %f) mDisabled1=0x%08x mDisabled2=0x%08x", 3167 MotionEvent.actionToString(event.getAction()), 3168 event.getRawX(), event.getRawY(), mDisabled1, mDisabled2)); 3169 } 3170 } 3171 3172 if (DEBUG_GESTURES) { 3173 mGestureRec.add(event); 3174 } 3175 3176 if (mStatusBarWindowState == WINDOW_STATE_SHOWING) { 3177 final boolean upOrCancel = 3178 event.getAction() == MotionEvent.ACTION_UP || 3179 event.getAction() == MotionEvent.ACTION_CANCEL; 3180 if (upOrCancel && !mExpandedVisible) { 3181 setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false); 3182 } else { 3183 setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true); 3184 } 3185 } 3186 return false; 3187 } 3188 3189 public GestureRecorder getGestureRecorder() { 3190 return mGestureRec; 3191 } 3192 3193 public FingerprintUnlockController getFingerprintUnlockController() { 3194 return mFingerprintUnlockController; 3195 } 3196 3197 @Override // CommandQueue 3198 public void setWindowState(int window, int state) { 3199 boolean showing = state == WINDOW_STATE_SHOWING; 3200 if (mStatusBarWindow != null 3201 && window == StatusBarManager.WINDOW_STATUS_BAR 3202 && mStatusBarWindowState != state) { 3203 mStatusBarWindowState = state; 3204 if (DEBUG_WINDOW_STATE) Log.d(TAG, "Status bar " + windowStateToString(state)); 3205 if (!showing && mState == StatusBarState.SHADE) { 3206 mStatusBarView.collapsePanel(false /* animate */, false /* delayed */, 3207 1.0f /* speedUpFactor */); 3208 } 3209 } 3210 } 3211 3212 @Override // CommandQueue 3213 public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, 3214 int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) { 3215 final int oldVal = mSystemUiVisibility; 3216 final int newVal = (oldVal&~mask) | (vis&mask); 3217 final int diff = newVal ^ oldVal; 3218 if (DEBUG) Log.d(TAG, String.format( 3219 "setSystemUiVisibility vis=%s mask=%s oldVal=%s newVal=%s diff=%s", 3220 Integer.toHexString(vis), Integer.toHexString(mask), 3221 Integer.toHexString(oldVal), Integer.toHexString(newVal), 3222 Integer.toHexString(diff))); 3223 boolean sbModeChanged = false; 3224 if (diff != 0) { 3225 mSystemUiVisibility = newVal; 3226 3227 // update low profile 3228 if ((diff & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) { 3229 setAreThereNotifications(); 3230 } 3231 3232 // ready to unhide 3233 if ((vis & View.STATUS_BAR_UNHIDE) != 0) { 3234 mSystemUiVisibility &= ~View.STATUS_BAR_UNHIDE; 3235 mNoAnimationOnNextBarModeChange = true; 3236 } 3237 3238 // update status bar mode 3239 final int sbMode = computeStatusBarMode(oldVal, newVal); 3240 3241 sbModeChanged = sbMode != -1; 3242 if (sbModeChanged && sbMode != mStatusBarMode) { 3243 if (sbMode != mStatusBarMode) { 3244 mStatusBarMode = sbMode; 3245 checkBarModes(); 3246 } 3247 touchAutoHide(); 3248 } 3249 3250 if ((vis & View.NAVIGATION_BAR_UNHIDE) != 0) { 3251 mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE; 3252 } 3253 3254 // send updated sysui visibility to window manager 3255 notifyUiVisibilityChanged(mSystemUiVisibility); 3256 } 3257 3258 mLightBarController.onSystemUiVisibilityChanged(fullscreenStackVis, dockedStackVis, 3259 mask, fullscreenStackBounds, dockedStackBounds, sbModeChanged, mStatusBarMode); 3260 } 3261 3262 void touchAutoHide() { 3263 // update transient bar autohide 3264 if (mStatusBarMode == MODE_SEMI_TRANSPARENT || (mNavigationBar != null 3265 && mNavigationBar.isSemiTransparent())) { 3266 scheduleAutohide(); 3267 } else { 3268 cancelAutohide(); 3269 } 3270 } 3271 3272 protected int computeStatusBarMode(int oldVal, int newVal) { 3273 return computeBarMode(oldVal, newVal, View.STATUS_BAR_TRANSIENT, 3274 View.STATUS_BAR_TRANSLUCENT, View.STATUS_BAR_TRANSPARENT); 3275 } 3276 3277 protected BarTransitions getStatusBarTransitions() { 3278 return mStatusBarView.getBarTransitions(); 3279 } 3280 3281 protected int computeBarMode(int oldVis, int newVis, 3282 int transientFlag, int translucentFlag, int transparentFlag) { 3283 final int oldMode = barMode(oldVis, transientFlag, translucentFlag, transparentFlag); 3284 final int newMode = barMode(newVis, transientFlag, translucentFlag, transparentFlag); 3285 if (oldMode == newMode) { 3286 return -1; // no mode change 3287 } 3288 return newMode; 3289 } 3290 3291 private int barMode(int vis, int transientFlag, int translucentFlag, int transparentFlag) { 3292 int lightsOutTransparent = View.SYSTEM_UI_FLAG_LOW_PROFILE | transparentFlag; 3293 return (vis & transientFlag) != 0 ? MODE_SEMI_TRANSPARENT 3294 : (vis & translucentFlag) != 0 ? MODE_TRANSLUCENT 3295 : (vis & lightsOutTransparent) == lightsOutTransparent ? MODE_LIGHTS_OUT_TRANSPARENT 3296 : (vis & transparentFlag) != 0 ? MODE_TRANSPARENT 3297 : (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0 ? MODE_LIGHTS_OUT 3298 : MODE_OPAQUE; 3299 } 3300 3301 void checkBarModes() { 3302 if (mDemoMode) return; 3303 if (mStatusBarView != null) checkBarMode(mStatusBarMode, mStatusBarWindowState, 3304 getStatusBarTransitions()); 3305 if (mNavigationBar != null) mNavigationBar.checkNavBarModes(); 3306 mNoAnimationOnNextBarModeChange = false; 3307 } 3308 3309 // Called by NavigationBarFragment 3310 void setQsScrimEnabled(boolean scrimEnabled) { 3311 mNotificationPanel.setQsScrimEnabled(scrimEnabled); 3312 } 3313 3314 void checkBarMode(int mode, int windowState, BarTransitions transitions) { 3315 final boolean powerSave = mBatteryController.isPowerSave(); 3316 final boolean anim = !mNoAnimationOnNextBarModeChange && mDeviceInteractive 3317 && windowState != WINDOW_STATE_HIDDEN && !powerSave; 3318 if (powerSave && getBarState() == StatusBarState.SHADE) { 3319 mode = MODE_WARNING; 3320 } 3321 transitions.transitionTo(mode, anim); 3322 } 3323 3324 private void finishBarAnimations() { 3325 if (mStatusBarView != null) { 3326 mStatusBarView.getBarTransitions().finishAnimations(); 3327 } 3328 if (mNavigationBar != null) { 3329 mNavigationBar.finishBarAnimations(); 3330 } 3331 } 3332 3333 private final Runnable mCheckBarModes = new Runnable() { 3334 @Override 3335 public void run() { 3336 checkBarModes(); 3337 } 3338 }; 3339 3340 public void setInteracting(int barWindow, boolean interacting) { 3341 final boolean changing = ((mInteractingWindows & barWindow) != 0) != interacting; 3342 mInteractingWindows = interacting 3343 ? (mInteractingWindows | barWindow) 3344 : (mInteractingWindows & ~barWindow); 3345 if (mInteractingWindows != 0) { 3346 suspendAutohide(); 3347 } else { 3348 resumeSuspendedAutohide(); 3349 } 3350 // manually dismiss the volume panel when interacting with the nav bar 3351 if (changing && interacting && barWindow == StatusBarManager.WINDOW_NAVIGATION_BAR) { 3352 dismissVolumeDialog(); 3353 } 3354 checkBarModes(); 3355 } 3356 3357 private void dismissVolumeDialog() { 3358 if (mVolumeComponent != null) { 3359 mVolumeComponent.dismissNow(); 3360 } 3361 } 3362 3363 private void resumeSuspendedAutohide() { 3364 if (mAutohideSuspended) { 3365 scheduleAutohide(); 3366 mHandler.postDelayed(mCheckBarModes, 500); // longer than home -> launcher 3367 } 3368 } 3369 3370 private void suspendAutohide() { 3371 mHandler.removeCallbacks(mAutohide); 3372 mHandler.removeCallbacks(mCheckBarModes); 3373 mAutohideSuspended = (mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0; 3374 } 3375 3376 private void cancelAutohide() { 3377 mAutohideSuspended = false; 3378 mHandler.removeCallbacks(mAutohide); 3379 } 3380 3381 private void scheduleAutohide() { 3382 cancelAutohide(); 3383 mHandler.postDelayed(mAutohide, AUTOHIDE_TIMEOUT_MS); 3384 } 3385 3386 void checkUserAutohide(View v, MotionEvent event) { 3387 if ((mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0 // a transient bar is revealed 3388 && event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar 3389 && event.getX() == 0 && event.getY() == 0 // a touch outside both bars 3390 && !mRemoteInputController.isRemoteInputActive()) { // not due to typing in IME 3391 userAutohide(); 3392 } 3393 } 3394 3395 private void checkRemoteInputOutside(MotionEvent event) { 3396 if (event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar 3397 && event.getX() == 0 && event.getY() == 0 // a touch outside both bars 3398 && mRemoteInputController.isRemoteInputActive()) { 3399 mRemoteInputController.closeRemoteInputs(); 3400 } 3401 } 3402 3403 private void userAutohide() { 3404 cancelAutohide(); 3405 mHandler.postDelayed(mAutohide, 350); // longer than app gesture -> flag clear 3406 } 3407 3408 private boolean areLightsOn() { 3409 return 0 == (mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE); 3410 } 3411 3412 public void setLightsOn(boolean on) { 3413 Log.v(TAG, "setLightsOn(" + on + ")"); 3414 if (on) { 3415 setSystemUiVisibility(0, 0, 0, View.SYSTEM_UI_FLAG_LOW_PROFILE, 3416 mLastFullscreenStackBounds, mLastDockedStackBounds); 3417 } else { 3418 setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE, 0, 0, 3419 View.SYSTEM_UI_FLAG_LOW_PROFILE, mLastFullscreenStackBounds, 3420 mLastDockedStackBounds); 3421 } 3422 } 3423 3424 private void notifyUiVisibilityChanged(int vis) { 3425 try { 3426 if (mLastDispatchedSystemUiVisibility != vis) { 3427 mWindowManagerService.statusBarVisibilityChanged(vis); 3428 mLastDispatchedSystemUiVisibility = vis; 3429 } 3430 } catch (RemoteException ex) { 3431 } 3432 } 3433 3434 @Override 3435 public void topAppWindowChanged(boolean showMenu) { 3436 if (SPEW) { 3437 Log.d(TAG, (showMenu?"showing":"hiding") + " the MENU button"); 3438 } 3439 3440 // See above re: lights-out policy for legacy apps. 3441 if (showMenu) setLightsOn(true); 3442 } 3443 3444 public static String viewInfo(View v) { 3445 return "[(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom() 3446 + ") " + v.getWidth() + "x" + v.getHeight() + "]"; 3447 } 3448 3449 @Override 3450 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 3451 synchronized (mQueueLock) { 3452 pw.println("Current Status Bar state:"); 3453 pw.println(" mExpandedVisible=" + mExpandedVisible 3454 + ", mTrackingPosition=" + mTrackingPosition); 3455 pw.println(" mTracking=" + mTracking); 3456 pw.println(" mDisplayMetrics=" + mDisplayMetrics); 3457 pw.println(" mStackScroller: " + viewInfo(mStackScroller)); 3458 pw.println(" mStackScroller: " + viewInfo(mStackScroller) 3459 + " scroll " + mStackScroller.getScrollX() 3460 + "," + mStackScroller.getScrollY()); 3461 } 3462 pw.print(" mPendingNotifications="); 3463 if (mPendingNotifications.size() == 0) { 3464 pw.println("null"); 3465 } else { 3466 for (Entry entry : mPendingNotifications.values()) { 3467 pw.println(entry.notification); 3468 } 3469 } 3470 3471 pw.print(" mInteractingWindows="); pw.println(mInteractingWindows); 3472 pw.print(" mStatusBarWindowState="); 3473 pw.println(windowStateToString(mStatusBarWindowState)); 3474 pw.print(" mStatusBarMode="); 3475 pw.println(BarTransitions.modeToString(mStatusBarMode)); 3476 pw.print(" mDozing="); pw.println(mDozing); 3477 pw.print(" mZenMode="); 3478 pw.println(Settings.Global.zenModeToString(mZenMode)); 3479 pw.print(" mUseHeadsUp="); 3480 pw.println(mUseHeadsUp); 3481 pw.print(" mKeyToRemoveOnGutsClosed="); 3482 pw.println(mKeyToRemoveOnGutsClosed); 3483 if (mStatusBarView != null) { 3484 dumpBarTransitions(pw, "mStatusBarView", mStatusBarView.getBarTransitions()); 3485 } 3486 3487 pw.print(" mMediaSessionManager="); 3488 pw.println(mMediaSessionManager); 3489 pw.print(" mMediaNotificationKey="); 3490 pw.println(mMediaNotificationKey); 3491 pw.print(" mMediaController="); 3492 pw.print(mMediaController); 3493 if (mMediaController != null) { 3494 pw.print(" state=" + mMediaController.getPlaybackState()); 3495 } 3496 pw.println(); 3497 pw.print(" mMediaMetadata="); 3498 pw.print(mMediaMetadata); 3499 if (mMediaMetadata != null) { 3500 pw.print(" title=" + mMediaMetadata.getText(MediaMetadata.METADATA_KEY_TITLE)); 3501 } 3502 pw.println(); 3503 3504 pw.println(" Panels: "); 3505 if (mNotificationPanel != null) { 3506 pw.println(" mNotificationPanel=" + 3507 mNotificationPanel + " params=" + mNotificationPanel.getLayoutParams().debug("")); 3508 pw.print (" "); 3509 mNotificationPanel.dump(fd, pw, args); 3510 } 3511 pw.println(" mStackScroller: "); 3512 if (mStackScroller != null) { 3513 pw.print (" "); 3514 mStackScroller.dump(fd, pw, args); 3515 } 3516 3517 DozeLog.dump(pw); 3518 3519 if (DUMPTRUCK) { 3520 synchronized (mNotificationData) { 3521 mNotificationData.dump(pw, " "); 3522 } 3523 3524 if (false) { 3525 pw.println("see the logcat for a dump of the views we have created."); 3526 // must happen on ui thread 3527 mHandler.post(new Runnable() { 3528 @Override 3529 public void run() { 3530 mStatusBarView.getLocationOnScreen(mAbsPos); 3531 Log.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1] 3532 + ") " + mStatusBarView.getWidth() + "x" 3533 + getStatusBarHeight()); 3534 mStatusBarView.debug(); 3535 } 3536 }); 3537 } 3538 } 3539 3540 if (DEBUG_GESTURES) { 3541 pw.print(" status bar gestures: "); 3542 mGestureRec.dump(fd, pw, args); 3543 } 3544 3545 if (mHeadsUpManager != null) { 3546 mHeadsUpManager.dump(fd, pw, args); 3547 } else { 3548 pw.println(" mHeadsUpManager: null"); 3549 } 3550 if (mGroupManager != null) { 3551 mGroupManager.dump(fd, pw, args); 3552 } else { 3553 pw.println(" mGroupManager: null"); 3554 } 3555 3556 if (mLightBarController != null) { 3557 mLightBarController.dump(fd, pw, args); 3558 } 3559 3560 if (KeyguardUpdateMonitor.getInstance(mContext) != null) { 3561 KeyguardUpdateMonitor.getInstance(mContext).dump(fd, pw, args); 3562 } 3563 3564 FalsingManager.getInstance(mContext).dump(pw); 3565 FalsingLog.dump(pw); 3566 3567 pw.println("SharedPreferences:"); 3568 for (Map.Entry<String, ?> entry : Prefs.getAll(mContext).entrySet()) { 3569 pw.print(" "); pw.print(entry.getKey()); pw.print("="); pw.println(entry.getValue()); 3570 } 3571 } 3572 3573 static void dumpBarTransitions(PrintWriter pw, String var, BarTransitions transitions) { 3574 pw.print(" "); pw.print(var); pw.print(".BarTransitions.mMode="); 3575 pw.println(BarTransitions.modeToString(transitions.getMode())); 3576 } 3577 3578 public void createAndAddWindows() { 3579 addStatusBarWindow(); 3580 } 3581 3582 private void addStatusBarWindow() { 3583 makeStatusBarView(); 3584 mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class); 3585 mRemoteInputController = new RemoteInputController(mHeadsUpManager); 3586 mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight()); 3587 } 3588 3589 // called by makeStatusbar and also by PhoneStatusBarView 3590 void updateDisplaySize() { 3591 mDisplay.getMetrics(mDisplayMetrics); 3592 mDisplay.getSize(mCurrentDisplaySize); 3593 if (DEBUG_GESTURES) { 3594 mGestureRec.tag("display", 3595 String.format("%dx%d", mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels)); 3596 } 3597 } 3598 3599 float getDisplayDensity() { 3600 return mDisplayMetrics.density; 3601 } 3602 3603 public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned, 3604 boolean dismissShade) { 3605 startActivityDismissingKeyguard(intent, onlyProvisioned, dismissShade, null /* callback */); 3606 } 3607 3608 public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned, 3609 final boolean dismissShade, final Callback callback) { 3610 if (onlyProvisioned && !isDeviceProvisioned()) return; 3611 3612 final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity( 3613 mContext, intent, mCurrentUserId); 3614 Runnable runnable = new Runnable() { 3615 @Override 3616 public void run() { 3617 mAssistManager.hideAssist(); 3618 intent.setFlags( 3619 Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); 3620 int result = ActivityManager.START_CANCELED; 3621 ActivityOptions options = new ActivityOptions(getActivityOptions()); 3622 if (intent == KeyguardBottomAreaView.INSECURE_CAMERA_INTENT) { 3623 // Normally an activity will set it's requested rotation 3624 // animation on its window. However when launching an activity 3625 // causes the orientation to change this is too late. In these cases 3626 // the default animation is used. This doesn't look good for 3627 // the camera (as it rotates the camera contents out of sync 3628 // with physical reality). So, we ask the WindowManager to 3629 // force the crossfade animation if an orientation change 3630 // happens to occur during the launch. 3631 options.setRotationAnimationHint( 3632 WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS); 3633 } 3634 try { 3635 result = ActivityManager.getService().startActivityAsUser( 3636 null, mContext.getBasePackageName(), 3637 intent, 3638 intent.resolveTypeIfNeeded(mContext.getContentResolver()), 3639 null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, 3640 options.toBundle(), UserHandle.CURRENT.getIdentifier()); 3641 } catch (RemoteException e) { 3642 Log.w(TAG, "Unable to start activity", e); 3643 } 3644 if (callback != null) { 3645 callback.onActivityStarted(result); 3646 } 3647 } 3648 }; 3649 Runnable cancelRunnable = new Runnable() { 3650 @Override 3651 public void run() { 3652 if (callback != null) { 3653 callback.onActivityStarted(ActivityManager.START_CANCELED); 3654 } 3655 } 3656 }; 3657 executeRunnableDismissingKeyguard(runnable, cancelRunnable, dismissShade, 3658 afterKeyguardGone, true /* deferred */); 3659 } 3660 3661 public void readyForKeyguardDone() { 3662 mStatusBarKeyguardViewManager.readyForKeyguardDone(); 3663 } 3664 3665 public void executeRunnableDismissingKeyguard(final Runnable runnable, 3666 final Runnable cancelAction, 3667 final boolean dismissShade, 3668 final boolean afterKeyguardGone, 3669 final boolean deferred) { 3670 dismissKeyguardThenExecute(() -> { 3671 if (runnable != null) { 3672 if (mStatusBarKeyguardViewManager.isShowing() 3673 && mStatusBarKeyguardViewManager.isOccluded()) { 3674 mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable); 3675 } else { 3676 AsyncTask.execute(runnable); 3677 } 3678 } 3679 if (dismissShade) { 3680 if (mExpandedVisible) { 3681 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */, 3682 true /* delayed*/); 3683 } else { 3684 3685 // Do it after DismissAction has been processed to conserve the needed ordering. 3686 mHandler.post(this::runPostCollapseRunnables); 3687 } 3688 } else if (isInLaunchTransition() && mNotificationPanel.isLaunchTransitionFinished()) { 3689 3690 // We are not dismissing the shade, but the launch transition is already finished, 3691 // so nobody will call readyForKeyguardDone anymore. Post it such that 3692 // keyguardDonePending gets called first. 3693 mHandler.post(mStatusBarKeyguardViewManager::readyForKeyguardDone); 3694 } 3695 return deferred; 3696 }, cancelAction, afterKeyguardGone); 3697 } 3698 3699 private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 3700 @Override 3701 public void onReceive(Context context, Intent intent) { 3702 if (DEBUG) Log.v(TAG, "onReceive: " + intent); 3703 String action = intent.getAction(); 3704 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) { 3705 KeyboardShortcuts.dismiss(); 3706 if (mRemoteInputController != null) { 3707 mRemoteInputController.closeRemoteInputs(); 3708 } 3709 if (isCurrentProfile(getSendingUserId())) { 3710 int flags = CommandQueue.FLAG_EXCLUDE_NONE; 3711 String reason = intent.getStringExtra("reason"); 3712 if (reason != null && reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) { 3713 flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL; 3714 } 3715 animateCollapsePanels(flags); 3716 } 3717 } 3718 else if (Intent.ACTION_SCREEN_OFF.equals(action)) { 3719 notifyHeadsUpScreenOff(); 3720 finishBarAnimations(); 3721 resetUserExpandedStates(); 3722 } 3723 else if (DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG.equals(action)) { 3724 mQSPanel.showDeviceMonitoringDialog(); 3725 } 3726 } 3727 }; 3728 3729 private BroadcastReceiver mDemoReceiver = new BroadcastReceiver() { 3730 @Override 3731 public void onReceive(Context context, Intent intent) { 3732 if (DEBUG) Log.v(TAG, "onReceive: " + intent); 3733 String action = intent.getAction(); 3734 if (ACTION_DEMO.equals(action)) { 3735 Bundle bundle = intent.getExtras(); 3736 if (bundle != null) { 3737 String command = bundle.getString("command", "").trim().toLowerCase(); 3738 if (command.length() > 0) { 3739 try { 3740 dispatchDemoCommand(command, bundle); 3741 } catch (Throwable t) { 3742 Log.w(TAG, "Error running demo command, intent=" + intent, t); 3743 } 3744 } 3745 } 3746 } else if (ACTION_FAKE_ARTWORK.equals(action)) { 3747 if (DEBUG_MEDIA_FAKE_ARTWORK) { 3748 updateMediaMetaData(true, true); 3749 } 3750 } 3751 } 3752 }; 3753 3754 public void resetUserExpandedStates() { 3755 ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications(); 3756 final int notificationCount = activeNotifications.size(); 3757 for (int i = 0; i < notificationCount; i++) { 3758 NotificationData.Entry entry = activeNotifications.get(i); 3759 if (entry.row != null) { 3760 entry.row.resetUserExpansion(); 3761 } 3762 } 3763 } 3764 3765 protected void dismissKeyguardThenExecute(OnDismissAction action, boolean afterKeyguardGone) { 3766 dismissKeyguardThenExecute(action, null /* cancelRunnable */, afterKeyguardGone); 3767 } 3768 3769 private void dismissKeyguardThenExecute(OnDismissAction action, Runnable cancelAction, 3770 boolean afterKeyguardGone) { 3771 if (mStatusBarKeyguardViewManager.isShowing()) { 3772 mStatusBarKeyguardViewManager.dismissWithAction(action, cancelAction, 3773 afterKeyguardGone); 3774 } else { 3775 action.onDismiss(); 3776 } 3777 } 3778 3779 // SystemUIService notifies SystemBars of configuration changes, which then calls down here 3780 @Override 3781 public void onConfigChanged(Configuration newConfig) { 3782 updateResources(); 3783 updateDisplaySize(); // populates mDisplayMetrics 3784 3785 if (DEBUG) { 3786 Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration()); 3787 } 3788 3789 updateRowStates(); 3790 mScreenPinningRequest.onConfigurationChanged(); 3791 } 3792 3793 public void userSwitched(int newUserId) { 3794 // Begin old BaseStatusBar.userSwitched 3795 setHeadsUpUser(newUserId); 3796 // End old BaseStatusBar.userSwitched 3797 if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId); 3798 animateCollapsePanels(); 3799 updatePublicMode(); 3800 mNotificationData.filterAndSort(); 3801 if (mReinflateNotificationsOnUserSwitched) { 3802 updateNotificationsOnDensityOrFontScaleChanged(); 3803 mReinflateNotificationsOnUserSwitched = false; 3804 } 3805 updateNotificationShade(); 3806 clearCurrentMediaNotification(); 3807 setLockscreenUser(newUserId); 3808 } 3809 3810 protected void setLockscreenUser(int newUserId) { 3811 mLockscreenWallpaper.setCurrentUser(newUserId); 3812 mScrimController.setCurrentUser(newUserId); 3813 updateMediaMetaData(true, false); 3814 } 3815 3816 /** 3817 * Reload some of our resources when the configuration changes. 3818 * 3819 * We don't reload everything when the configuration changes -- we probably 3820 * should, but getting that smooth is tough. Someday we'll fix that. In the 3821 * meantime, just update the things that we know change. 3822 */ 3823 void updateResources() { 3824 // Update the quick setting tiles 3825 if (mQSPanel != null) { 3826 mQSPanel.updateResources(); 3827 } 3828 3829 loadDimens(); 3830 3831 if (mNotificationPanel != null) { 3832 mNotificationPanel.updateResources(); 3833 } 3834 if (mBrightnessMirrorController != null) { 3835 mBrightnessMirrorController.updateResources(); 3836 } 3837 } 3838 3839 protected void loadDimens() { 3840 final Resources res = mContext.getResources(); 3841 3842 int oldBarHeight = mNaturalBarHeight; 3843 mNaturalBarHeight = res.getDimensionPixelSize( 3844 com.android.internal.R.dimen.status_bar_height); 3845 if (mStatusBarWindowManager != null && mNaturalBarHeight != oldBarHeight) { 3846 mStatusBarWindowManager.setBarHeight(mNaturalBarHeight); 3847 } 3848 mMaxAllowedKeyguardNotifications = res.getInteger( 3849 R.integer.keyguard_max_notification_count); 3850 3851 if (DEBUG) Log.v(TAG, "defineSlots"); 3852 } 3853 3854 // Visibility reporting 3855 3856 protected void handleVisibleToUserChanged(boolean visibleToUser) { 3857 if (visibleToUser) { 3858 handleVisibleToUserChangedImpl(visibleToUser); 3859 startNotificationLogging(); 3860 } else { 3861 stopNotificationLogging(); 3862 handleVisibleToUserChangedImpl(visibleToUser); 3863 } 3864 } 3865 3866 void handlePeekToExpandTransistion() { 3867 try { 3868 // consider the transition from peek to expanded to be a panel open, 3869 // but not one that clears notification effects. 3870 int notificationLoad = mNotificationData.getActiveNotifications().size(); 3871 mBarService.onPanelRevealed(false, notificationLoad); 3872 } catch (RemoteException ex) { 3873 // Won't fail unless the world has ended. 3874 } 3875 } 3876 3877 /** 3878 * The LEDs are turned off when the notification panel is shown, even just a little bit. 3879 * See also StatusBar.setPanelExpanded for another place where we attempt to do this. 3880 */ 3881 // Old BaseStatusBar.handleVisibileToUserChanged 3882 private void handleVisibleToUserChangedImpl(boolean visibleToUser) { 3883 try { 3884 if (visibleToUser) { 3885 boolean pinnedHeadsUp = mHeadsUpManager.hasPinnedHeadsUp(); 3886 boolean clearNotificationEffects = 3887 !isPanelFullyCollapsed() && 3888 (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED); 3889 int notificationLoad = mNotificationData.getActiveNotifications().size(); 3890 if (pinnedHeadsUp && isPanelFullyCollapsed()) { 3891 notificationLoad = 1; 3892 } 3893 mBarService.onPanelRevealed(clearNotificationEffects, notificationLoad); 3894 } else { 3895 mBarService.onPanelHidden(); 3896 } 3897 } catch (RemoteException ex) { 3898 // Won't fail unless the world has ended. 3899 } 3900 } 3901 3902 private void stopNotificationLogging() { 3903 // Report all notifications as invisible and turn down the 3904 // reporter. 3905 if (!mCurrentlyVisibleNotifications.isEmpty()) { 3906 logNotificationVisibilityChanges(Collections.<NotificationVisibility>emptyList(), 3907 mCurrentlyVisibleNotifications); 3908 recycleAllVisibilityObjects(mCurrentlyVisibleNotifications); 3909 } 3910 mHandler.removeCallbacks(mVisibilityReporter); 3911 mStackScroller.setChildLocationsChangedListener(null); 3912 } 3913 3914 private void startNotificationLogging() { 3915 mStackScroller.setChildLocationsChangedListener(mNotificationLocationsChangedListener); 3916 // Some transitions like mVisibleToUser=false -> mVisibleToUser=true don't 3917 // cause the scroller to emit child location events. Hence generate 3918 // one ourselves to guarantee that we're reporting visible 3919 // notifications. 3920 // (Note that in cases where the scroller does emit events, this 3921 // additional event doesn't break anything.) 3922 mNotificationLocationsChangedListener.onChildLocationsChanged(mStackScroller); 3923 } 3924 3925 private void logNotificationVisibilityChanges( 3926 Collection<NotificationVisibility> newlyVisible, 3927 Collection<NotificationVisibility> noLongerVisible) { 3928 if (newlyVisible.isEmpty() && noLongerVisible.isEmpty()) { 3929 return; 3930 } 3931 NotificationVisibility[] newlyVisibleAr = 3932 newlyVisible.toArray(new NotificationVisibility[newlyVisible.size()]); 3933 NotificationVisibility[] noLongerVisibleAr = 3934 noLongerVisible.toArray(new NotificationVisibility[noLongerVisible.size()]); 3935 try { 3936 mBarService.onNotificationVisibilityChanged(newlyVisibleAr, noLongerVisibleAr); 3937 } catch (RemoteException e) { 3938 // Ignore. 3939 } 3940 3941 final int N = newlyVisible.size(); 3942 if (N > 0) { 3943 String[] newlyVisibleKeyAr = new String[N]; 3944 for (int i = 0; i < N; i++) { 3945 newlyVisibleKeyAr[i] = newlyVisibleAr[i].key; 3946 } 3947 3948 setNotificationsShown(newlyVisibleKeyAr); 3949 } 3950 } 3951 3952 // State logging 3953 3954 private void logStateToEventlog() { 3955 boolean isShowing = mStatusBarKeyguardViewManager.isShowing(); 3956 boolean isOccluded = mStatusBarKeyguardViewManager.isOccluded(); 3957 boolean isBouncerShowing = mStatusBarKeyguardViewManager.isBouncerShowing(); 3958 boolean isSecure = mUnlockMethodCache.isMethodSecure(); 3959 boolean canSkipBouncer = mUnlockMethodCache.canSkipBouncer(); 3960 int stateFingerprint = getLoggingFingerprint(mState, 3961 isShowing, 3962 isOccluded, 3963 isBouncerShowing, 3964 isSecure, 3965 canSkipBouncer); 3966 if (stateFingerprint != mLastLoggedStateFingerprint) { 3967 if (mStatusBarStateLog == null) { 3968 mStatusBarStateLog = new LogMaker(MetricsEvent.VIEW_UNKNOWN); 3969 } 3970 mMetricsLogger.write(mStatusBarStateLog 3971 .setCategory(isBouncerShowing ? MetricsEvent.BOUNCER : MetricsEvent.LOCKSCREEN) 3972 .setType(isShowing ? MetricsEvent.TYPE_OPEN : MetricsEvent.TYPE_CLOSE) 3973 .setSubtype(isSecure ? 1 : 0)); 3974 EventLogTags.writeSysuiStatusBarState(mState, 3975 isShowing ? 1 : 0, 3976 isOccluded ? 1 : 0, 3977 isBouncerShowing ? 1 : 0, 3978 isSecure ? 1 : 0, 3979 canSkipBouncer ? 1 : 0); 3980 mLastLoggedStateFingerprint = stateFingerprint; 3981 } 3982 } 3983 3984 /** 3985 * Returns a fingerprint of fields logged to eventlog 3986 */ 3987 private static int getLoggingFingerprint(int statusBarState, boolean keyguardShowing, 3988 boolean keyguardOccluded, boolean bouncerShowing, boolean secure, 3989 boolean currentlyInsecure) { 3990 // Reserve 8 bits for statusBarState. We'll never go higher than 3991 // that, right? Riiiight. 3992 return (statusBarState & 0xFF) 3993 | ((keyguardShowing ? 1 : 0) << 8) 3994 | ((keyguardOccluded ? 1 : 0) << 9) 3995 | ((bouncerShowing ? 1 : 0) << 10) 3996 | ((secure ? 1 : 0) << 11) 3997 | ((currentlyInsecure ? 1 : 0) << 12); 3998 } 3999 4000 // 4001 // tracing 4002 // 4003 4004 void postStartTracing() { 4005 mHandler.postDelayed(mStartTracing, 3000); 4006 } 4007 4008 void vibrate() { 4009 android.os.Vibrator vib = (android.os.Vibrator)mContext.getSystemService( 4010 Context.VIBRATOR_SERVICE); 4011 vib.vibrate(250, VIBRATION_ATTRIBUTES); 4012 } 4013 4014 Runnable mStartTracing = new Runnable() { 4015 @Override 4016 public void run() { 4017 vibrate(); 4018 SystemClock.sleep(250); 4019 Log.d(TAG, "startTracing"); 4020 android.os.Debug.startMethodTracing("/data/statusbar-traces/trace"); 4021 mHandler.postDelayed(mStopTracing, 10000); 4022 } 4023 }; 4024 4025 Runnable mStopTracing = new Runnable() { 4026 @Override 4027 public void run() { 4028 android.os.Debug.stopMethodTracing(); 4029 Log.d(TAG, "stopTracing"); 4030 vibrate(); 4031 } 4032 }; 4033 4034 @Override 4035 public void postQSRunnableDismissingKeyguard(final Runnable runnable) { 4036 mHandler.post(() -> { 4037 mLeaveOpenOnKeyguardHide = true; 4038 executeRunnableDismissingKeyguard(() -> mHandler.post(runnable), null, false, false, 4039 false); 4040 }); 4041 } 4042 4043 @Override 4044 public void postStartActivityDismissingKeyguard(final PendingIntent intent) { 4045 mHandler.post(() -> startPendingIntentDismissingKeyguard(intent)); 4046 } 4047 4048 @Override 4049 public void postStartActivityDismissingKeyguard(final Intent intent, int delay) { 4050 mHandler.postDelayed(() -> 4051 handleStartActivityDismissingKeyguard(intent, true /*onlyProvisioned*/), delay); 4052 } 4053 4054 private void handleStartActivityDismissingKeyguard(Intent intent, boolean onlyProvisioned) { 4055 startActivityDismissingKeyguard(intent, onlyProvisioned, true /* dismissShade */); 4056 } 4057 4058 private static class FastColorDrawable extends Drawable { 4059 private final int mColor; 4060 4061 public FastColorDrawable(int color) { 4062 mColor = 0xff000000 | color; 4063 } 4064 4065 @Override 4066 public void draw(Canvas canvas) { 4067 canvas.drawColor(mColor, PorterDuff.Mode.SRC); 4068 } 4069 4070 @Override 4071 public void setAlpha(int alpha) { 4072 } 4073 4074 @Override 4075 public void setColorFilter(ColorFilter colorFilter) { 4076 } 4077 4078 @Override 4079 public int getOpacity() { 4080 return PixelFormat.OPAQUE; 4081 } 4082 4083 @Override 4084 public void setBounds(int left, int top, int right, int bottom) { 4085 } 4086 4087 @Override 4088 public void setBounds(Rect bounds) { 4089 } 4090 } 4091 4092 public void destroy() { 4093 // Begin old BaseStatusBar.destroy(). 4094 mContext.unregisterReceiver(mBaseBroadcastReceiver); 4095 try { 4096 mNotificationListener.unregisterAsSystemService(); 4097 } catch (RemoteException e) { 4098 // Ignore. 4099 } 4100 mDeviceProvisionedController.removeCallback(mDeviceProvisionedListener); 4101 // End old BaseStatusBar.destroy(). 4102 if (mStatusBarWindow != null) { 4103 mWindowManager.removeViewImmediate(mStatusBarWindow); 4104 mStatusBarWindow = null; 4105 } 4106 if (mNavigationBarView != null) { 4107 mWindowManager.removeViewImmediate(mNavigationBarView); 4108 mNavigationBarView = null; 4109 } 4110 mContext.unregisterReceiver(mBroadcastReceiver); 4111 mContext.unregisterReceiver(mDemoReceiver); 4112 mAssistManager.destroy(); 4113 4114 if (mQSPanel != null && mQSPanel.getHost() != null) { 4115 mQSPanel.getHost().destroy(); 4116 } 4117 Dependency.get(ActivityStarterDelegate.class).setActivityStarterImpl(null); 4118 mDeviceProvisionedController.removeCallback(mUserSetupObserver); 4119 Dependency.get(ConfigurationController.class).removeCallback(this); 4120 } 4121 4122 private boolean mDemoModeAllowed; 4123 private boolean mDemoMode; 4124 4125 @Override 4126 public void dispatchDemoCommand(String command, Bundle args) { 4127 if (!mDemoModeAllowed) { 4128 mDemoModeAllowed = Settings.Global.getInt(mContext.getContentResolver(), 4129 DEMO_MODE_ALLOWED, 0) != 0; 4130 } 4131 if (!mDemoModeAllowed) return; 4132 if (command.equals(COMMAND_ENTER)) { 4133 mDemoMode = true; 4134 } else if (command.equals(COMMAND_EXIT)) { 4135 mDemoMode = false; 4136 checkBarModes(); 4137 } else if (!mDemoMode) { 4138 // automatically enter demo mode on first demo command 4139 dispatchDemoCommand(COMMAND_ENTER, new Bundle()); 4140 } 4141 boolean modeChange = command.equals(COMMAND_ENTER) || command.equals(COMMAND_EXIT); 4142 if ((modeChange || command.equals(COMMAND_VOLUME)) && mVolumeComponent != null) { 4143 mVolumeComponent.dispatchDemoCommand(command, args); 4144 } 4145 if (modeChange || command.equals(COMMAND_CLOCK)) { 4146 dispatchDemoCommandToView(command, args, R.id.clock); 4147 } 4148 if (modeChange || command.equals(COMMAND_BATTERY)) { 4149 mBatteryController.dispatchDemoCommand(command, args); 4150 } 4151 if (modeChange || command.equals(COMMAND_STATUS)) { 4152 ((StatusBarIconControllerImpl) mIconController).dispatchDemoCommand(command, args); 4153 } 4154 if (mNetworkController != null && (modeChange || command.equals(COMMAND_NETWORK))) { 4155 mNetworkController.dispatchDemoCommand(command, args); 4156 } 4157 if (modeChange || command.equals(COMMAND_NOTIFICATIONS)) { 4158 View notifications = mStatusBarView == null ? null 4159 : mStatusBarView.findViewById(R.id.notification_icon_area); 4160 if (notifications != null) { 4161 String visible = args.getString("visible"); 4162 int vis = mDemoMode && "false".equals(visible) ? View.INVISIBLE : View.VISIBLE; 4163 notifications.setVisibility(vis); 4164 } 4165 } 4166 if (command.equals(COMMAND_BARS)) { 4167 String mode = args.getString("mode"); 4168 int barMode = "opaque".equals(mode) ? MODE_OPAQUE : 4169 "translucent".equals(mode) ? MODE_TRANSLUCENT : 4170 "semi-transparent".equals(mode) ? MODE_SEMI_TRANSPARENT : 4171 "transparent".equals(mode) ? MODE_TRANSPARENT : 4172 "warning".equals(mode) ? MODE_WARNING : 4173 -1; 4174 if (barMode != -1) { 4175 boolean animate = true; 4176 if (mStatusBarView != null) { 4177 mStatusBarView.getBarTransitions().transitionTo(barMode, animate); 4178 } 4179 if (mNavigationBar != null) { 4180 mNavigationBar.getBarTransitions().transitionTo(barMode, animate); 4181 } 4182 } 4183 } 4184 } 4185 4186 private void dispatchDemoCommandToView(String command, Bundle args, int id) { 4187 if (mStatusBarView == null) return; 4188 View v = mStatusBarView.findViewById(id); 4189 if (v instanceof DemoMode) { 4190 ((DemoMode)v).dispatchDemoCommand(command, args); 4191 } 4192 } 4193 4194 /** 4195 * @return The {@link StatusBarState} the status bar is in. 4196 */ 4197 public int getBarState() { 4198 return mState; 4199 } 4200 4201 public boolean isPanelFullyCollapsed() { 4202 return mNotificationPanel.isFullyCollapsed(); 4203 } 4204 4205 public void showKeyguard() { 4206 mKeyguardRequested = true; 4207 // Unconditionally show keyguard again. There's some logic that relies on this 4208 // being called even when the keyguard is already showing, e.g. for updating 4209 // sensitiveness of notifications and making sure the panels are expanded. 4210 showKeyguardImpl(); 4211 } 4212 4213 public boolean hideKeyguard() { 4214 mKeyguardRequested = false; 4215 return updateIsKeyguard(); 4216 } 4217 4218 private boolean updateIsKeyguard() { 4219 // For dozing, keyguard needs to be shown whenever the device is non-interactive. Otherwise 4220 // there's no surface we can show to the user. Note that the device goes fully interactive 4221 // late in the transition, so we also allow the device to start dozing once the screen has 4222 // turned off fully. 4223 boolean keyguardForDozing = mDozingRequested && 4224 (!mDeviceInteractive || mStartedGoingToSleep && (mScreenFullyOff || mIsKeyguard)); 4225 boolean shouldBeKeyguard = mKeyguardRequested || keyguardForDozing; 4226 if (keyguardForDozing) { 4227 updatePanelExpansionForKeyguard(); 4228 } 4229 if (shouldBeKeyguard) { 4230 showKeyguardImpl(); 4231 } else { 4232 return hideKeyguardImpl(); 4233 } 4234 return false; 4235 } 4236 4237 public void showKeyguardImpl() { 4238 mIsKeyguard = true; 4239 if (mLaunchTransitionFadingAway) { 4240 mNotificationPanel.animate().cancel(); 4241 onLaunchTransitionFadingEnded(); 4242 } 4243 mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT); 4244 if (mUserSwitcherController != null && mUserSwitcherController.useFullscreenUserSwitcher()) { 4245 setBarState(StatusBarState.FULLSCREEN_USER_SWITCHER); 4246 } else { 4247 setBarState(StatusBarState.KEYGUARD); 4248 } 4249 updateKeyguardState(false /* goingToFullShade */, false /* fromShadeLocked */); 4250 updatePanelExpansionForKeyguard(); 4251 mLeaveOpenOnKeyguardHide = false; 4252 if (mDraggedDownRow != null) { 4253 mDraggedDownRow.setUserLocked(false); 4254 mDraggedDownRow.notifyHeightChanged(false /* needsAnimation */); 4255 mDraggedDownRow = null; 4256 } 4257 mPendingRemoteInputView = null; 4258 mAssistManager.onLockscreenShown(); 4259 } 4260 4261 private void updatePanelExpansionForKeyguard() { 4262 if (mState == StatusBarState.KEYGUARD) { 4263 instantExpandNotificationsPanel(); 4264 } else if (mState == StatusBarState.FULLSCREEN_USER_SWITCHER) { 4265 instantCollapseNotificationPanel(); 4266 } 4267 } 4268 4269 private void onLaunchTransitionFadingEnded() { 4270 mNotificationPanel.setAlpha(1.0f); 4271 mNotificationPanel.onAffordanceLaunchEnded(); 4272 releaseGestureWakeLock(); 4273 runLaunchTransitionEndRunnable(); 4274 mLaunchTransitionFadingAway = false; 4275 mScrimController.forceHideScrims(false /* hide */, false /* animated */); 4276 updateMediaMetaData(true /* metaDataChanged */, true); 4277 } 4278 4279 public boolean isCollapsing() { 4280 return mNotificationPanel.isCollapsing(); 4281 } 4282 4283 public void addPostCollapseAction(Runnable r) { 4284 mPostCollapseRunnables.add(r); 4285 } 4286 4287 public boolean isInLaunchTransition() { 4288 return mNotificationPanel.isLaunchTransitionRunning() 4289 || mNotificationPanel.isLaunchTransitionFinished(); 4290 } 4291 4292 /** 4293 * Fades the content of the keyguard away after the launch transition is done. 4294 * 4295 * @param beforeFading the runnable to be run when the circle is fully expanded and the fading 4296 * starts 4297 * @param endRunnable the runnable to be run when the transition is done 4298 */ 4299 public void fadeKeyguardAfterLaunchTransition(final Runnable beforeFading, 4300 Runnable endRunnable) { 4301 mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT); 4302 mLaunchTransitionEndRunnable = endRunnable; 4303 Runnable hideRunnable = new Runnable() { 4304 @Override 4305 public void run() { 4306 mLaunchTransitionFadingAway = true; 4307 if (beforeFading != null) { 4308 beforeFading.run(); 4309 } 4310 mScrimController.forceHideScrims(true /* hide */, false /* animated */); 4311 updateMediaMetaData(false, true); 4312 mNotificationPanel.setAlpha(1); 4313 mStackScroller.setParentNotFullyVisible(true); 4314 mNotificationPanel.animate() 4315 .alpha(0) 4316 .setStartDelay(FADE_KEYGUARD_START_DELAY) 4317 .setDuration(FADE_KEYGUARD_DURATION) 4318 .withLayer() 4319 .withEndAction(new Runnable() { 4320 @Override 4321 public void run() { 4322 onLaunchTransitionFadingEnded(); 4323 } 4324 }); 4325 mCommandQueue.appTransitionStarting(SystemClock.uptimeMillis(), 4326 LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true); 4327 } 4328 }; 4329 if (mNotificationPanel.isLaunchTransitionRunning()) { 4330 mNotificationPanel.setLaunchTransitionEndRunnable(hideRunnable); 4331 } else { 4332 hideRunnable.run(); 4333 } 4334 } 4335 4336 /** 4337 * Fades the content of the Keyguard while we are dozing and makes it invisible when finished 4338 * fading. 4339 */ 4340 public void fadeKeyguardWhilePulsing() { 4341 mNotificationPanel.notifyStartFading(); 4342 mNotificationPanel.animate() 4343 .alpha(0f) 4344 .setStartDelay(0) 4345 .setDuration(FADE_KEYGUARD_DURATION_PULSING) 4346 .setInterpolator(ScrimController.KEYGUARD_FADE_OUT_INTERPOLATOR) 4347 .start(); 4348 } 4349 4350 /** 4351 * Plays the animation when an activity that was occluding Keyguard goes away. 4352 */ 4353 public void animateKeyguardUnoccluding() { 4354 mScrimController.animateKeyguardUnoccluding(500); 4355 mNotificationPanel.setExpandedFraction(0f); 4356 animateExpandNotificationsPanel(); 4357 } 4358 4359 /** 4360 * Starts the timeout when we try to start the affordances on Keyguard. We usually rely that 4361 * Keyguard goes away via fadeKeyguardAfterLaunchTransition, however, that might not happen 4362 * because the launched app crashed or something else went wrong. 4363 */ 4364 public void startLaunchTransitionTimeout() { 4365 mHandler.sendEmptyMessageDelayed(MSG_LAUNCH_TRANSITION_TIMEOUT, 4366 LAUNCH_TRANSITION_TIMEOUT_MS); 4367 } 4368 4369 private void onLaunchTransitionTimeout() { 4370 Log.w(TAG, "Launch transition: Timeout!"); 4371 mNotificationPanel.onAffordanceLaunchEnded(); 4372 releaseGestureWakeLock(); 4373 mNotificationPanel.resetViews(); 4374 } 4375 4376 private void runLaunchTransitionEndRunnable() { 4377 if (mLaunchTransitionEndRunnable != null) { 4378 Runnable r = mLaunchTransitionEndRunnable; 4379 4380 // mLaunchTransitionEndRunnable might call showKeyguard, which would execute it again, 4381 // which would lead to infinite recursion. Protect against it. 4382 mLaunchTransitionEndRunnable = null; 4383 r.run(); 4384 } 4385 } 4386 4387 /** 4388 * @return true if we would like to stay in the shade, false if it should go away entirely 4389 */ 4390 public boolean hideKeyguardImpl() { 4391 mIsKeyguard = false; 4392 Trace.beginSection("StatusBar#hideKeyguard"); 4393 boolean staying = mLeaveOpenOnKeyguardHide; 4394 setBarState(StatusBarState.SHADE); 4395 View viewToClick = null; 4396 if (mLeaveOpenOnKeyguardHide) { 4397 mLeaveOpenOnKeyguardHide = false; 4398 long delay = calculateGoingToFullShadeDelay(); 4399 mNotificationPanel.animateToFullShade(delay); 4400 if (mDraggedDownRow != null) { 4401 mDraggedDownRow.setUserLocked(false); 4402 mDraggedDownRow = null; 4403 } 4404 viewToClick = mPendingRemoteInputView; 4405 mPendingRemoteInputView = null; 4406 4407 // Disable layout transitions in navbar for this transition because the load is just 4408 // too heavy for the CPU and GPU on any device. 4409 if (mNavigationBar != null) { 4410 mNavigationBar.disableAnimationsDuringHide(delay); 4411 } 4412 } else if (!mNotificationPanel.isCollapsing()) { 4413 instantCollapseNotificationPanel(); 4414 } 4415 updateKeyguardState(staying, false /* fromShadeLocked */); 4416 4417 if (viewToClick != null && viewToClick.isAttachedToWindow()) { 4418 viewToClick.callOnClick(); 4419 } 4420 4421 // Keyguard state has changed, but QS is not listening anymore. Make sure to update the tile 4422 // visibilities so next time we open the panel we know the correct height already. 4423 if (mQSPanel != null) { 4424 mQSPanel.refreshAllTiles(); 4425 } 4426 mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT); 4427 releaseGestureWakeLock(); 4428 mNotificationPanel.onAffordanceLaunchEnded(); 4429 mNotificationPanel.animate().cancel(); 4430 mNotificationPanel.setAlpha(1f); 4431 Trace.endSection(); 4432 return staying; 4433 } 4434 4435 private void releaseGestureWakeLock() { 4436 if (mGestureWakeLock.isHeld()) { 4437 mGestureWakeLock.release(); 4438 } 4439 } 4440 4441 public long calculateGoingToFullShadeDelay() { 4442 return mKeyguardFadingAwayDelay + mKeyguardFadingAwayDuration; 4443 } 4444 4445 /** 4446 * Notifies the status bar that Keyguard is going away very soon. 4447 */ 4448 public void keyguardGoingAway() { 4449 4450 // Treat Keyguard exit animation as an app transition to achieve nice transition for status 4451 // bar. 4452 mKeyguardGoingAway = true; 4453 mKeyguardMonitor.notifyKeyguardGoingAway(true); 4454 mCommandQueue.appTransitionPending(true); 4455 } 4456 4457 /** 4458 * Notifies the status bar the Keyguard is fading away with the specified timings. 4459 * 4460 * @param startTime the start time of the animations in uptime millis 4461 * @param delay the precalculated animation delay in miliseconds 4462 * @param fadeoutDuration the duration of the exit animation, in milliseconds 4463 */ 4464 public void setKeyguardFadingAway(long startTime, long delay, long fadeoutDuration) { 4465 mKeyguardFadingAway = true; 4466 mKeyguardFadingAwayDelay = delay; 4467 mKeyguardFadingAwayDuration = fadeoutDuration; 4468 mWaitingForKeyguardExit = false; 4469 mCommandQueue.appTransitionStarting(startTime + fadeoutDuration 4470 - LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, 4471 LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true); 4472 recomputeDisableFlags(fadeoutDuration > 0 /* animate */); 4473 mCommandQueue.appTransitionStarting( 4474 startTime - LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, 4475 LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true); 4476 mKeyguardMonitor.notifyKeyguardFadingAway(delay, fadeoutDuration); 4477 } 4478 4479 public boolean isKeyguardFadingAway() { 4480 return mKeyguardFadingAway; 4481 } 4482 4483 /** 4484 * Notifies that the Keyguard fading away animation is done. 4485 */ 4486 public void finishKeyguardFadingAway() { 4487 mKeyguardFadingAway = false; 4488 mKeyguardGoingAway = false; 4489 mKeyguardMonitor.notifyKeyguardDoneFading(); 4490 } 4491 4492 public void stopWaitingForKeyguardExit() { 4493 mWaitingForKeyguardExit = false; 4494 } 4495 4496 private void updatePublicMode() { 4497 final boolean showingKeyguard = mStatusBarKeyguardViewManager.isShowing(); 4498 final boolean devicePublic = showingKeyguard 4499 && mStatusBarKeyguardViewManager.isSecure(mCurrentUserId); 4500 4501 // Look for public mode users. Users are considered public in either case of: 4502 // - device keyguard is shown in secure mode; 4503 // - profile is locked with a work challenge. 4504 for (int i = mCurrentProfiles.size() - 1; i >= 0; i--) { 4505 final int userId = mCurrentProfiles.valueAt(i).id; 4506 boolean isProfilePublic = devicePublic; 4507 if (!devicePublic && userId != mCurrentUserId) { 4508 // We can't rely on KeyguardManager#isDeviceLocked() for unified profile challenge 4509 // due to a race condition where this code could be called before 4510 // TrustManagerService updates its internal records, resulting in an incorrect 4511 // state being cached in mLockscreenPublicMode. (b/35951989) 4512 if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId) 4513 && mStatusBarKeyguardViewManager.isSecure(userId)) { 4514 isProfilePublic = mKeyguardManager.isDeviceLocked(userId); 4515 } 4516 } 4517 setLockscreenPublicMode(isProfilePublic, userId); 4518 } 4519 } 4520 4521 protected void updateKeyguardState(boolean goingToFullShade, boolean fromShadeLocked) { 4522 Trace.beginSection("StatusBar#updateKeyguardState"); 4523 if (mState == StatusBarState.KEYGUARD) { 4524 mKeyguardIndicationController.setVisible(true); 4525 mNotificationPanel.resetViews(); 4526 if (mKeyguardUserSwitcher != null) { 4527 mKeyguardUserSwitcher.setKeyguard(true, fromShadeLocked); 4528 } 4529 mStatusBarView.removePendingHideExpandedRunnables(); 4530 if (mAmbientIndicationContainer != null) { 4531 mAmbientIndicationContainer.setVisibility(View.VISIBLE); 4532 } 4533 } else { 4534 mKeyguardIndicationController.setVisible(false); 4535 if (mKeyguardUserSwitcher != null) { 4536 mKeyguardUserSwitcher.setKeyguard(false, 4537 goingToFullShade || 4538 mState == StatusBarState.SHADE_LOCKED || 4539 fromShadeLocked); 4540 } 4541 if (mAmbientIndicationContainer != null) { 4542 mAmbientIndicationContainer.setVisibility(View.INVISIBLE); 4543 } 4544 } 4545 if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) { 4546 mScrimController.setKeyguardShowing(true); 4547 } else { 4548 mScrimController.setKeyguardShowing(false); 4549 } 4550 mNotificationPanel.setBarState(mState, mKeyguardFadingAway, goingToFullShade); 4551 updateTheme(); 4552 updateDozingState(); 4553 updatePublicMode(); 4554 updateStackScrollerState(goingToFullShade, fromShadeLocked); 4555 updateNotifications(); 4556 checkBarModes(); 4557 updateMediaMetaData(false, mState != StatusBarState.KEYGUARD); 4558 mKeyguardMonitor.notifyKeyguardState(mStatusBarKeyguardViewManager.isShowing(), 4559 mUnlockMethodCache.isMethodSecure(), 4560 mStatusBarKeyguardViewManager.isOccluded()); 4561 Trace.endSection(); 4562 } 4563 4564 /** 4565 * Switches theme from light to dark and vice-versa. 4566 */ 4567 private void updateTheme() { 4568 4569 int which; 4570 if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) { 4571 which = WallpaperManager.FLAG_LOCK; 4572 } else { 4573 which = WallpaperManager.FLAG_SYSTEM; 4574 } 4575 4576 // Gradient defines if text color should be light or dark. 4577 final boolean useDarkText = mColorExtractor.getColors(which, true /* ignoreVisibility */) 4578 .supportsDarkText(); 4579 // And wallpaper defines if QS should be light or dark. 4580 WallpaperColors systemColors = mColorExtractor 4581 .getWallpaperColors(WallpaperManager.FLAG_SYSTEM); 4582 final boolean useDarkTheme = systemColors != null 4583 && (systemColors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_THEME) != 0; 4584 4585 // Enable/disable dark UI. 4586 if (isUsingDarkTheme() != useDarkTheme) { 4587 try { 4588 mOverlayManager.setEnabled("com.android.systemui.theme.dark", 4589 useDarkTheme, mCurrentUserId); 4590 } catch (RemoteException e) { 4591 Log.w(TAG, "Can't change theme", e); 4592 } 4593 } 4594 // Enable/disable dark text overlay. 4595 if (isUsingDarkText() != useDarkText) { 4596 try { 4597 mOverlayManager.setEnabled("com.android.systemui.theme.lightwallpaper", 4598 useDarkText, mCurrentUserId); 4599 } catch (RemoteException e) { 4600 Log.w(TAG, "Can't change theme", e); 4601 } 4602 } 4603 4604 // Make sure we have the correct navbar/statusbar colors. 4605 mStatusBarWindowManager.setKeyguardDark(useDarkText); 4606 } 4607 4608 private void updateDozingState() { 4609 Trace.beginSection("StatusBar#updateDozingState"); 4610 boolean animate = !mDozing && mDozeServiceHost.shouldAnimateWakeup(); 4611 mNotificationPanel.setDozing(mDozing, animate); 4612 mStackScroller.setDark(mDozing, animate, mWakeUpTouchLocation); 4613 mScrimController.setDozing(mDozing); 4614 mKeyguardIndicationController.setDozing(mDozing); 4615 mNotificationPanel.setDark(mDozing, animate); 4616 updateQsExpansionEnabled(); 4617 mDozeScrimController.setDozing(mDozing, animate); 4618 updateRowStates(); 4619 Trace.endSection(); 4620 } 4621 4622 public void updateStackScrollerState(boolean goingToFullShade, boolean fromShadeLocked) { 4623 if (mStackScroller == null) return; 4624 boolean onKeyguard = mState == StatusBarState.KEYGUARD; 4625 boolean publicMode = isAnyProfilePublicMode(); 4626 mStackScroller.setHideSensitive(publicMode, goingToFullShade); 4627 mStackScroller.setDimmed(onKeyguard, fromShadeLocked /* animate */); 4628 mStackScroller.setExpandingEnabled(!onKeyguard); 4629 ActivatableNotificationView activatedChild = mStackScroller.getActivatedChild(); 4630 mStackScroller.setActivatedChild(null); 4631 if (activatedChild != null) { 4632 activatedChild.makeInactive(false /* animate */); 4633 } 4634 } 4635 4636 public void userActivity() { 4637 if (mState == StatusBarState.KEYGUARD) { 4638 mKeyguardViewMediatorCallback.userActivity(); 4639 } 4640 } 4641 4642 public boolean interceptMediaKey(KeyEvent event) { 4643 return mState == StatusBarState.KEYGUARD 4644 && mStatusBarKeyguardViewManager.interceptMediaKey(event); 4645 } 4646 4647 protected boolean shouldUnlockOnMenuPressed() { 4648 return mDeviceInteractive && mState != StatusBarState.SHADE 4649 && mStatusBarKeyguardViewManager.shouldDismissOnMenuPressed(); 4650 } 4651 4652 public boolean onMenuPressed() { 4653 if (shouldUnlockOnMenuPressed()) { 4654 animateCollapsePanels( 4655 CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL /* flags */, true /* force */); 4656 return true; 4657 } 4658 return false; 4659 } 4660 4661 public void endAffordanceLaunch() { 4662 releaseGestureWakeLock(); 4663 mNotificationPanel.onAffordanceLaunchEnded(); 4664 } 4665 4666 public boolean onBackPressed() { 4667 if (mStatusBarKeyguardViewManager.onBackPressed()) { 4668 return true; 4669 } 4670 if (mNotificationPanel.isQsExpanded()) { 4671 if (mNotificationPanel.isQsDetailShowing()) { 4672 mNotificationPanel.closeQsDetail(); 4673 } else { 4674 mNotificationPanel.animateCloseQs(); 4675 } 4676 return true; 4677 } 4678 if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED) { 4679 animateCollapsePanels(); 4680 return true; 4681 } 4682 if (mKeyguardUserSwitcher.hideIfNotSimple(true)) { 4683 return true; 4684 } 4685 return false; 4686 } 4687 4688 public boolean onSpacePressed() { 4689 if (mDeviceInteractive && mState != StatusBarState.SHADE) { 4690 animateCollapsePanels( 4691 CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL /* flags */, true /* force */); 4692 return true; 4693 } 4694 return false; 4695 } 4696 4697 private void showBouncerIfKeyguard() { 4698 if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) { 4699 showBouncer(); 4700 } 4701 } 4702 4703 protected void showBouncer() { 4704 mWaitingForKeyguardExit = mStatusBarKeyguardViewManager.isShowing(); 4705 mStatusBarKeyguardViewManager.dismiss(); 4706 } 4707 4708 private void instantExpandNotificationsPanel() { 4709 // Make our window larger and the panel expanded. 4710 makeExpandedVisible(true); 4711 mNotificationPanel.expand(false /* animate */); 4712 } 4713 4714 private void instantCollapseNotificationPanel() { 4715 mNotificationPanel.instantCollapse(); 4716 } 4717 4718 @Override 4719 public void onActivated(ActivatableNotificationView view) { 4720 onActivated((View)view); 4721 mStackScroller.setActivatedChild(view); 4722 } 4723 4724 public void onActivated(View view) { 4725 mLockscreenGestureLogger.write( 4726 MetricsEvent.ACTION_LS_NOTE, 4727 0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */); 4728 mKeyguardIndicationController.showTransientIndication(R.string.notification_tap_again); 4729 ActivatableNotificationView previousView = mStackScroller.getActivatedChild(); 4730 if (previousView != null) { 4731 previousView.makeInactive(true /* animate */); 4732 } 4733 } 4734 4735 /** 4736 * @param state The {@link StatusBarState} to set. 4737 */ 4738 public void setBarState(int state) { 4739 // If we're visible and switched to SHADE_LOCKED (the user dragged 4740 // down on the lockscreen), clear notification LED, vibration, 4741 // ringing. 4742 // Other transitions are covered in handleVisibleToUserChanged(). 4743 if (state != mState && mVisible && (state == StatusBarState.SHADE_LOCKED 4744 || (state == StatusBarState.SHADE && isGoingToNotificationShade()))) { 4745 clearNotificationEffects(); 4746 } 4747 if (state == StatusBarState.KEYGUARD) { 4748 removeRemoteInputEntriesKeptUntilCollapsed(); 4749 maybeEscalateHeadsUp(); 4750 } 4751 mState = state; 4752 mGroupManager.setStatusBarState(state); 4753 mHeadsUpManager.setStatusBarState(state); 4754 mFalsingManager.setStatusBarState(state); 4755 mStatusBarWindowManager.setStatusBarState(state); 4756 mStackScroller.setStatusBarState(state); 4757 updateReportRejectedTouchVisibility(); 4758 updateDozing(); 4759 updateTheme(); 4760 mNotificationShelf.setStatusBarState(state); 4761 } 4762 4763 @Override 4764 public void onActivationReset(ActivatableNotificationView view) { 4765 if (view == mStackScroller.getActivatedChild()) { 4766 mStackScroller.setActivatedChild(null); 4767 onActivationReset((View)view); 4768 } 4769 } 4770 4771 public void onActivationReset(View view) { 4772 mKeyguardIndicationController.hideTransientIndication(); 4773 } 4774 4775 public void onTrackingStarted() { 4776 runPostCollapseRunnables(); 4777 } 4778 4779 public void onClosingFinished() { 4780 runPostCollapseRunnables(); 4781 if (!isPanelFullyCollapsed()) { 4782 // if we set it not to be focusable when collapsing, we have to undo it when we aborted 4783 // the closing 4784 mStatusBarWindowManager.setStatusBarFocusable(true); 4785 } 4786 } 4787 4788 public void onUnlockHintStarted() { 4789 mFalsingManager.onUnlockHintStarted(); 4790 mKeyguardIndicationController.showTransientIndication(R.string.keyguard_unlock); 4791 } 4792 4793 public void onHintFinished() { 4794 // Delay the reset a bit so the user can read the text. 4795 mKeyguardIndicationController.hideTransientIndicationDelayed(HINT_RESET_DELAY_MS); 4796 } 4797 4798 public void onCameraHintStarted() { 4799 mFalsingManager.onCameraHintStarted(); 4800 mKeyguardIndicationController.showTransientIndication(R.string.camera_hint); 4801 } 4802 4803 public void onVoiceAssistHintStarted() { 4804 mFalsingManager.onLeftAffordanceHintStarted(); 4805 mKeyguardIndicationController.showTransientIndication(R.string.voice_hint); 4806 } 4807 4808 public void onPhoneHintStarted() { 4809 mFalsingManager.onLeftAffordanceHintStarted(); 4810 mKeyguardIndicationController.showTransientIndication(R.string.phone_hint); 4811 } 4812 4813 public void onTrackingStopped(boolean expand) { 4814 if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) { 4815 if (!expand && !mUnlockMethodCache.canSkipBouncer()) { 4816 showBouncerIfKeyguard(); 4817 } 4818 } 4819 } 4820 4821 protected int getMaxKeyguardNotifications(boolean recompute) { 4822 if (recompute) { 4823 mMaxKeyguardNotifications = Math.max(1, 4824 mNotificationPanel.computeMaxKeyguardNotifications( 4825 mMaxAllowedKeyguardNotifications)); 4826 return mMaxKeyguardNotifications; 4827 } 4828 return mMaxKeyguardNotifications; 4829 } 4830 4831 public int getMaxKeyguardNotifications() { 4832 return getMaxKeyguardNotifications(false /* recompute */); 4833 } 4834 4835 // TODO: Figure out way to remove these. 4836 public NavigationBarView getNavigationBarView() { 4837 return (mNavigationBar != null ? (NavigationBarView) mNavigationBar.getView() : null); 4838 } 4839 4840 public View getNavigationBarWindow() { 4841 return mNavigationBarView; 4842 } 4843 4844 /** 4845 * TODO: Remove this method. Views should not be passed forward. Will cause theme issues. 4846 * @return bottom area view 4847 */ 4848 public KeyguardBottomAreaView getKeyguardBottomAreaView() { 4849 return mNotificationPanel.getKeyguardBottomAreaView(); 4850 } 4851 4852 // ---------------------- DragDownHelper.OnDragDownListener ------------------------------------ 4853 4854 4855 /* Only ever called as a consequence of a lockscreen expansion gesture. */ 4856 @Override 4857 public boolean onDraggedDown(View startingChild, int dragLengthY) { 4858 if (mState == StatusBarState.KEYGUARD 4859 && hasActiveNotifications() && (!isDozing() || isPulsing())) { 4860 mLockscreenGestureLogger.write( 4861 MetricsEvent.ACTION_LS_SHADE, 4862 (int) (dragLengthY / mDisplayMetrics.density), 4863 0 /* velocityDp - N/A */); 4864 4865 // We have notifications, go to locked shade. 4866 goToLockedShade(startingChild); 4867 if (startingChild instanceof ExpandableNotificationRow) { 4868 ExpandableNotificationRow row = (ExpandableNotificationRow) startingChild; 4869 row.onExpandedByGesture(true /* drag down is always an open */); 4870 } 4871 return true; 4872 } else { 4873 // abort gesture. 4874 return false; 4875 } 4876 } 4877 4878 @Override 4879 public void onDragDownReset() { 4880 mStackScroller.setDimmed(true /* dimmed */, true /* animated */); 4881 mStackScroller.resetScrollPosition(); 4882 mStackScroller.resetCheckSnoozeLeavebehind(); 4883 } 4884 4885 @Override 4886 public void onCrossedThreshold(boolean above) { 4887 mStackScroller.setDimmed(!above /* dimmed */, true /* animate */); 4888 } 4889 4890 @Override 4891 public void onTouchSlopExceeded() { 4892 mStackScroller.removeLongPressCallback(); 4893 mStackScroller.checkSnoozeLeavebehind(); 4894 } 4895 4896 @Override 4897 public void setEmptyDragAmount(float amount) { 4898 mNotificationPanel.setEmptyDragAmount(amount); 4899 } 4900 4901 /** 4902 * If secure with redaction: Show bouncer, go to unlocked shade. 4903 * 4904 * <p>If secure without redaction or no security: Go to {@link StatusBarState#SHADE_LOCKED}.</p> 4905 * 4906 * @param expandView The view to expand after going to the shade. 4907 */ 4908 public void goToLockedShade(View expandView) { 4909 int userId = mCurrentUserId; 4910 ExpandableNotificationRow row = null; 4911 if (expandView instanceof ExpandableNotificationRow) { 4912 row = (ExpandableNotificationRow) expandView; 4913 row.setUserExpanded(true /* userExpanded */, true /* allowChildExpansion */); 4914 // Indicate that the group expansion is changing at this time -- this way the group 4915 // and children backgrounds / divider animations will look correct. 4916 row.setGroupExpansionChanging(true); 4917 if (row.getStatusBarNotification() != null) { 4918 userId = row.getStatusBarNotification().getUserId(); 4919 } 4920 } 4921 boolean fullShadeNeedsBouncer = !userAllowsPrivateNotificationsInPublic(mCurrentUserId) 4922 || !mShowLockscreenNotifications || mFalsingManager.shouldEnforceBouncer(); 4923 if (isLockscreenPublicMode(userId) && fullShadeNeedsBouncer) { 4924 mLeaveOpenOnKeyguardHide = true; 4925 showBouncerIfKeyguard(); 4926 mDraggedDownRow = row; 4927 mPendingRemoteInputView = null; 4928 } else { 4929 mNotificationPanel.animateToFullShade(0 /* delay */); 4930 setBarState(StatusBarState.SHADE_LOCKED); 4931 updateKeyguardState(false /* goingToFullShade */, false /* fromShadeLocked */); 4932 } 4933 } 4934 4935 public void onLockedNotificationImportanceChange(OnDismissAction dismissAction) { 4936 mLeaveOpenOnKeyguardHide = true; 4937 dismissKeyguardThenExecute(dismissAction, true /* afterKeyguardGone */); 4938 } 4939 4940 protected void onLockedRemoteInput(ExpandableNotificationRow row, View clicked) { 4941 mLeaveOpenOnKeyguardHide = true; 4942 showBouncer(); 4943 mPendingRemoteInputView = clicked; 4944 } 4945 4946 protected void onMakeExpandedVisibleForRemoteInput(ExpandableNotificationRow row, 4947 View clickedView) { 4948 if (isKeyguardShowing()) { 4949 onLockedRemoteInput(row, clickedView); 4950 } else { 4951 row.setUserExpanded(true); 4952 row.getPrivateLayout().setOnExpandedVisibleListener(clickedView::performClick); 4953 } 4954 } 4955 4956 protected boolean startWorkChallengeIfNecessary(int userId, IntentSender intendSender, 4957 String notificationKey) { 4958 // Clear pending remote view, as we do not want to trigger pending remote input view when 4959 // it's called by other code 4960 mPendingWorkRemoteInputView = null; 4961 // Begin old BaseStatusBar.startWorkChallengeIfNecessary. 4962 final Intent newIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null, 4963 null, userId); 4964 if (newIntent == null) { 4965 return false; 4966 } 4967 final Intent callBackIntent = new Intent(NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION); 4968 callBackIntent.putExtra(Intent.EXTRA_INTENT, intendSender); 4969 callBackIntent.putExtra(Intent.EXTRA_INDEX, notificationKey); 4970 callBackIntent.setPackage(mContext.getPackageName()); 4971 4972 PendingIntent callBackPendingIntent = PendingIntent.getBroadcast( 4973 mContext, 4974 0, 4975 callBackIntent, 4976 PendingIntent.FLAG_CANCEL_CURRENT | 4977 PendingIntent.FLAG_ONE_SHOT | 4978 PendingIntent.FLAG_IMMUTABLE); 4979 newIntent.putExtra( 4980 Intent.EXTRA_INTENT, 4981 callBackPendingIntent.getIntentSender()); 4982 try { 4983 ActivityManager.getService().startConfirmDeviceCredentialIntent(newIntent, 4984 null /*options*/); 4985 } catch (RemoteException ex) { 4986 // ignore 4987 } 4988 return true; 4989 // End old BaseStatusBar.startWorkChallengeIfNecessary. 4990 } 4991 4992 protected void onLockedWorkRemoteInput(int userId, ExpandableNotificationRow row, 4993 View clicked) { 4994 // Collapse notification and show work challenge 4995 animateCollapsePanels(); 4996 startWorkChallengeIfNecessary(userId, null, null); 4997 // Add pending remote input view after starting work challenge, as starting work challenge 4998 // will clear all previous pending review view 4999 mPendingWorkRemoteInputView = clicked; 5000 } 5001 5002 private boolean isAnyProfilePublicMode() { 5003 for (int i = mCurrentProfiles.size() - 1; i >= 0; i--) { 5004 if (isLockscreenPublicMode(mCurrentProfiles.valueAt(i).id)) { 5005 return true; 5006 } 5007 } 5008 return false; 5009 } 5010 5011 protected void onWorkChallengeChanged() { 5012 updatePublicMode(); 5013 updateNotifications(); 5014 if (mPendingWorkRemoteInputView != null && !isAnyProfilePublicMode()) { 5015 // Expand notification panel and the notification row, then click on remote input view 5016 final Runnable clickPendingViewRunnable = new Runnable() { 5017 @Override 5018 public void run() { 5019 final View pendingWorkRemoteInputView = mPendingWorkRemoteInputView; 5020 if (pendingWorkRemoteInputView == null) { 5021 return; 5022 } 5023 5024 // Climb up the hierarchy until we get to the container for this row. 5025 ViewParent p = pendingWorkRemoteInputView.getParent(); 5026 while (!(p instanceof ExpandableNotificationRow)) { 5027 if (p == null) { 5028 return; 5029 } 5030 p = p.getParent(); 5031 } 5032 5033 final ExpandableNotificationRow row = (ExpandableNotificationRow) p; 5034 ViewParent viewParent = row.getParent(); 5035 if (viewParent instanceof NotificationStackScrollLayout) { 5036 final NotificationStackScrollLayout scrollLayout = 5037 (NotificationStackScrollLayout) viewParent; 5038 row.makeActionsVisibile(); 5039 row.post(new Runnable() { 5040 @Override 5041 public void run() { 5042 final Runnable finishScrollingCallback = new Runnable() { 5043 @Override 5044 public void run() { 5045 mPendingWorkRemoteInputView.callOnClick(); 5046 mPendingWorkRemoteInputView = null; 5047 scrollLayout.setFinishScrollingCallback(null); 5048 } 5049 }; 5050 if (scrollLayout.scrollTo(row)) { 5051 // It scrolls! So call it when it's finished. 5052 scrollLayout.setFinishScrollingCallback( 5053 finishScrollingCallback); 5054 } else { 5055 // It does not scroll, so call it now! 5056 finishScrollingCallback.run(); 5057 } 5058 } 5059 }); 5060 } 5061 } 5062 }; 5063 mNotificationPanel.getViewTreeObserver().addOnGlobalLayoutListener( 5064 new ViewTreeObserver.OnGlobalLayoutListener() { 5065 @Override 5066 public void onGlobalLayout() { 5067 if (mNotificationPanel.mStatusBar.getStatusBarWindow() 5068 .getHeight() != mNotificationPanel.mStatusBar 5069 .getStatusBarHeight()) { 5070 mNotificationPanel.getViewTreeObserver() 5071 .removeOnGlobalLayoutListener(this); 5072 mNotificationPanel.post(clickPendingViewRunnable); 5073 } 5074 } 5075 }); 5076 instantExpandNotificationsPanel(); 5077 } 5078 } 5079 5080 @Override 5081 public void onExpandClicked(Entry clickedEntry, boolean nowExpanded) { 5082 mHeadsUpManager.setExpanded(clickedEntry, nowExpanded); 5083 if (mState == StatusBarState.KEYGUARD && nowExpanded) { 5084 goToLockedShade(clickedEntry.row); 5085 } 5086 } 5087 5088 /** 5089 * Goes back to the keyguard after hanging around in {@link StatusBarState#SHADE_LOCKED}. 5090 */ 5091 public void goToKeyguard() { 5092 if (mState == StatusBarState.SHADE_LOCKED) { 5093 mStackScroller.onGoToKeyguard(); 5094 setBarState(StatusBarState.KEYGUARD); 5095 updateKeyguardState(false /* goingToFullShade */, true /* fromShadeLocked*/); 5096 } 5097 } 5098 5099 public long getKeyguardFadingAwayDelay() { 5100 return mKeyguardFadingAwayDelay; 5101 } 5102 5103 public long getKeyguardFadingAwayDuration() { 5104 return mKeyguardFadingAwayDuration; 5105 } 5106 5107 public void setBouncerShowing(boolean bouncerShowing) { 5108 mBouncerShowing = bouncerShowing; 5109 if (mStatusBarView != null) mStatusBarView.setBouncerShowing(bouncerShowing); 5110 recomputeDisableFlags(true /* animate */); 5111 } 5112 5113 public void onStartedGoingToSleep() { 5114 mStartedGoingToSleep = true; 5115 } 5116 5117 public void onFinishedGoingToSleep() { 5118 mNotificationPanel.onAffordanceLaunchEnded(); 5119 releaseGestureWakeLock(); 5120 mLaunchCameraOnScreenTurningOn = false; 5121 mStartedGoingToSleep = false; 5122 mDeviceInteractive = false; 5123 mWakeUpComingFromTouch = false; 5124 mWakeUpTouchLocation = null; 5125 mStackScroller.setAnimationsEnabled(false); 5126 mVisualStabilityManager.setScreenOn(false); 5127 updateVisibleToUser(); 5128 5129 // We need to disable touch events because these might 5130 // collapse the panel after we expanded it, and thus we would end up with a blank 5131 // Keyguard. 5132 mNotificationPanel.setTouchDisabled(true); 5133 mStatusBarWindow.cancelCurrentTouch(); 5134 if (mLaunchCameraOnFinishedGoingToSleep) { 5135 mLaunchCameraOnFinishedGoingToSleep = false; 5136 5137 // This gets executed before we will show Keyguard, so post it in order that the state 5138 // is correct. 5139 mHandler.post(new Runnable() { 5140 @Override 5141 public void run() { 5142 onCameraLaunchGestureDetected(mLastCameraLaunchSource); 5143 } 5144 }); 5145 } 5146 updateIsKeyguard(); 5147 } 5148 5149 public void onStartedWakingUp() { 5150 mDeviceInteractive = true; 5151 mStackScroller.setAnimationsEnabled(true); 5152 mVisualStabilityManager.setScreenOn(true); 5153 mNotificationPanel.setTouchDisabled(false); 5154 updateVisibleToUser(); 5155 updateIsKeyguard(); 5156 } 5157 5158 public void onScreenTurningOn() { 5159 mScreenFullyOff = false; 5160 mScreenTurningOn = true; 5161 mFalsingManager.onScreenTurningOn(); 5162 mNotificationPanel.onScreenTurningOn(); 5163 if (mLaunchCameraOnScreenTurningOn) { 5164 mNotificationPanel.launchCamera(false, mLastCameraLaunchSource); 5165 mLaunchCameraOnScreenTurningOn = false; 5166 } 5167 } 5168 5169 private void vibrateForCameraGesture() { 5170 // Make sure to pass -1 for repeat so VibratorService doesn't stop us when going to sleep. 5171 mVibrator.vibrate(mCameraLaunchGestureVibePattern, -1 /* repeat */); 5172 } 5173 5174 public void onScreenTurnedOn() { 5175 mScreenTurningOn = false; 5176 mDozeScrimController.onScreenTurnedOn(); 5177 } 5178 5179 /** 5180 * @return true if the screen is currently fully off, i.e. has finished turning off and has 5181 * since not started turning on. 5182 */ 5183 public boolean isScreenFullyOff() { 5184 return mScreenFullyOff; 5185 } 5186 5187 @Override 5188 public void showScreenPinningRequest(int taskId) { 5189 if (mKeyguardMonitor.isShowing()) { 5190 // Don't allow apps to trigger this from keyguard. 5191 return; 5192 } 5193 // Show screen pinning request, since this comes from an app, show 'no thanks', button. 5194 showScreenPinningRequest(taskId, true); 5195 } 5196 5197 public void showScreenPinningRequest(int taskId, boolean allowCancel) { 5198 mScreenPinningRequest.showPrompt(taskId, allowCancel); 5199 } 5200 5201 public boolean hasActiveNotifications() { 5202 return !mNotificationData.getActiveNotifications().isEmpty(); 5203 } 5204 5205 public void wakeUpIfDozing(long time, View where) { 5206 if (mDozing) { 5207 PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); 5208 pm.wakeUp(time, "com.android.systemui:NODOZE"); 5209 mWakeUpComingFromTouch = true; 5210 where.getLocationInWindow(mTmpInt2); 5211 mWakeUpTouchLocation = new PointF(mTmpInt2[0] + where.getWidth() / 2, 5212 mTmpInt2[1] + where.getHeight() / 2); 5213 mStatusBarKeyguardViewManager.notifyDeviceWakeUpRequested(); 5214 mFalsingManager.onScreenOnFromTouch(); 5215 } 5216 } 5217 5218 @Override 5219 public void appTransitionCancelled() { 5220 EventBus.getDefault().send(new AppTransitionFinishedEvent()); 5221 } 5222 5223 @Override 5224 public void appTransitionFinished() { 5225 EventBus.getDefault().send(new AppTransitionFinishedEvent()); 5226 } 5227 5228 @Override 5229 public void onCameraLaunchGestureDetected(int source) { 5230 mLastCameraLaunchSource = source; 5231 if (mStartedGoingToSleep) { 5232 if (DEBUG_CAMERA_LIFT) Slog.d(TAG, "Finish going to sleep before launching camera"); 5233 mLaunchCameraOnFinishedGoingToSleep = true; 5234 return; 5235 } 5236 if (!mNotificationPanel.canCameraGestureBeLaunched( 5237 mStatusBarKeyguardViewManager.isShowing() && mExpandedVisible)) { 5238 if (DEBUG_CAMERA_LIFT) Slog.d(TAG, "Can't launch camera right now, mExpandedVisible: " + 5239 mExpandedVisible); 5240 return; 5241 } 5242 if (!mDeviceInteractive) { 5243 PowerManager pm = mContext.getSystemService(PowerManager.class); 5244 pm.wakeUp(SystemClock.uptimeMillis(), "com.android.systemui:CAMERA_GESTURE"); 5245 mStatusBarKeyguardViewManager.notifyDeviceWakeUpRequested(); 5246 } 5247 vibrateForCameraGesture(); 5248 if (!mStatusBarKeyguardViewManager.isShowing()) { 5249 startActivity(KeyguardBottomAreaView.INSECURE_CAMERA_INTENT, 5250 true /* dismissShade */); 5251 } else { 5252 if (!mDeviceInteractive) { 5253 // Avoid flickering of the scrim when we instant launch the camera and the bouncer 5254 // comes on. 5255 mScrimController.dontAnimateBouncerChangesUntilNextFrame(); 5256 mGestureWakeLock.acquire(LAUNCH_TRANSITION_TIMEOUT_MS + 1000L); 5257 } 5258 if (mScreenTurningOn || mStatusBarKeyguardViewManager.isScreenTurnedOn()) { 5259 if (DEBUG_CAMERA_LIFT) Slog.d(TAG, "Launching camera"); 5260 mNotificationPanel.launchCamera(mDeviceInteractive /* animate */, source); 5261 } else { 5262 // We need to defer the camera launch until the screen comes on, since otherwise 5263 // we will dismiss us too early since we are waiting on an activity to be drawn and 5264 // incorrectly get notified because of the screen on event (which resumes and pauses 5265 // some activities) 5266 if (DEBUG_CAMERA_LIFT) Slog.d(TAG, "Deferring until screen turns on"); 5267 mLaunchCameraOnScreenTurningOn = true; 5268 } 5269 } 5270 } 5271 5272 boolean isCameraAllowedByAdmin() { 5273 if (mDevicePolicyManager.getCameraDisabled(null, mCurrentUserId)) { 5274 return false; 5275 } else if (isKeyguardShowing() && isKeyguardSecure()) { 5276 // Check if the admin has disabled the camera specifically for the keyguard 5277 return (mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUserId) 5278 & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) == 0; 5279 } 5280 5281 return true; 5282 } 5283 5284 public void notifyFpAuthModeChanged() { 5285 updateDozing(); 5286 } 5287 5288 private void updateDozing() { 5289 Trace.beginSection("StatusBar#updateDozing"); 5290 // When in wake-and-unlock while pulsing, keep dozing state until fully unlocked. 5291 mDozing = mDozingRequested && mState == StatusBarState.KEYGUARD 5292 || mFingerprintUnlockController.getMode() 5293 == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING; 5294 // When in wake-and-unlock we may not have received a change to mState 5295 // but we still should not be dozing, manually set to false. 5296 if (mFingerprintUnlockController.getMode() == 5297 FingerprintUnlockController.MODE_WAKE_AND_UNLOCK) { 5298 mDozing = false; 5299 } 5300 mStatusBarWindowManager.setDozing(mDozing); 5301 mStatusBarKeyguardViewManager.setDozing(mDozing); 5302 updateDozingState(); 5303 Trace.endSection(); 5304 } 5305 5306 public boolean isKeyguardShowing() { 5307 if (mStatusBarKeyguardViewManager == null) { 5308 Slog.i(TAG, "isKeyguardShowing() called before startKeyguard(), returning true"); 5309 return true; 5310 } 5311 return mStatusBarKeyguardViewManager.isShowing(); 5312 } 5313 5314 private final class DozeServiceHost implements DozeHost { 5315 private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>(); 5316 private boolean mAnimateWakeup; 5317 5318 @Override 5319 public String toString() { 5320 return "PSB.DozeServiceHost[mCallbacks=" + mCallbacks.size() + "]"; 5321 } 5322 5323 public void firePowerSaveChanged(boolean active) { 5324 for (Callback callback : mCallbacks) { 5325 callback.onPowerSaveChanged(active); 5326 } 5327 } 5328 5329 public void fireNotificationHeadsUp() { 5330 for (Callback callback : mCallbacks) { 5331 callback.onNotificationHeadsUp(); 5332 } 5333 } 5334 5335 @Override 5336 public void addCallback(@NonNull Callback callback) { 5337 mCallbacks.add(callback); 5338 } 5339 5340 @Override 5341 public void removeCallback(@NonNull Callback callback) { 5342 mCallbacks.remove(callback); 5343 } 5344 5345 @Override 5346 public void startDozing() { 5347 if (!mDozingRequested) { 5348 mDozingRequested = true; 5349 DozeLog.traceDozing(mContext, mDozing); 5350 updateDozing(); 5351 updateIsKeyguard(); 5352 } 5353 } 5354 5355 @Override 5356 public void pulseWhileDozing(@NonNull PulseCallback callback, int reason) { 5357 if (reason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS) { 5358 mPowerManager.wakeUp(SystemClock.uptimeMillis(), "com.android.systemui:NODOZE"); 5359 startAssist(new Bundle()); 5360 return; 5361 } 5362 5363 mDozeScrimController.pulse(new PulseCallback() { 5364 5365 @Override 5366 public void onPulseStarted() { 5367 callback.onPulseStarted(); 5368 Collection<HeadsUpManager.HeadsUpEntry> pulsingEntries = 5369 mHeadsUpManager.getAllEntries(); 5370 if (!pulsingEntries.isEmpty()) { 5371 // Only pulse the stack scroller if there's actually something to show. 5372 // Otherwise just show the always-on screen. 5373 setPulsing(pulsingEntries); 5374 } 5375 } 5376 5377 @Override 5378 public void onPulseFinished() { 5379 callback.onPulseFinished(); 5380 setPulsing(null); 5381 } 5382 5383 private void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> pulsing) { 5384 mStackScroller.setPulsing(pulsing); 5385 mNotificationPanel.setPulsing(pulsing != null); 5386 mVisualStabilityManager.setPulsing(pulsing != null); 5387 } 5388 }, reason); 5389 } 5390 5391 @Override 5392 public void stopDozing() { 5393 if (mDozingRequested) { 5394 mDozingRequested = false; 5395 DozeLog.traceDozing(mContext, mDozing); 5396 updateDozing(); 5397 } 5398 } 5399 5400 @Override 5401 public void dozeTimeTick() { 5402 mNotificationPanel.refreshTime(); 5403 } 5404 5405 @Override 5406 public boolean isPowerSaveActive() { 5407 return mBatteryController.isPowerSave(); 5408 } 5409 5410 @Override 5411 public boolean isPulsingBlocked() { 5412 return mFingerprintUnlockController.getMode() 5413 == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK; 5414 } 5415 5416 @Override 5417 public boolean isProvisioned() { 5418 return mDeviceProvisionedController.isDeviceProvisioned() 5419 && mDeviceProvisionedController.isCurrentUserSetup(); 5420 } 5421 5422 @Override 5423 public boolean isBlockingDoze() { 5424 if (mFingerprintUnlockController.hasPendingAuthentication()) { 5425 Log.i(TAG, "Blocking AOD because fingerprint has authenticated"); 5426 return true; 5427 } 5428 return false; 5429 } 5430 5431 @Override 5432 public void startPendingIntentDismissingKeyguard(PendingIntent intent) { 5433 StatusBar.this.startPendingIntentDismissingKeyguard(intent); 5434 } 5435 5436 @Override 5437 public void abortPulsing() { 5438 mDozeScrimController.abortPulsing(); 5439 } 5440 5441 @Override 5442 public void extendPulse() { 5443 mDozeScrimController.extendPulse(); 5444 } 5445 5446 @Override 5447 public void setAnimateWakeup(boolean animateWakeup) { 5448 mAnimateWakeup = animateWakeup; 5449 } 5450 5451 @Override 5452 public void onDoubleTap(float screenX, float screenY) { 5453 if (screenX > 0 && screenY > 0 && mAmbientIndicationContainer != null 5454 && mAmbientIndicationContainer.getVisibility() == View.VISIBLE) { 5455 mAmbientIndicationContainer.getLocationOnScreen(mTmpInt2); 5456 float viewX = screenX - mTmpInt2[0]; 5457 float viewY = screenY - mTmpInt2[1]; 5458 if (0 <= viewX && viewX <= mAmbientIndicationContainer.getWidth() 5459 && 0 <= viewY && viewY <= mAmbientIndicationContainer.getHeight()) { 5460 dispatchDoubleTap(viewX, viewY); 5461 } 5462 } 5463 } 5464 5465 @Override 5466 public void setDozeScreenBrightness(int value) { 5467 mStatusBarWindowManager.setDozeScreenBrightness(value); 5468 } 5469 5470 public void dispatchDoubleTap(float viewX, float viewY) { 5471 dispatchTap(mAmbientIndicationContainer, viewX, viewY); 5472 dispatchTap(mAmbientIndicationContainer, viewX, viewY); 5473 } 5474 5475 private void dispatchTap(View view, float x, float y) { 5476 long now = SystemClock.elapsedRealtime(); 5477 dispatchTouchEvent(view, x, y, now, MotionEvent.ACTION_DOWN); 5478 dispatchTouchEvent(view, x, y, now, MotionEvent.ACTION_UP); 5479 } 5480 5481 private void dispatchTouchEvent(View view, float x, float y, long now, int action) { 5482 MotionEvent ev = MotionEvent.obtain(now, now, action, x, y, 0 /* meta */); 5483 view.dispatchTouchEvent(ev); 5484 ev.recycle(); 5485 } 5486 5487 private boolean shouldAnimateWakeup() { 5488 return mAnimateWakeup; 5489 } 5490 } 5491 5492 // Begin Extra BaseStatusBar methods. 5493 5494 protected CommandQueue mCommandQueue; 5495 protected IStatusBarService mBarService; 5496 5497 // all notifications 5498 protected NotificationData mNotificationData; 5499 protected NotificationStackScrollLayout mStackScroller; 5500 5501 protected NotificationGroupManager mGroupManager = new NotificationGroupManager(); 5502 5503 protected RemoteInputController mRemoteInputController; 5504 5505 // for heads up notifications 5506 protected HeadsUpManager mHeadsUpManager; 5507 5508 private AboveShelfObserver mAboveShelfObserver; 5509 5510 // handling reordering 5511 protected VisualStabilityManager mVisualStabilityManager = new VisualStabilityManager(); 5512 5513 protected int mCurrentUserId = 0; 5514 final protected SparseArray<UserInfo> mCurrentProfiles = new SparseArray<UserInfo>(); 5515 5516 protected int mLayoutDirection = -1; // invalid 5517 protected AccessibilityManager mAccessibilityManager; 5518 5519 protected boolean mDeviceInteractive; 5520 5521 protected boolean mVisible; 5522 protected ArraySet<Entry> mHeadsUpEntriesToRemoveOnSwitch = new ArraySet<>(); 5523 protected ArraySet<Entry> mRemoteInputEntriesToRemoveOnCollapse = new ArraySet<>(); 5524 5525 /** 5526 * Notifications with keys in this set are not actually around anymore. We kept them around 5527 * when they were canceled in response to a remote input interaction. This allows us to show 5528 * what you replied and allows you to continue typing into it. 5529 */ 5530 protected ArraySet<String> mKeysKeptForRemoteInput = new ArraySet<>(); 5531 5532 // mScreenOnFromKeyguard && mVisible. 5533 private boolean mVisibleToUser; 5534 5535 private Locale mLocale; 5536 5537 protected boolean mUseHeadsUp = false; 5538 protected boolean mHeadsUpTicker = false; 5539 protected boolean mDisableNotificationAlerts = false; 5540 5541 protected DevicePolicyManager mDevicePolicyManager; 5542 protected PowerManager mPowerManager; 5543 protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; 5544 5545 // public mode, private notifications, etc 5546 private final SparseBooleanArray mLockscreenPublicMode = new SparseBooleanArray(); 5547 private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray(); 5548 private final SparseBooleanArray mUsersAllowingNotifications = new SparseBooleanArray(); 5549 5550 private UserManager mUserManager; 5551 5552 protected KeyguardManager mKeyguardManager; 5553 private LockPatternUtils mLockPatternUtils; 5554 private DeviceProvisionedController mDeviceProvisionedController; 5555 protected SystemServicesProxy mSystemServicesProxy; 5556 5557 // UI-specific methods 5558 5559 protected WindowManager mWindowManager; 5560 protected IWindowManager mWindowManagerService; 5561 5562 protected Display mDisplay; 5563 5564 protected RecentsComponent mRecents; 5565 5566 protected int mZenMode; 5567 5568 // which notification is currently being longpress-examined by the user 5569 private NotificationGuts mNotificationGutsExposed; 5570 private MenuItem mGutsMenuItem; 5571 5572 private KeyboardShortcuts mKeyboardShortcuts; 5573 5574 protected NotificationShelf mNotificationShelf; 5575 protected DismissView mDismissView; 5576 protected EmptyShadeView mEmptyShadeView; 5577 5578 private NotificationClicker mNotificationClicker = new NotificationClicker(); 5579 5580 protected AssistManager mAssistManager; 5581 5582 protected boolean mVrMode; 5583 5584 private Set<String> mNonBlockablePkgs; 5585 5586 public boolean isDeviceInteractive() { 5587 return mDeviceInteractive; 5588 } 5589 5590 @Override // NotificationData.Environment 5591 public boolean isDeviceProvisioned() { 5592 return mDeviceProvisionedController.isDeviceProvisioned(); 5593 } 5594 5595 private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() { 5596 @Override 5597 public void onVrStateChanged(boolean enabled) { 5598 mVrMode = enabled; 5599 } 5600 }; 5601 5602 public boolean isDeviceInVrMode() { 5603 return mVrMode; 5604 } 5605 5606 private final DeviceProvisionedListener mDeviceProvisionedListener = 5607 new DeviceProvisionedListener() { 5608 @Override 5609 public void onDeviceProvisionedChanged() { 5610 updateNotifications(); 5611 } 5612 }; 5613 5614 protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) { 5615 @Override 5616 public void onChange(boolean selfChange) { 5617 final int mode = Settings.Global.getInt(mContext.getContentResolver(), 5618 Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF); 5619 setZenMode(mode); 5620 5621 updateLockscreenNotificationSetting(); 5622 } 5623 }; 5624 5625 private final ContentObserver mLockscreenSettingsObserver = new ContentObserver(mHandler) { 5626 @Override 5627 public void onChange(boolean selfChange) { 5628 // We don't know which user changed LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS or 5629 // LOCK_SCREEN_SHOW_NOTIFICATIONS, so we just dump our cache ... 5630 mUsersAllowingPrivateNotifications.clear(); 5631 mUsersAllowingNotifications.clear(); 5632 // ... and refresh all the notifications 5633 updateLockscreenNotificationSetting(); 5634 updateNotifications(); 5635 } 5636 }; 5637 5638 private RemoteViews.OnClickHandler mOnClickHandler = new RemoteViews.OnClickHandler() { 5639 5640 @Override 5641 public boolean onClickHandler( 5642 final View view, final PendingIntent pendingIntent, final Intent fillInIntent) { 5643 wakeUpIfDozing(SystemClock.uptimeMillis(), view); 5644 5645 5646 if (handleRemoteInput(view, pendingIntent, fillInIntent)) { 5647 return true; 5648 } 5649 5650 if (DEBUG) { 5651 Log.v(TAG, "Notification click handler invoked for intent: " + pendingIntent); 5652 } 5653 logActionClick(view); 5654 // The intent we are sending is for the application, which 5655 // won't have permission to immediately start an activity after 5656 // the user switches to home. We know it is safe to do at this 5657 // point, so make sure new activity switches are now allowed. 5658 try { 5659 ActivityManager.getService().resumeAppSwitches(); 5660 } catch (RemoteException e) { 5661 } 5662 final boolean isActivity = pendingIntent.isActivity(); 5663 if (isActivity) { 5664 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing(); 5665 final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity( 5666 mContext, pendingIntent.getIntent(), mCurrentUserId); 5667 dismissKeyguardThenExecute(new OnDismissAction() { 5668 @Override 5669 public boolean onDismiss() { 5670 try { 5671 ActivityManager.getService().resumeAppSwitches(); 5672 } catch (RemoteException e) { 5673 } 5674 5675 boolean handled = superOnClickHandler(view, pendingIntent, fillInIntent); 5676 5677 // close the shade if it was open 5678 if (handled) { 5679 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, 5680 true /* force */); 5681 visibilityChanged(false); 5682 mAssistManager.hideAssist(); 5683 } 5684 5685 // Wait for activity start. 5686 return handled; 5687 } 5688 }, afterKeyguardGone); 5689 return true; 5690 } else { 5691 return superOnClickHandler(view, pendingIntent, fillInIntent); 5692 } 5693 } 5694 5695 private void logActionClick(View view) { 5696 ViewParent parent = view.getParent(); 5697 String key = getNotificationKeyForParent(parent); 5698 if (key == null) { 5699 Log.w(TAG, "Couldn't determine notification for click."); 5700 return; 5701 } 5702 int index = -1; 5703 // If this is a default template, determine the index of the button. 5704 if (view.getId() == com.android.internal.R.id.action0 && 5705 parent != null && parent instanceof ViewGroup) { 5706 ViewGroup actionGroup = (ViewGroup) parent; 5707 index = actionGroup.indexOfChild(view); 5708 } 5709 try { 5710 mBarService.onNotificationActionClick(key, index); 5711 } catch (RemoteException e) { 5712 // Ignore 5713 } 5714 } 5715 5716 private String getNotificationKeyForParent(ViewParent parent) { 5717 while (parent != null) { 5718 if (parent instanceof ExpandableNotificationRow) { 5719 return ((ExpandableNotificationRow) parent).getStatusBarNotification().getKey(); 5720 } 5721 parent = parent.getParent(); 5722 } 5723 return null; 5724 } 5725 5726 private boolean superOnClickHandler(View view, PendingIntent pendingIntent, 5727 Intent fillInIntent) { 5728 return super.onClickHandler(view, pendingIntent, fillInIntent, 5729 StackId.FULLSCREEN_WORKSPACE_STACK_ID); 5730 } 5731 5732 private boolean handleRemoteInput(View view, PendingIntent pendingIntent, Intent fillInIntent) { 5733 Object tag = view.getTag(com.android.internal.R.id.remote_input_tag); 5734 RemoteInput[] inputs = null; 5735 if (tag instanceof RemoteInput[]) { 5736 inputs = (RemoteInput[]) tag; 5737 } 5738 5739 if (inputs == null) { 5740 return false; 5741 } 5742 5743 RemoteInput input = null; 5744 5745 for (RemoteInput i : inputs) { 5746 if (i.getAllowFreeFormInput()) { 5747 input = i; 5748 } 5749 } 5750 5751 if (input == null) { 5752 return false; 5753 } 5754 5755 ViewParent p = view.getParent(); 5756 RemoteInputView riv = null; 5757 while (p != null) { 5758 if (p instanceof View) { 5759 View pv = (View) p; 5760 if (pv.isRootNamespace()) { 5761 riv = findRemoteInputView(pv); 5762 break; 5763 } 5764 } 5765 p = p.getParent(); 5766 } 5767 ExpandableNotificationRow row = null; 5768 while (p != null) { 5769 if (p instanceof ExpandableNotificationRow) { 5770 row = (ExpandableNotificationRow) p; 5771 break; 5772 } 5773 p = p.getParent(); 5774 } 5775 5776 if (row == null) { 5777 return false; 5778 } 5779 5780 row.setUserExpanded(true); 5781 5782 if (!mAllowLockscreenRemoteInput) { 5783 final int userId = pendingIntent.getCreatorUserHandle().getIdentifier(); 5784 if (isLockscreenPublicMode(userId)) { 5785 onLockedRemoteInput(row, view); 5786 return true; 5787 } 5788 if (mUserManager.getUserInfo(userId).isManagedProfile() 5789 && mKeyguardManager.isDeviceLocked(userId)) { 5790 onLockedWorkRemoteInput(userId, row, view); 5791 return true; 5792 } 5793 } 5794 5795 if (riv == null) { 5796 riv = findRemoteInputView(row.getPrivateLayout().getExpandedChild()); 5797 if (riv == null) { 5798 return false; 5799 } 5800 if (!row.getPrivateLayout().getExpandedChild().isShown()) { 5801 onMakeExpandedVisibleForRemoteInput(row, view); 5802 return true; 5803 } 5804 } 5805 5806 int width = view.getWidth(); 5807 if (view instanceof TextView) { 5808 // Center the reveal on the text which might be off-center from the TextView 5809 TextView tv = (TextView) view; 5810 if (tv.getLayout() != null) { 5811 int innerWidth = (int) tv.getLayout().getLineWidth(0); 5812 innerWidth += tv.getCompoundPaddingLeft() + tv.getCompoundPaddingRight(); 5813 width = Math.min(width, innerWidth); 5814 } 5815 } 5816 int cx = view.getLeft() + width / 2; 5817 int cy = view.getTop() + view.getHeight() / 2; 5818 int w = riv.getWidth(); 5819 int h = riv.getHeight(); 5820 int r = Math.max( 5821 Math.max(cx + cy, cx + (h - cy)), 5822 Math.max((w - cx) + cy, (w - cx) + (h - cy))); 5823 5824 riv.setRevealParameters(cx, cy, r); 5825 riv.setPendingIntent(pendingIntent); 5826 riv.setRemoteInput(inputs, input); 5827 riv.focusAnimated(); 5828 5829 return true; 5830 } 5831 5832 private RemoteInputView findRemoteInputView(View v) { 5833 if (v == null) { 5834 return null; 5835 } 5836 return (RemoteInputView) v.findViewWithTag(RemoteInputView.VIEW_TAG); 5837 } 5838 }; 5839 5840 private final BroadcastReceiver mBaseBroadcastReceiver = new BroadcastReceiver() { 5841 @Override 5842 public void onReceive(Context context, Intent intent) { 5843 String action = intent.getAction(); 5844 if (Intent.ACTION_USER_SWITCHED.equals(action)) { 5845 mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); 5846 updateCurrentProfilesCache(); 5847 if (true) Log.v(TAG, "userId " + mCurrentUserId + " is in the house"); 5848 5849 updateLockscreenNotificationSetting(); 5850 5851 userSwitched(mCurrentUserId); 5852 } else if (Intent.ACTION_USER_ADDED.equals(action)) { 5853 updateCurrentProfilesCache(); 5854 } else if (Intent.ACTION_USER_PRESENT.equals(action)) { 5855 List<ActivityManager.RecentTaskInfo> recentTask = null; 5856 try { 5857 recentTask = ActivityManager.getService().getRecentTasks(1, 5858 ActivityManager.RECENT_WITH_EXCLUDED 5859 | ActivityManager.RECENT_INCLUDE_PROFILES, 5860 mCurrentUserId).getList(); 5861 } catch (RemoteException e) { 5862 // Abandon hope activity manager not running. 5863 } 5864 if (recentTask != null && recentTask.size() > 0) { 5865 UserInfo user = mUserManager.getUserInfo(recentTask.get(0).userId); 5866 if (user != null && user.isManagedProfile()) { 5867 Toast toast = Toast.makeText(mContext, 5868 R.string.managed_profile_foreground_toast, 5869 Toast.LENGTH_SHORT); 5870 TextView text = (TextView) toast.getView().findViewById( 5871 android.R.id.message); 5872 text.setCompoundDrawablesRelativeWithIntrinsicBounds( 5873 R.drawable.stat_sys_managed_profile_status, 0, 0, 0); 5874 int paddingPx = mContext.getResources().getDimensionPixelSize( 5875 R.dimen.managed_profile_toast_padding); 5876 text.setCompoundDrawablePadding(paddingPx); 5877 toast.show(); 5878 } 5879 } 5880 } else if (BANNER_ACTION_CANCEL.equals(action) || BANNER_ACTION_SETUP.equals(action)) { 5881 NotificationManager noMan = (NotificationManager) 5882 mContext.getSystemService(Context.NOTIFICATION_SERVICE); 5883 noMan.cancel(SystemMessage.NOTE_HIDDEN_NOTIFICATIONS); 5884 5885 Settings.Secure.putInt(mContext.getContentResolver(), 5886 Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0); 5887 if (BANNER_ACTION_SETUP.equals(action)) { 5888 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, 5889 true /* force */); 5890 mContext.startActivity(new Intent(Settings.ACTION_APP_NOTIFICATION_REDACTION) 5891 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 5892 5893 ); 5894 } 5895 } else if (NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION.equals(action)) { 5896 final IntentSender intentSender = intent.getParcelableExtra(Intent.EXTRA_INTENT); 5897 final String notificationKey = intent.getStringExtra(Intent.EXTRA_INDEX); 5898 if (intentSender != null) { 5899 try { 5900 mContext.startIntentSender(intentSender, null, 0, 0, 0); 5901 } catch (IntentSender.SendIntentException e) { 5902 /* ignore */ 5903 } 5904 } 5905 if (notificationKey != null) { 5906 try { 5907 mBarService.onNotificationClick(notificationKey); 5908 } catch (RemoteException e) { 5909 /* ignore */ 5910 } 5911 } 5912 } 5913 } 5914 }; 5915 5916 private final BroadcastReceiver mAllUsersReceiver = new BroadcastReceiver() { 5917 @Override 5918 public void onReceive(Context context, Intent intent) { 5919 final String action = intent.getAction(); 5920 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); 5921 5922 if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action) && 5923 isCurrentProfile(getSendingUserId())) { 5924 mUsersAllowingPrivateNotifications.clear(); 5925 updateLockscreenNotificationSetting(); 5926 updateNotifications(); 5927 } else if (Intent.ACTION_DEVICE_LOCKED_CHANGED.equals(action)) { 5928 if (userId != mCurrentUserId && isCurrentProfile(userId)) { 5929 onWorkChallengeChanged(); 5930 } 5931 } 5932 } 5933 }; 5934 5935 private final NotificationListenerWithPlugins mNotificationListener = 5936 new NotificationListenerWithPlugins() { 5937 @Override 5938 public void onListenerConnected() { 5939 if (DEBUG) Log.d(TAG, "onListenerConnected"); 5940 onPluginConnected(); 5941 final StatusBarNotification[] notifications = getActiveNotifications(); 5942 if (notifications == null) { 5943 Log.w(TAG, "onListenerConnected unable to get active notifications."); 5944 return; 5945 } 5946 final RankingMap currentRanking = getCurrentRanking(); 5947 mHandler.post(new Runnable() { 5948 @Override 5949 public void run() { 5950 for (StatusBarNotification sbn : notifications) { 5951 try { 5952 addNotification(sbn, currentRanking); 5953 } catch (InflationException e) { 5954 handleInflationException(sbn, e); 5955 } 5956 } 5957 } 5958 }); 5959 } 5960 5961 @Override 5962 public void onNotificationPosted(final StatusBarNotification sbn, 5963 final RankingMap rankingMap) { 5964 if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn); 5965 if (sbn != null && !onPluginNotificationPosted(sbn, rankingMap)) { 5966 mHandler.post(new Runnable() { 5967 @Override 5968 public void run() { 5969 processForRemoteInput(sbn.getNotification()); 5970 String key = sbn.getKey(); 5971 mKeysKeptForRemoteInput.remove(key); 5972 boolean isUpdate = mNotificationData.get(key) != null; 5973 // In case we don't allow child notifications, we ignore children of 5974 // notifications that have a summary, since we're not going to show them 5975 // anyway. This is true also when the summary is canceled, 5976 // because children are automatically canceled by NoMan in that case. 5977 if (!ENABLE_CHILD_NOTIFICATIONS 5978 && mGroupManager.isChildInGroupWithSummary(sbn)) { 5979 if (DEBUG) { 5980 Log.d(TAG, "Ignoring group child due to existing summary: " + sbn); 5981 } 5982 5983 // Remove existing notification to avoid stale data. 5984 if (isUpdate) { 5985 removeNotification(key, rankingMap); 5986 } else { 5987 mNotificationData.updateRanking(rankingMap); 5988 } 5989 return; 5990 } 5991 try { 5992 if (isUpdate) { 5993 updateNotification(sbn, rankingMap); 5994 } else { 5995 addNotification(sbn, rankingMap); 5996 } 5997 } catch (InflationException e) { 5998 handleInflationException(sbn, e); 5999 } 6000 } 6001 }); 6002 } 6003 } 6004 6005 @Override 6006 public void onNotificationRemoved(StatusBarNotification sbn, 6007 final RankingMap rankingMap) { 6008 if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn); 6009 if (sbn != null && !onPluginNotificationRemoved(sbn, rankingMap)) { 6010 final String key = sbn.getKey(); 6011 mHandler.post(() -> removeNotification(key, rankingMap)); 6012 } 6013 } 6014 6015 @Override 6016 public void onNotificationRankingUpdate(final RankingMap rankingMap) { 6017 if (DEBUG) Log.d(TAG, "onRankingUpdate"); 6018 if (rankingMap != null) { 6019 RankingMap r = onPluginRankingUpdate(rankingMap); 6020 mHandler.post(() -> updateNotificationRanking(r)); 6021 } 6022 } 6023 6024 }; 6025 6026 private void updateCurrentProfilesCache() { 6027 synchronized (mCurrentProfiles) { 6028 mCurrentProfiles.clear(); 6029 if (mUserManager != null) { 6030 for (UserInfo user : mUserManager.getProfiles(mCurrentUserId)) { 6031 mCurrentProfiles.put(user.id, user); 6032 } 6033 } 6034 } 6035 } 6036 6037 protected void notifyUserAboutHiddenNotifications() { 6038 if (0 != Settings.Secure.getInt(mContext.getContentResolver(), 6039 Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 1)) { 6040 Log.d(TAG, "user hasn't seen notification about hidden notifications"); 6041 if (!mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())) { 6042 Log.d(TAG, "insecure lockscreen, skipping notification"); 6043 Settings.Secure.putInt(mContext.getContentResolver(), 6044 Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0); 6045 return; 6046 } 6047 Log.d(TAG, "disabling lockecreen notifications and alerting the user"); 6048 // disable lockscreen notifications until user acts on the banner. 6049 Settings.Secure.putInt(mContext.getContentResolver(), 6050 Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0); 6051 Settings.Secure.putInt(mContext.getContentResolver(), 6052 Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0); 6053 6054 final String packageName = mContext.getPackageName(); 6055 PendingIntent cancelIntent = PendingIntent.getBroadcast(mContext, 0, 6056 new Intent(BANNER_ACTION_CANCEL).setPackage(packageName), 6057 PendingIntent.FLAG_CANCEL_CURRENT); 6058 PendingIntent setupIntent = PendingIntent.getBroadcast(mContext, 0, 6059 new Intent(BANNER_ACTION_SETUP).setPackage(packageName), 6060 PendingIntent.FLAG_CANCEL_CURRENT); 6061 6062 final int colorRes = com.android.internal.R.color.system_notification_accent_color; 6063 Notification.Builder note = 6064 new Notification.Builder(mContext, NotificationChannels.GENERAL) 6065 .setSmallIcon(R.drawable.ic_android) 6066 .setContentTitle(mContext.getString( 6067 R.string.hidden_notifications_title)) 6068 .setContentText(mContext.getString(R.string.hidden_notifications_text)) 6069 .setOngoing(true) 6070 .setColor(mContext.getColor(colorRes)) 6071 .setContentIntent(setupIntent) 6072 .addAction(R.drawable.ic_close, 6073 mContext.getString(R.string.hidden_notifications_cancel), 6074 cancelIntent) 6075 .addAction(R.drawable.ic_settings, 6076 mContext.getString(R.string.hidden_notifications_setup), 6077 setupIntent); 6078 overrideNotificationAppName(mContext, note); 6079 6080 NotificationManager noMan = 6081 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); 6082 noMan.notify(SystemMessage.NOTE_HIDDEN_NOTIFICATIONS, note.build()); 6083 } 6084 } 6085 6086 @Override // NotificationData.Environment 6087 public boolean isNotificationForCurrentProfiles(StatusBarNotification n) { 6088 final int thisUserId = mCurrentUserId; 6089 final int notificationUserId = n.getUserId(); 6090 if (DEBUG && MULTIUSER_DEBUG) { 6091 Log.v(TAG, String.format("%s: current userid: %d, notification userid: %d", 6092 n, thisUserId, notificationUserId)); 6093 } 6094 return isCurrentProfile(notificationUserId); 6095 } 6096 6097 protected void setNotificationShown(StatusBarNotification n) { 6098 setNotificationsShown(new String[]{n.getKey()}); 6099 } 6100 6101 protected void setNotificationsShown(String[] keys) { 6102 try { 6103 mNotificationListener.setNotificationsShown(keys); 6104 } catch (RuntimeException e) { 6105 Log.d(TAG, "failed setNotificationsShown: ", e); 6106 } 6107 } 6108 6109 protected boolean isCurrentProfile(int userId) { 6110 synchronized (mCurrentProfiles) { 6111 return userId == UserHandle.USER_ALL || mCurrentProfiles.get(userId) != null; 6112 } 6113 } 6114 6115 @Override 6116 public NotificationGroupManager getGroupManager() { 6117 return mGroupManager; 6118 } 6119 6120 public boolean isMediaNotification(NotificationData.Entry entry) { 6121 // TODO: confirm that there's a valid media key 6122 return entry.getExpandedContentView() != null && 6123 entry.getExpandedContentView() 6124 .findViewById(com.android.internal.R.id.media_actions) != null; 6125 } 6126 6127 // The button in the guts that links to the system notification settings for that app 6128 private void startAppNotificationSettingsActivity(String packageName, final int appUid, 6129 final NotificationChannel channel) { 6130 final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS); 6131 intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName); 6132 intent.putExtra(Settings.EXTRA_APP_UID, appUid); 6133 if (channel != null) { 6134 intent.putExtra(EXTRA_FRAGMENT_ARG_KEY, channel.getId()); 6135 } 6136 startNotificationGutsIntent(intent, appUid); 6137 } 6138 6139 private void startNotificationGutsIntent(final Intent intent, final int appUid) { 6140 dismissKeyguardThenExecute(new OnDismissAction() { 6141 @Override 6142 public boolean onDismiss() { 6143 AsyncTask.execute(new Runnable() { 6144 @Override 6145 public void run() { 6146 TaskStackBuilder.create(mContext) 6147 .addNextIntentWithParentStack(intent) 6148 .startActivities(getActivityOptions(), 6149 new UserHandle(UserHandle.getUserId(appUid))); 6150 } 6151 }); 6152 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */); 6153 return true; 6154 } 6155 }, false /* afterKeyguardGone */); 6156 } 6157 6158 public void setNotificationSnoozed(StatusBarNotification sbn, SnoozeOption snoozeOption) { 6159 if (snoozeOption.getSnoozeCriterion() != null) { 6160 mNotificationListener.snoozeNotification(sbn.getKey(), 6161 snoozeOption.getSnoozeCriterion().getId()); 6162 } else { 6163 mNotificationListener.snoozeNotification(sbn.getKey(), 6164 snoozeOption.getMinutesToSnoozeFor() * 60 * 1000); 6165 } 6166 } 6167 6168 private void bindGuts(final ExpandableNotificationRow row, MenuItem item) { 6169 row.inflateGuts(); 6170 row.setGutsView(item); 6171 final StatusBarNotification sbn = row.getStatusBarNotification(); 6172 row.setTag(sbn.getPackageName()); 6173 final NotificationGuts guts = row.getGuts(); 6174 guts.setClosedListener((NotificationGuts g) -> { 6175 if (!g.willBeRemoved() && !row.isRemoved()) { 6176 mStackScroller.onHeightChanged(row, !isPanelFullyCollapsed() /* needsAnimation */); 6177 } 6178 if (mNotificationGutsExposed == g) { 6179 mNotificationGutsExposed = null; 6180 mGutsMenuItem = null; 6181 } 6182 String key = sbn.getKey(); 6183 if (key.equals(mKeyToRemoveOnGutsClosed)) { 6184 mKeyToRemoveOnGutsClosed = null; 6185 removeNotification(key, mLatestRankingMap); 6186 } 6187 }); 6188 6189 View gutsView = item.getGutsView(); 6190 if (gutsView instanceof NotificationSnooze) { 6191 NotificationSnooze snoozeGuts = (NotificationSnooze) gutsView; 6192 snoozeGuts.setSnoozeListener(mStackScroller.getSwipeActionHelper()); 6193 snoozeGuts.setStatusBarNotification(sbn); 6194 snoozeGuts.setSnoozeOptions(row.getEntry().snoozeCriteria); 6195 guts.setHeightChangedListener((NotificationGuts g) -> { 6196 mStackScroller.onHeightChanged(row, row.isShown() /* needsAnimation */); 6197 }); 6198 } 6199 6200 if (gutsView instanceof NotificationInfo) { 6201 final UserHandle userHandle = sbn.getUser(); 6202 PackageManager pmUser = getPackageManagerForUser(mContext, 6203 userHandle.getIdentifier()); 6204 final INotificationManager iNotificationManager = INotificationManager.Stub.asInterface( 6205 ServiceManager.getService(Context.NOTIFICATION_SERVICE)); 6206 final String pkg = sbn.getPackageName(); 6207 NotificationInfo info = (NotificationInfo) gutsView; 6208 // Settings link is only valid for notifications that specify a user, unless this is the 6209 // system user. 6210 NotificationInfo.OnSettingsClickListener onSettingsClick = null; 6211 if (!userHandle.equals(UserHandle.ALL) || mCurrentUserId == UserHandle.USER_SYSTEM) { 6212 onSettingsClick = (View v, NotificationChannel channel, int appUid) -> { 6213 mMetricsLogger.action(MetricsEvent.ACTION_NOTE_INFO); 6214 guts.resetFalsingCheck(); 6215 startAppNotificationSettingsActivity(pkg, appUid, channel); 6216 }; 6217 } 6218 final NotificationInfo.OnAppSettingsClickListener onAppSettingsClick = (View v, 6219 Intent intent) -> { 6220 mMetricsLogger.action(MetricsEvent.ACTION_APP_NOTE_SETTINGS); 6221 guts.resetFalsingCheck(); 6222 startNotificationGutsIntent(intent, sbn.getUid()); 6223 }; 6224 final View.OnClickListener onDoneClick = (View v) -> { 6225 saveAndCloseNotificationMenu(info, row, guts, v); 6226 }; 6227 final NotificationInfo.CheckSaveListener checkSaveListener = 6228 (Runnable saveImportance) -> { 6229 // If the user has security enabled, show challenge if the setting is changed. 6230 if (isLockscreenPublicMode(userHandle.getIdentifier()) 6231 && (mState == StatusBarState.KEYGUARD 6232 || mState == StatusBarState.SHADE_LOCKED)) { 6233 onLockedNotificationImportanceChange(() -> { 6234 saveImportance.run(); 6235 return true; 6236 }); 6237 } else { 6238 saveImportance.run(); 6239 } 6240 }; 6241 6242 ArraySet<NotificationChannel> channels = new ArraySet<NotificationChannel>(); 6243 channels.add(row.getEntry().channel); 6244 if (row.isSummaryWithChildren()) { 6245 // If this is a summary, then add in the children notification channels for the 6246 // same user and pkg. 6247 final List<ExpandableNotificationRow> childrenRows = row.getNotificationChildren(); 6248 final int numChildren = childrenRows.size(); 6249 for (int i = 0; i < numChildren; i++) { 6250 final ExpandableNotificationRow childRow = childrenRows.get(i); 6251 final NotificationChannel childChannel = childRow.getEntry().channel; 6252 final StatusBarNotification childSbn = childRow.getStatusBarNotification(); 6253 if (childSbn.getUser().equals(userHandle) && 6254 childSbn.getPackageName().equals(pkg)) { 6255 channels.add(childChannel); 6256 } 6257 } 6258 } 6259 try { 6260 info.bindNotification(pmUser, iNotificationManager, pkg, new ArrayList(channels), 6261 row.getEntry().channel.getImportance(), sbn, onSettingsClick, 6262 onAppSettingsClick, onDoneClick, checkSaveListener, 6263 mNonBlockablePkgs); 6264 } catch (RemoteException e) { 6265 Log.e(TAG, e.toString()); 6266 } 6267 } 6268 } 6269 6270 private void saveAndCloseNotificationMenu(NotificationInfo info, 6271 ExpandableNotificationRow row, NotificationGuts guts, View done) { 6272 guts.resetFalsingCheck(); 6273 int[] rowLocation = new int[2]; 6274 int[] doneLocation = new int[2]; 6275 row.getLocationOnScreen(rowLocation); 6276 done.getLocationOnScreen(doneLocation); 6277 6278 final int centerX = done.getWidth() / 2; 6279 final int centerY = done.getHeight() / 2; 6280 final int x = doneLocation[0] - rowLocation[0] + centerX; 6281 final int y = doneLocation[1] - rowLocation[1] + centerY; 6282 closeAndSaveGuts(false /* removeLeavebehind */, false /* force */, 6283 true /* removeControls */, x, y, true /* resetMenu */); 6284 } 6285 6286 protected SwipeHelper.LongPressListener getNotificationLongClicker() { 6287 return new SwipeHelper.LongPressListener() { 6288 @Override 6289 public boolean onLongPress(View v, final int x, final int y, 6290 MenuItem item) { 6291 if (!(v instanceof ExpandableNotificationRow)) { 6292 return false; 6293 } 6294 if (v.getWindowToken() == null) { 6295 Log.e(TAG, "Trying to show notification guts, but not attached to window"); 6296 return false; 6297 } 6298 6299 final ExpandableNotificationRow row = (ExpandableNotificationRow) v; 6300 if (row.isDark()) { 6301 return false; 6302 } 6303 if (row.areGutsExposed()) { 6304 closeAndSaveGuts(false /* removeLeavebehind */, false /* force */, 6305 true /* removeControls */, -1 /* x */, -1 /* y */, 6306 true /* resetMenu */); 6307 return false; 6308 } 6309 bindGuts(row, item); 6310 NotificationGuts guts = row.getGuts(); 6311 6312 // Assume we are a status_bar_notification_row 6313 if (guts == null) { 6314 // This view has no guts. Examples are the more card or the dismiss all view 6315 return false; 6316 } 6317 6318 mMetricsLogger.action(MetricsEvent.ACTION_NOTE_CONTROLS); 6319 6320 // ensure that it's laid but not visible until actually laid out 6321 guts.setVisibility(View.INVISIBLE); 6322 // Post to ensure the the guts are properly laid out. 6323 guts.post(new Runnable() { 6324 @Override 6325 public void run() { 6326 if (row.getWindowToken() == null) { 6327 Log.e(TAG, "Trying to show notification guts, but not attached to " 6328 + "window"); 6329 return; 6330 } 6331 closeAndSaveGuts(true /* removeLeavebehind */, true /* force */, 6332 true /* removeControls */, -1 /* x */, -1 /* y */, 6333 false /* resetMenu */); 6334 guts.setVisibility(View.VISIBLE); 6335 final double horz = Math.max(guts.getWidth() - x, x); 6336 final double vert = Math.max(guts.getHeight() - y, y); 6337 final float r = (float) Math.hypot(horz, vert); 6338 final Animator a 6339 = ViewAnimationUtils.createCircularReveal(guts, x, y, 0, r); 6340 a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD); 6341 a.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); 6342 a.addListener(new AnimatorListenerAdapter() { 6343 @Override 6344 public void onAnimationEnd(Animator animation) { 6345 super.onAnimationEnd(animation); 6346 // Move the notification view back over the menu 6347 row.resetTranslation(); 6348 } 6349 }); 6350 a.start(); 6351 final boolean needsFalsingProtection = 6352 (mState == StatusBarState.KEYGUARD && 6353 !mAccessibilityManager.isTouchExplorationEnabled()); 6354 guts.setExposed(true /* exposed */, needsFalsingProtection); 6355 row.closeRemoteInput(); 6356 mStackScroller.onHeightChanged(row, true /* needsAnimation */); 6357 mNotificationGutsExposed = guts; 6358 mGutsMenuItem = item; 6359 } 6360 }); 6361 return true; 6362 } 6363 }; 6364 } 6365 6366 /** 6367 * Returns the exposed NotificationGuts or null if none are exposed. 6368 */ 6369 public NotificationGuts getExposedGuts() { 6370 return mNotificationGutsExposed; 6371 } 6372 6373 /** 6374 * Closes guts or notification menus that might be visible and saves any changes. 6375 * 6376 * @param removeLeavebehinds true if leavebehinds (e.g. snooze) should be closed. 6377 * @param force true if guts should be closed regardless of state (used for snooze only). 6378 * @param removeControls true if controls (e.g. info) should be closed. 6379 * @param x if closed based on touch location, this is the x touch location. 6380 * @param y if closed based on touch location, this is the y touch location. 6381 * @param resetMenu if any notification menus that might be revealed should be closed. 6382 */ 6383 public void closeAndSaveGuts(boolean removeLeavebehinds, boolean force, boolean removeControls, 6384 int x, int y, boolean resetMenu) { 6385 if (mNotificationGutsExposed != null) { 6386 mNotificationGutsExposed.closeControls(removeLeavebehinds, removeControls, x, y, force); 6387 } 6388 if (resetMenu) { 6389 mStackScroller.resetExposedMenuView(false /* animate */, true /* force */); 6390 } 6391 } 6392 6393 @Override 6394 public void toggleSplitScreen() { 6395 toggleSplitScreenMode(-1 /* metricsDockAction */, -1 /* metricsUndockAction */); 6396 } 6397 6398 @Override 6399 public void preloadRecentApps() { 6400 int msg = MSG_PRELOAD_RECENT_APPS; 6401 mHandler.removeMessages(msg); 6402 mHandler.sendEmptyMessage(msg); 6403 } 6404 6405 @Override 6406 public void cancelPreloadRecentApps() { 6407 int msg = MSG_CANCEL_PRELOAD_RECENT_APPS; 6408 mHandler.removeMessages(msg); 6409 mHandler.sendEmptyMessage(msg); 6410 } 6411 6412 @Override 6413 public void dismissKeyboardShortcutsMenu() { 6414 int msg = MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU; 6415 mHandler.removeMessages(msg); 6416 mHandler.sendEmptyMessage(msg); 6417 } 6418 6419 @Override 6420 public void toggleKeyboardShortcutsMenu(int deviceId) { 6421 int msg = MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU; 6422 mHandler.removeMessages(msg); 6423 mHandler.obtainMessage(msg, deviceId, 0).sendToTarget(); 6424 } 6425 6426 protected void sendCloseSystemWindows(String reason) { 6427 try { 6428 ActivityManager.getService().closeSystemDialogs(reason); 6429 } catch (RemoteException e) { 6430 } 6431 } 6432 6433 protected void toggleKeyboardShortcuts(int deviceId) { 6434 KeyboardShortcuts.toggle(mContext, deviceId); 6435 } 6436 6437 protected void dismissKeyboardShortcuts() { 6438 KeyboardShortcuts.dismiss(); 6439 } 6440 6441 /** 6442 * Save the current "public" (locked and secure) state of the lockscreen. 6443 */ 6444 public void setLockscreenPublicMode(boolean publicMode, int userId) { 6445 mLockscreenPublicMode.put(userId, publicMode); 6446 } 6447 6448 public boolean isLockscreenPublicMode(int userId) { 6449 if (userId == UserHandle.USER_ALL) { 6450 return mLockscreenPublicMode.get(mCurrentUserId, false); 6451 } 6452 return mLockscreenPublicMode.get(userId, false); 6453 } 6454 6455 /** 6456 * Has the given user chosen to allow notifications to be shown even when the lockscreen is in 6457 * "public" (secure & locked) mode? 6458 */ 6459 public boolean userAllowsNotificationsInPublic(int userHandle) { 6460 if (userHandle == UserHandle.USER_ALL) { 6461 return true; 6462 } 6463 6464 if (mUsersAllowingNotifications.indexOfKey(userHandle) < 0) { 6465 final boolean allowed = 0 != Settings.Secure.getIntForUser( 6466 mContext.getContentResolver(), 6467 Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, userHandle); 6468 mUsersAllowingNotifications.append(userHandle, allowed); 6469 return allowed; 6470 } 6471 6472 return mUsersAllowingNotifications.get(userHandle); 6473 } 6474 6475 /** 6476 * Has the given user chosen to allow their private (full) notifications to be shown even 6477 * when the lockscreen is in "public" (secure & locked) mode? 6478 */ 6479 public boolean userAllowsPrivateNotificationsInPublic(int userHandle) { 6480 if (userHandle == UserHandle.USER_ALL) { 6481 return true; 6482 } 6483 6484 if (mUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) { 6485 final boolean allowedByUser = 0 != Settings.Secure.getIntForUser( 6486 mContext.getContentResolver(), 6487 Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle); 6488 final boolean allowedByDpm = adminAllowsUnredactedNotifications(userHandle); 6489 final boolean allowed = allowedByUser && allowedByDpm; 6490 mUsersAllowingPrivateNotifications.append(userHandle, allowed); 6491 return allowed; 6492 } 6493 6494 return mUsersAllowingPrivateNotifications.get(userHandle); 6495 } 6496 6497 private boolean adminAllowsUnredactedNotifications(int userHandle) { 6498 if (userHandle == UserHandle.USER_ALL) { 6499 return true; 6500 } 6501 final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(null /* admin */, 6502 userHandle); 6503 return (dpmFlags & DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS) == 0; 6504 } 6505 6506 /** 6507 * Returns true if we're on a secure lockscreen and the user wants to hide notification data. 6508 * If so, notifications should be hidden. 6509 */ 6510 @Override // NotificationData.Environment 6511 public boolean shouldHideNotifications(int userId) { 6512 return isLockscreenPublicMode(userId) && !userAllowsNotificationsInPublic(userId) 6513 || (userId != mCurrentUserId && shouldHideNotifications(mCurrentUserId)); 6514 } 6515 6516 /** 6517 * Returns true if we're on a secure lockscreen and the user wants to hide notifications via 6518 * package-specific override. 6519 */ 6520 @Override // NotificationDate.Environment 6521 public boolean shouldHideNotifications(String key) { 6522 return isLockscreenPublicMode(mCurrentUserId) 6523 && mNotificationData.getVisibilityOverride(key) == Notification.VISIBILITY_SECRET; 6524 } 6525 6526 /** 6527 * Returns true if we're on a secure lockscreen. 6528 */ 6529 @Override // NotificationData.Environment 6530 public boolean isSecurelyLocked(int userId) { 6531 return isLockscreenPublicMode(userId); 6532 } 6533 6534 public void onNotificationClear(StatusBarNotification notification) { 6535 try { 6536 mBarService.onNotificationClear( 6537 notification.getPackageName(), 6538 notification.getTag(), 6539 notification.getId(), 6540 notification.getUserId()); 6541 } catch (android.os.RemoteException ex) { 6542 // oh well 6543 } 6544 } 6545 6546 /** 6547 * Called when the notification panel layouts 6548 */ 6549 public void onPanelLaidOut() { 6550 updateKeyguardMaxNotifications(); 6551 } 6552 6553 public void updateKeyguardMaxNotifications() { 6554 if (mState == StatusBarState.KEYGUARD) { 6555 // Since the number of notifications is determined based on the height of the view, we 6556 // need to update them. 6557 int maxBefore = getMaxKeyguardNotifications(false /* recompute */); 6558 int maxNotifications = getMaxKeyguardNotifications(true /* recompute */); 6559 if (maxBefore != maxNotifications) { 6560 updateRowStates(); 6561 } 6562 } 6563 } 6564 6565 protected void inflateViews(Entry entry, ViewGroup parent) { 6566 PackageManager pmUser = getPackageManagerForUser(mContext, 6567 entry.notification.getUser().getIdentifier()); 6568 6569 final StatusBarNotification sbn = entry.notification; 6570 if (entry.row != null) { 6571 entry.reset(); 6572 updateNotification(entry, pmUser, sbn, entry.row); 6573 } else { 6574 new RowInflaterTask().inflate(mContext, parent, entry, 6575 row -> { 6576 bindRow(entry, pmUser, sbn, row); 6577 updateNotification(entry, pmUser, sbn, row); 6578 }); 6579 } 6580 6581 } 6582 6583 private void bindRow(Entry entry, PackageManager pmUser, 6584 StatusBarNotification sbn, ExpandableNotificationRow row) { 6585 row.setExpansionLogger(this, entry.notification.getKey()); 6586 row.setGroupManager(mGroupManager); 6587 row.setHeadsUpManager(mHeadsUpManager); 6588 row.setAboveShelfChangedListener(mAboveShelfObserver); 6589 row.setRemoteInputController(mRemoteInputController); 6590 row.setOnExpandClickListener(this); 6591 row.setRemoteViewClickHandler(mOnClickHandler); 6592 row.setInflationCallback(this); 6593 6594 // Get the app name. 6595 // Note that Notification.Builder#bindHeaderAppName has similar logic 6596 // but since this field is used in the guts, it must be accurate. 6597 // Therefore we will only show the application label, or, failing that, the 6598 // package name. No substitutions. 6599 final String pkg = sbn.getPackageName(); 6600 String appname = pkg; 6601 try { 6602 final ApplicationInfo info = pmUser.getApplicationInfo(pkg, 6603 PackageManager.MATCH_UNINSTALLED_PACKAGES 6604 | PackageManager.MATCH_DISABLED_COMPONENTS); 6605 if (info != null) { 6606 appname = String.valueOf(pmUser.getApplicationLabel(info)); 6607 } 6608 } catch (NameNotFoundException e) { 6609 // Do nothing 6610 } 6611 row.setAppName(appname); 6612 row.setOnDismissRunnable(() -> 6613 performRemoveNotification(row.getStatusBarNotification())); 6614 row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); 6615 if (ENABLE_REMOTE_INPUT) { 6616 row.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS); 6617 } 6618 } 6619 6620 private void updateNotification(Entry entry, PackageManager pmUser, 6621 StatusBarNotification sbn, ExpandableNotificationRow row) { 6622 row.setNeedsRedaction(needsRedaction(entry)); 6623 boolean isLowPriority = mNotificationData.isAmbient(sbn.getKey()); 6624 boolean isUpdate = mNotificationData.get(entry.key) != null; 6625 boolean wasLowPriority = row.isLowPriority(); 6626 row.setIsLowPriority(isLowPriority); 6627 row.setLowPriorityStateUpdated(isUpdate && (wasLowPriority != isLowPriority)); 6628 // bind the click event to the content area 6629 mNotificationClicker.register(row, sbn); 6630 6631 // Extract target SDK version. 6632 try { 6633 ApplicationInfo info = pmUser.getApplicationInfo(sbn.getPackageName(), 0); 6634 entry.targetSdk = info.targetSdkVersion; 6635 } catch (NameNotFoundException ex) { 6636 Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex); 6637 } 6638 row.setLegacy(entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD 6639 && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP); 6640 entry.setIconTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP); 6641 entry.autoRedacted = entry.notification.getNotification().publicVersion == null; 6642 6643 entry.row = row; 6644 entry.row.setOnActivatedListener(this); 6645 6646 boolean useIncreasedCollapsedHeight = mMessagingUtil.isImportantMessaging(sbn, 6647 mNotificationData.getImportance(sbn.getKey())); 6648 boolean useIncreasedHeadsUp = useIncreasedCollapsedHeight && mPanelExpanded; 6649 row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight); 6650 row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp); 6651 row.updateNotification(entry); 6652 } 6653 6654 /** 6655 * Adds RemoteInput actions from the WearableExtender; to be removed once more apps support this 6656 * via first-class API. 6657 * 6658 * TODO: Remove once enough apps specify remote inputs on their own. 6659 */ 6660 private void processForRemoteInput(Notification n) { 6661 if (!ENABLE_REMOTE_INPUT) return; 6662 6663 if (n.extras != null && n.extras.containsKey("android.wearable.EXTENSIONS") && 6664 (n.actions == null || n.actions.length == 0)) { 6665 Notification.Action viableAction = null; 6666 Notification.WearableExtender we = new Notification.WearableExtender(n); 6667 6668 List<Notification.Action> actions = we.getActions(); 6669 final int numActions = actions.size(); 6670 6671 for (int i = 0; i < numActions; i++) { 6672 Notification.Action action = actions.get(i); 6673 if (action == null) { 6674 continue; 6675 } 6676 RemoteInput[] remoteInputs = action.getRemoteInputs(); 6677 if (remoteInputs == null) { 6678 continue; 6679 } 6680 for (RemoteInput ri : remoteInputs) { 6681 if (ri.getAllowFreeFormInput()) { 6682 viableAction = action; 6683 break; 6684 } 6685 } 6686 if (viableAction != null) { 6687 break; 6688 } 6689 } 6690 6691 if (viableAction != null) { 6692 Notification.Builder rebuilder = Notification.Builder.recoverBuilder(mContext, n); 6693 rebuilder.setActions(viableAction); 6694 rebuilder.build(); // will rewrite n 6695 } 6696 } 6697 } 6698 6699 public void startPendingIntentDismissingKeyguard(final PendingIntent intent) { 6700 if (!isDeviceProvisioned()) return; 6701 6702 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing(); 6703 final boolean afterKeyguardGone = intent.isActivity() 6704 && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(), 6705 mCurrentUserId); 6706 dismissKeyguardThenExecute(new OnDismissAction() { 6707 @Override 6708 public boolean onDismiss() { 6709 new Thread() { 6710 @Override 6711 public void run() { 6712 try { 6713 // The intent we are sending is for the application, which 6714 // won't have permission to immediately start an activity after 6715 // the user switches to home. We know it is safe to do at this 6716 // point, so make sure new activity switches are now allowed. 6717 ActivityManager.getService().resumeAppSwitches(); 6718 } catch (RemoteException e) { 6719 } 6720 try { 6721 intent.send(null, 0, null, null, null, null, getActivityOptions()); 6722 } catch (PendingIntent.CanceledException e) { 6723 // the stack trace isn't very helpful here. 6724 // Just log the exception message. 6725 Log.w(TAG, "Sending intent failed: " + e); 6726 6727 // TODO: Dismiss Keyguard. 6728 } 6729 if (intent.isActivity()) { 6730 mAssistManager.hideAssist(); 6731 } 6732 } 6733 }.start(); 6734 6735 // close the shade if it was open 6736 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, 6737 true /* force */, true /* delayed */); 6738 visibilityChanged(false); 6739 6740 return true; 6741 } 6742 }, afterKeyguardGone); 6743 } 6744 6745 6746 private final class NotificationClicker implements View.OnClickListener { 6747 6748 @Override 6749 public void onClick(final View v) { 6750 if (!(v instanceof ExpandableNotificationRow)) { 6751 Log.e(TAG, "NotificationClicker called on a view that is not a notification row."); 6752 return; 6753 } 6754 6755 wakeUpIfDozing(SystemClock.uptimeMillis(), v); 6756 6757 final ExpandableNotificationRow row = (ExpandableNotificationRow) v; 6758 final StatusBarNotification sbn = row.getStatusBarNotification(); 6759 if (sbn == null) { 6760 Log.e(TAG, "NotificationClicker called on an unclickable notification,"); 6761 return; 6762 } 6763 6764 // Check if the notification is displaying the menu, if so slide notification back 6765 if (row.getProvider() != null && row.getProvider().isMenuVisible()) { 6766 row.animateTranslateNotification(0); 6767 return; 6768 } 6769 6770 Notification notification = sbn.getNotification(); 6771 final PendingIntent intent = notification.contentIntent != null 6772 ? notification.contentIntent 6773 : notification.fullScreenIntent; 6774 final String notificationKey = sbn.getKey(); 6775 6776 // Mark notification for one frame. 6777 row.setJustClicked(true); 6778 DejankUtils.postAfterTraversal(new Runnable() { 6779 @Override 6780 public void run() { 6781 row.setJustClicked(false); 6782 } 6783 }); 6784 6785 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing(); 6786 final boolean afterKeyguardGone = intent.isActivity() 6787 && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(), 6788 mCurrentUserId); 6789 dismissKeyguardThenExecute(new OnDismissAction() { 6790 @Override 6791 public boolean onDismiss() { 6792 if (mHeadsUpManager != null && mHeadsUpManager.isHeadsUp(notificationKey)) { 6793 // Release the HUN notification to the shade. 6794 6795 if (isPanelFullyCollapsed()) { 6796 HeadsUpManager.setIsClickedNotification(row, true); 6797 } 6798 // 6799 // In most cases, when FLAG_AUTO_CANCEL is set, the notification will 6800 // become canceled shortly by NoMan, but we can't assume that. 6801 mHeadsUpManager.releaseImmediately(notificationKey); 6802 } 6803 StatusBarNotification parentToCancel = null; 6804 if (shouldAutoCancel(sbn) && mGroupManager.isOnlyChildInGroup(sbn)) { 6805 StatusBarNotification summarySbn = mGroupManager.getLogicalGroupSummary(sbn) 6806 .getStatusBarNotification(); 6807 if (shouldAutoCancel(summarySbn)) { 6808 parentToCancel = summarySbn; 6809 } 6810 } 6811 final StatusBarNotification parentToCancelFinal = parentToCancel; 6812 new Thread() { 6813 @Override 6814 public void run() { 6815 try { 6816 // The intent we are sending is for the application, which 6817 // won't have permission to immediately start an activity after 6818 // the user switches to home. We know it is safe to do at this 6819 // point, so make sure new activity switches are now allowed. 6820 ActivityManager.getService().resumeAppSwitches(); 6821 } catch (RemoteException e) { 6822 } 6823 if (intent != null) { 6824 // If we are launching a work activity and require to launch 6825 // separate work challenge, we defer the activity action and cancel 6826 // notification until work challenge is unlocked. 6827 if (intent.isActivity()) { 6828 final int userId = intent.getCreatorUserHandle() 6829 .getIdentifier(); 6830 if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId) 6831 && mKeyguardManager.isDeviceLocked(userId)) { 6832 // TODO(b/28935539): should allow certain activities to 6833 // bypass work challenge 6834 if (startWorkChallengeIfNecessary(userId, 6835 intent.getIntentSender(), notificationKey)) { 6836 // Show work challenge, do not run PendingIntent and 6837 // remove notification 6838 return; 6839 } 6840 } 6841 } 6842 try { 6843 intent.send(null, 0, null, null, null, null, 6844 getActivityOptions()); 6845 } catch (PendingIntent.CanceledException e) { 6846 // the stack trace isn't very helpful here. 6847 // Just log the exception message. 6848 Log.w(TAG, "Sending contentIntent failed: " + e); 6849 6850 // TODO: Dismiss Keyguard. 6851 } 6852 if (intent.isActivity()) { 6853 mAssistManager.hideAssist(); 6854 } 6855 } 6856 6857 try { 6858 mBarService.onNotificationClick(notificationKey); 6859 } catch (RemoteException ex) { 6860 // system process is dead if we're here. 6861 } 6862 if (parentToCancelFinal != null) { 6863 // We have to post it to the UI thread for synchronization 6864 mHandler.post(new Runnable() { 6865 @Override 6866 public void run() { 6867 Runnable removeRunnable = new Runnable() { 6868 @Override 6869 public void run() { 6870 performRemoveNotification(parentToCancelFinal); 6871 } 6872 }; 6873 if (isCollapsing()) { 6874 // To avoid lags we're only performing the remove 6875 // after the shade was collapsed 6876 addPostCollapseAction(removeRunnable); 6877 } else { 6878 removeRunnable.run(); 6879 } 6880 } 6881 }); 6882 } 6883 } 6884 }.start(); 6885 6886 // close the shade if it was open 6887 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, 6888 true /* force */, true /* delayed */); 6889 visibilityChanged(false); 6890 6891 return true; 6892 } 6893 }, afterKeyguardGone); 6894 } 6895 6896 private boolean shouldAutoCancel(StatusBarNotification sbn) { 6897 int flags = sbn.getNotification().flags; 6898 if ((flags & Notification.FLAG_AUTO_CANCEL) != Notification.FLAG_AUTO_CANCEL) { 6899 return false; 6900 } 6901 if ((flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) { 6902 return false; 6903 } 6904 return true; 6905 } 6906 6907 public void register(ExpandableNotificationRow row, StatusBarNotification sbn) { 6908 Notification notification = sbn.getNotification(); 6909 if (notification.contentIntent != null || notification.fullScreenIntent != null) { 6910 row.setOnClickListener(this); 6911 } else { 6912 row.setOnClickListener(null); 6913 } 6914 } 6915 } 6916 6917 protected Bundle getActivityOptions() { 6918 // Anything launched from the notification shade should always go into the 6919 // fullscreen stack. 6920 ActivityOptions options = ActivityOptions.makeBasic(); 6921 options.setLaunchStackId(StackId.FULLSCREEN_WORKSPACE_STACK_ID); 6922 return options.toBundle(); 6923 } 6924 6925 protected void visibilityChanged(boolean visible) { 6926 if (mVisible != visible) { 6927 mVisible = visible; 6928 if (!visible) { 6929 closeAndSaveGuts(true /* removeLeavebehind */, true /* force */, 6930 true /* removeControls */, -1 /* x */, -1 /* y */, true /* resetMenu */); 6931 } 6932 } 6933 updateVisibleToUser(); 6934 } 6935 6936 protected void updateVisibleToUser() { 6937 boolean oldVisibleToUser = mVisibleToUser; 6938 mVisibleToUser = mVisible && mDeviceInteractive; 6939 6940 if (oldVisibleToUser != mVisibleToUser) { 6941 handleVisibleToUserChanged(mVisibleToUser); 6942 } 6943 } 6944 6945 /** 6946 * Clear Buzz/Beep/Blink. 6947 */ 6948 public void clearNotificationEffects() { 6949 try { 6950 mBarService.clearNotificationEffects(); 6951 } catch (RemoteException e) { 6952 // Won't fail unless the world has ended. 6953 } 6954 } 6955 6956 /** 6957 * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService 6958 * about the failure. 6959 * 6960 * WARNING: this will call back into us. Don't hold any locks. 6961 */ 6962 void handleNotificationError(StatusBarNotification n, String message) { 6963 removeNotification(n.getKey(), null); 6964 try { 6965 mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(), 6966 n.getInitialPid(), message, n.getUserId()); 6967 } catch (RemoteException ex) { 6968 // The end is nigh. 6969 } 6970 } 6971 6972 protected StatusBarNotification removeNotificationViews(String key, RankingMap ranking) { 6973 NotificationData.Entry entry = mNotificationData.remove(key, ranking); 6974 if (entry == null) { 6975 Log.w(TAG, "removeNotification for unknown key: " + key); 6976 return null; 6977 } 6978 updateNotifications(); 6979 Dependency.get(LeakDetector.class).trackGarbage(entry); 6980 return entry.notification; 6981 } 6982 6983 protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn) 6984 throws InflationException { 6985 if (DEBUG) { 6986 Log.d(TAG, "createNotificationViews(notification=" + sbn); 6987 } 6988 NotificationData.Entry entry = new NotificationData.Entry(sbn); 6989 Dependency.get(LeakDetector.class).trackInstance(entry); 6990 entry.createIcons(mContext, sbn); 6991 // Construct the expanded view. 6992 inflateViews(entry, mStackScroller); 6993 return entry; 6994 } 6995 6996 protected void addNotificationViews(Entry entry) { 6997 if (entry == null) { 6998 return; 6999 } 7000 // Add the expanded view and icon. 7001 mNotificationData.add(entry); 7002 updateNotifications(); 7003 } 7004 7005 /** 7006 * Updates expanded, dimmed and locked states of notification rows. 7007 */ 7008 protected void updateRowStates() { 7009 final int N = mStackScroller.getChildCount(); 7010 7011 int visibleNotifications = 0; 7012 boolean onKeyguard = mState == StatusBarState.KEYGUARD; 7013 int maxNotifications = -1; 7014 if (onKeyguard) { 7015 maxNotifications = getMaxKeyguardNotifications(true /* recompute */); 7016 } 7017 mStackScroller.setMaxDisplayedNotifications(maxNotifications); 7018 Stack<ExpandableNotificationRow> stack = new Stack<>(); 7019 for (int i = N - 1; i >= 0; i--) { 7020 View child = mStackScroller.getChildAt(i); 7021 if (!(child instanceof ExpandableNotificationRow)) { 7022 continue; 7023 } 7024 stack.push((ExpandableNotificationRow) child); 7025 } 7026 while(!stack.isEmpty()) { 7027 ExpandableNotificationRow row = stack.pop(); 7028 NotificationData.Entry entry = row.getEntry(); 7029 boolean isChildNotification = 7030 mGroupManager.isChildInGroupWithSummary(entry.notification); 7031 7032 row.setOnKeyguard(onKeyguard); 7033 7034 if (!onKeyguard) { 7035 // If mAlwaysExpandNonGroupedNotification is false, then only expand the 7036 // very first notification and if it's not a child of grouped notifications. 7037 row.setSystemExpanded(mAlwaysExpandNonGroupedNotification 7038 || (visibleNotifications == 0 && !isChildNotification)); 7039 } 7040 7041 entry.row.setShowAmbient(isDozing()); 7042 int userId = entry.notification.getUserId(); 7043 boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup( 7044 entry.notification) && !entry.row.isRemoved(); 7045 boolean showOnKeyguard = shouldShowOnKeyguard(entry.notification); 7046 if (suppressedSummary 7047 || (isLockscreenPublicMode(userId) && !mShowLockscreenNotifications) 7048 || (onKeyguard && !showOnKeyguard)) { 7049 entry.row.setVisibility(View.GONE); 7050 } else { 7051 boolean wasGone = entry.row.getVisibility() == View.GONE; 7052 if (wasGone) { 7053 entry.row.setVisibility(View.VISIBLE); 7054 } 7055 if (!isChildNotification && !entry.row.isRemoved()) { 7056 if (wasGone) { 7057 // notify the scroller of a child addition 7058 mStackScroller.generateAddAnimation(entry.row, 7059 !showOnKeyguard /* fromMoreCard */); 7060 } 7061 visibleNotifications++; 7062 } 7063 } 7064 if (row.isSummaryWithChildren()) { 7065 List<ExpandableNotificationRow> notificationChildren = 7066 row.getNotificationChildren(); 7067 int size = notificationChildren.size(); 7068 for (int i = size - 1; i >= 0; i--) { 7069 stack.push(notificationChildren.get(i)); 7070 } 7071 } 7072 } 7073 mNotificationPanel.setNoVisibleNotifications(visibleNotifications == 0); 7074 7075 // The following views will be moved to the end of mStackScroller. This counter represents 7076 // the offset from the last child. Initialized to 1 for the very last position. It is post- 7077 // incremented in the following "changeViewPosition" calls so that its value is correct for 7078 // subsequent calls. 7079 int offsetFromEnd = 1; 7080 if (mDismissView != null) { 7081 mStackScroller.changeViewPosition(mDismissView, 7082 mStackScroller.getChildCount() - offsetFromEnd++); 7083 } 7084 7085 mStackScroller.changeViewPosition(mEmptyShadeView, 7086 mStackScroller.getChildCount() - offsetFromEnd++); 7087 7088 // No post-increment for this call because it is the last one. Make sure to add one if 7089 // another "changeViewPosition" call is ever added. 7090 mStackScroller.changeViewPosition(mNotificationShelf, 7091 mStackScroller.getChildCount() - offsetFromEnd); 7092 7093 // Scrim opacity varies based on notification count 7094 mScrimController.setNotificationCount(mStackScroller.getNotGoneChildCount()); 7095 } 7096 7097 public boolean shouldShowOnKeyguard(StatusBarNotification sbn) { 7098 return mShowLockscreenNotifications && !mNotificationData.isAmbient(sbn.getKey()); 7099 } 7100 7101 // extended in StatusBar 7102 protected void setShowLockscreenNotifications(boolean show) { 7103 mShowLockscreenNotifications = show; 7104 } 7105 7106 protected void setLockScreenAllowRemoteInput(boolean allowLockscreenRemoteInput) { 7107 mAllowLockscreenRemoteInput = allowLockscreenRemoteInput; 7108 } 7109 7110 private void updateLockscreenNotificationSetting() { 7111 final boolean show = Settings.Secure.getIntForUser(mContext.getContentResolver(), 7112 Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 7113 1, 7114 mCurrentUserId) != 0; 7115 final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures( 7116 null /* admin */, mCurrentUserId); 7117 final boolean allowedByDpm = (dpmFlags 7118 & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0; 7119 7120 setShowLockscreenNotifications(show && allowedByDpm); 7121 7122 if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) { 7123 final boolean remoteInput = Settings.Secure.getIntForUser(mContext.getContentResolver(), 7124 Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT, 7125 0, 7126 mCurrentUserId) != 0; 7127 final boolean remoteInputDpm = 7128 (dpmFlags & DevicePolicyManager.KEYGUARD_DISABLE_REMOTE_INPUT) == 0; 7129 7130 setLockScreenAllowRemoteInput(remoteInput && remoteInputDpm); 7131 } else { 7132 setLockScreenAllowRemoteInput(false); 7133 } 7134 } 7135 7136 public void updateNotification(StatusBarNotification notification, RankingMap ranking) 7137 throws InflationException { 7138 if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")"); 7139 7140 final String key = notification.getKey(); 7141 abortExistingInflation(key); 7142 Entry entry = mNotificationData.get(key); 7143 if (entry == null) { 7144 return; 7145 } 7146 mHeadsUpEntriesToRemoveOnSwitch.remove(entry); 7147 mRemoteInputEntriesToRemoveOnCollapse.remove(entry); 7148 if (key.equals(mKeyToRemoveOnGutsClosed)) { 7149 mKeyToRemoveOnGutsClosed = null; 7150 Log.w(TAG, "Notification that was kept for guts was updated. " + key); 7151 } 7152 7153 Notification n = notification.getNotification(); 7154 mNotificationData.updateRanking(ranking); 7155 7156 final StatusBarNotification oldNotification = entry.notification; 7157 entry.notification = notification; 7158 mGroupManager.onEntryUpdated(entry, oldNotification); 7159 7160 entry.updateIcons(mContext, notification); 7161 inflateViews(entry, mStackScroller); 7162 7163 mForegroundServiceController.updateNotification(notification, 7164 mNotificationData.getImportance(key)); 7165 7166 boolean shouldPeek = shouldPeek(entry, notification); 7167 boolean alertAgain = alertAgain(entry, n); 7168 7169 updateHeadsUp(key, entry, shouldPeek, alertAgain); 7170 updateNotifications(); 7171 7172 if (!notification.isClearable()) { 7173 // The user may have performed a dismiss action on the notification, since it's 7174 // not clearable we should snap it back. 7175 mStackScroller.snapViewIfNeeded(entry.row); 7176 } 7177 7178 if (DEBUG) { 7179 // Is this for you? 7180 boolean isForCurrentUser = isNotificationForCurrentProfiles(notification); 7181 Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you"); 7182 } 7183 7184 setAreThereNotifications(); 7185 } 7186 7187 protected void updatePublicContentView(Entry entry, 7188 StatusBarNotification sbn) { 7189 final RemoteViews publicContentView = entry.cachedPublicContentView; 7190 View inflatedView = entry.getPublicContentView(); 7191 if (entry.autoRedacted && publicContentView != null && inflatedView != null) { 7192 final boolean disabledByPolicy = 7193 !adminAllowsUnredactedNotifications(entry.notification.getUserId()); 7194 String notificationHiddenText = mContext.getString(disabledByPolicy 7195 ? com.android.internal.R.string.notification_hidden_by_policy_text 7196 : com.android.internal.R.string.notification_hidden_text); 7197 TextView titleView = (TextView) inflatedView.findViewById(android.R.id.title); 7198 if (titleView != null 7199 && !titleView.getText().toString().equals(notificationHiddenText)) { 7200 titleView.setText(notificationHiddenText); 7201 } 7202 } 7203 } 7204 7205 protected void notifyHeadsUpScreenOff() { 7206 maybeEscalateHeadsUp(); 7207 } 7208 7209 private boolean alertAgain(Entry oldEntry, Notification newNotification) { 7210 return oldEntry == null || !oldEntry.hasInterrupted() 7211 || (newNotification.flags & Notification.FLAG_ONLY_ALERT_ONCE) == 0; 7212 } 7213 7214 protected boolean shouldPeek(Entry entry) { 7215 return shouldPeek(entry, entry.notification); 7216 } 7217 7218 protected boolean shouldPeek(Entry entry, StatusBarNotification sbn) { 7219 if (!mUseHeadsUp || isDeviceInVrMode()) { 7220 if (DEBUG) Log.d(TAG, "No peeking: no huns or vr mode"); 7221 return false; 7222 } 7223 7224 if (mNotificationData.shouldFilterOut(sbn)) { 7225 if (DEBUG) Log.d(TAG, "No peeking: filtered notification: " + sbn.getKey()); 7226 return false; 7227 } 7228 7229 boolean inUse = mPowerManager.isScreenOn() && !mSystemServicesProxy.isDreaming(); 7230 7231 if (!inUse && !isDozing()) { 7232 if (DEBUG) { 7233 Log.d(TAG, "No peeking: not in use: " + sbn.getKey()); 7234 } 7235 return false; 7236 } 7237 7238 if (!isDozing() && mNotificationData.shouldSuppressScreenOn(sbn.getKey())) { 7239 if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey()); 7240 return false; 7241 } 7242 7243 if (isDozing() && mNotificationData.shouldSuppressScreenOff(sbn.getKey())) { 7244 if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey()); 7245 return false; 7246 } 7247 7248 if (entry.hasJustLaunchedFullScreenIntent()) { 7249 if (DEBUG) Log.d(TAG, "No peeking: recent fullscreen: " + sbn.getKey()); 7250 return false; 7251 } 7252 7253 if (isSnoozedPackage(sbn)) { 7254 if (DEBUG) Log.d(TAG, "No peeking: snoozed package: " + sbn.getKey()); 7255 return false; 7256 } 7257 7258 // Allow peeking for DEFAULT notifications only if we're on Ambient Display. 7259 int importanceLevel = isDozing() ? NotificationManager.IMPORTANCE_DEFAULT 7260 : NotificationManager.IMPORTANCE_HIGH; 7261 if (mNotificationData.getImportance(sbn.getKey()) < importanceLevel) { 7262 if (DEBUG) Log.d(TAG, "No peeking: unimportant notification: " + sbn.getKey()); 7263 return false; 7264 } 7265 7266 if (sbn.getNotification().fullScreenIntent != null) { 7267 if (mAccessibilityManager.isTouchExplorationEnabled()) { 7268 if (DEBUG) Log.d(TAG, "No peeking: accessible fullscreen: " + sbn.getKey()); 7269 return false; 7270 } else if (mDozing) { 7271 // We never want heads up when we are dozing. 7272 return false; 7273 } else { 7274 // we only allow head-up on the lockscreen if it doesn't have a fullscreen intent 7275 return !mStatusBarKeyguardViewManager.isShowing() 7276 || mStatusBarKeyguardViewManager.isOccluded(); 7277 } 7278 } 7279 7280 // Don't peek notifications that are suppressed due to group alert behavior 7281 if (sbn.isGroup() && sbn.getNotification().suppressAlertingDueToGrouping()) { 7282 if (DEBUG) Log.d(TAG, "No peeking: suppressed due to group alert behavior"); 7283 return false; 7284 } 7285 7286 return true; 7287 } 7288 7289 /** 7290 * @return Whether the security bouncer from Keyguard is showing. 7291 */ 7292 public boolean isBouncerShowing() { 7293 return mBouncerShowing; 7294 } 7295 7296 /** 7297 * @return a PackageManger for userId or if userId is < 0 (USER_ALL etc) then 7298 * return PackageManager for mContext 7299 */ 7300 public static PackageManager getPackageManagerForUser(Context context, int userId) { 7301 Context contextForUser = context; 7302 // UserHandle defines special userId as negative values, e.g. USER_ALL 7303 if (userId >= 0) { 7304 try { 7305 // Create a context for the correct user so if a package isn't installed 7306 // for user 0 we can still load information about the package. 7307 contextForUser = 7308 context.createPackageContextAsUser(context.getPackageName(), 7309 Context.CONTEXT_RESTRICTED, 7310 new UserHandle(userId)); 7311 } catch (NameNotFoundException e) { 7312 // Shouldn't fail to find the package name for system ui. 7313 } 7314 } 7315 return contextForUser.getPackageManager(); 7316 } 7317 7318 @Override 7319 public void logNotificationExpansion(String key, boolean userAction, boolean expanded) { 7320 mUiOffloadThread.submit(() -> { 7321 try { 7322 mBarService.onNotificationExpansionChanged(key, userAction, expanded); 7323 } catch (RemoteException e) { 7324 // Ignore. 7325 } 7326 }); 7327 } 7328 7329 public boolean isKeyguardSecure() { 7330 if (mStatusBarKeyguardViewManager == null) { 7331 // startKeyguard() hasn't been called yet, so we don't know. 7332 // Make sure anything that needs to know isKeyguardSecure() checks and re-checks this 7333 // value onVisibilityChanged(). 7334 Slog.w(TAG, "isKeyguardSecure() called before startKeyguard(), returning false", 7335 new Throwable()); 7336 return false; 7337 } 7338 return mStatusBarKeyguardViewManager.isSecure(); 7339 } 7340 7341 @Override 7342 public void showAssistDisclosure() { 7343 if (mAssistManager != null) { 7344 mAssistManager.showDisclosure(); 7345 } 7346 } 7347 7348 public NotificationPanelView getPanel() { 7349 return mNotificationPanel; 7350 } 7351 7352 @Override 7353 public void startAssist(Bundle args) { 7354 if (mAssistManager != null) { 7355 mAssistManager.startAssist(args); 7356 } 7357 } 7358 // End Extra BaseStatusBarMethods. 7359} 7360