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