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