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