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