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