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