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