1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.statusbar; 18 19 import android.animation.Animator; 20 import android.animation.AnimatorListenerAdapter; 21 import android.app.ActivityManager; 22 import android.app.ActivityManager.StackId; 23 import android.app.ActivityManagerNative; 24 import android.app.ActivityOptions; 25 import android.app.KeyguardManager; 26 import android.app.Notification; 27 import android.app.NotificationManager; 28 import android.app.PendingIntent; 29 import android.app.RemoteInput; 30 import android.app.TaskStackBuilder; 31 import android.app.admin.DevicePolicyManager; 32 import android.content.BroadcastReceiver; 33 import android.content.ComponentName; 34 import android.content.Context; 35 import android.content.Intent; 36 import android.content.IntentFilter; 37 import android.content.IntentSender; 38 import android.content.pm.ApplicationInfo; 39 import android.content.pm.PackageManager; 40 import android.content.pm.PackageManager.NameNotFoundException; 41 import android.content.pm.UserInfo; 42 import android.content.res.Configuration; 43 import android.database.ContentObserver; 44 import android.graphics.Rect; 45 import android.graphics.drawable.Drawable; 46 import android.graphics.drawable.Icon; 47 import android.os.AsyncTask; 48 import android.os.Build; 49 import android.os.Bundle; 50 import android.os.Handler; 51 import android.os.IBinder; 52 import android.os.Message; 53 import android.os.PowerManager; 54 import android.os.RemoteException; 55 import android.os.ServiceManager; 56 import android.os.SystemProperties; 57 import android.os.UserHandle; 58 import android.os.UserManager; 59 import android.provider.Settings; 60 import android.service.dreams.DreamService; 61 import android.service.dreams.IDreamManager; 62 import android.service.notification.NotificationListenerService; 63 import android.service.notification.NotificationListenerService.RankingMap; 64 import android.service.notification.StatusBarNotification; 65 import android.service.vr.IVrManager; 66 import android.service.vr.IVrStateCallbacks; 67 import android.text.TextUtils; 68 import android.util.ArraySet; 69 import android.util.Log; 70 import android.util.Slog; 71 import android.util.SparseArray; 72 import android.util.SparseBooleanArray; 73 import android.view.Display; 74 import android.view.IWindowManager; 75 import android.view.LayoutInflater; 76 import android.view.MotionEvent; 77 import android.view.View; 78 import android.view.ViewAnimationUtils; 79 import android.view.ViewGroup; 80 import android.view.ViewParent; 81 import android.view.WindowManager; 82 import android.view.WindowManagerGlobal; 83 import android.view.accessibility.AccessibilityManager; 84 import android.widget.ImageView; 85 import android.widget.RemoteViews; 86 import android.widget.TextView; 87 import android.widget.Toast; 88 89 import com.android.internal.logging.MetricsLogger; 90 import com.android.internal.logging.MetricsProto.MetricsEvent; 91 import com.android.internal.messages.SystemMessageProto.SystemMessage; 92 import com.android.internal.statusbar.IStatusBarService; 93 import com.android.internal.statusbar.StatusBarIcon; 94 import com.android.internal.widget.LockPatternUtils; 95 import com.android.keyguard.KeyguardHostView.OnDismissAction; 96 import com.android.keyguard.KeyguardUpdateMonitor; 97 import com.android.systemui.DejankUtils; 98 import com.android.systemui.Interpolators; 99 import com.android.systemui.R; 100 import com.android.systemui.RecentsComponent; 101 import com.android.systemui.SwipeHelper; 102 import com.android.systemui.SystemUI; 103 import com.android.systemui.assist.AssistManager; 104 import com.android.systemui.recents.Recents; 105 import com.android.systemui.statusbar.NotificationData.Entry; 106 import com.android.systemui.statusbar.NotificationGuts.OnGutsClosedListener; 107 import com.android.systemui.statusbar.notification.VisualStabilityManager; 108 import com.android.systemui.statusbar.phone.NavigationBarView; 109 import com.android.systemui.statusbar.phone.NotificationGroupManager; 110 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; 111 import com.android.systemui.statusbar.policy.HeadsUpManager; 112 import com.android.systemui.statusbar.policy.PreviewInflater; 113 import com.android.systemui.statusbar.policy.RemoteInputView; 114 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; 115 import com.android.systemui.statusbar.stack.StackStateAnimator; 116 117 import java.util.ArrayList; 118 import java.util.Collections; 119 import java.util.HashSet; 120 import java.util.List; 121 import java.util.Locale; 122 import java.util.Set; 123 import java.util.Stack; 124 125 import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_HIGH; 126 127 public abstract class BaseStatusBar extends SystemUI implements 128 CommandQueue.Callbacks, ActivatableNotificationView.OnActivatedListener, 129 ExpandableNotificationRow.ExpansionLogger, NotificationData.Environment, 130 ExpandableNotificationRow.OnExpandClickListener, OnGutsClosedListener { 131 public static final String TAG = "StatusBar"; 132 public static final boolean DEBUG = false; 133 public static final boolean MULTIUSER_DEBUG = false; 134 135 public static final boolean ENABLE_REMOTE_INPUT = 136 SystemProperties.getBoolean("debug.enable_remote_input", true); 137 public static final boolean ENABLE_CHILD_NOTIFICATIONS 138 = SystemProperties.getBoolean("debug.child_notifs", true); 139 public static final boolean FORCE_REMOTE_INPUT_HISTORY = 140 SystemProperties.getBoolean("debug.force_remoteinput_history", false); 141 private static boolean ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT = false; 142 143 protected static final int MSG_SHOW_RECENT_APPS = 1019; 144 protected static final int MSG_HIDE_RECENT_APPS = 1020; 145 protected static final int MSG_TOGGLE_RECENTS_APPS = 1021; 146 protected static final int MSG_PRELOAD_RECENT_APPS = 1022; 147 protected static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 1023; 148 protected static final int MSG_SHOW_NEXT_AFFILIATED_TASK = 1024; 149 protected static final int MSG_SHOW_PREV_AFFILIATED_TASK = 1025; 150 protected static final int MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU = 1026; 151 protected static final int MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU = 1027; 152 153 protected static final boolean ENABLE_HEADS_UP = true; 154 protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up"; 155 156 private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF"; 157 158 // Should match the values in PhoneWindowManager 159 public static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps"; 160 public static final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey"; 161 162 private static final String BANNER_ACTION_CANCEL = 163 "com.android.systemui.statusbar.banner_action_cancel"; 164 private static final String BANNER_ACTION_SETUP = 165 "com.android.systemui.statusbar.banner_action_setup"; 166 private static final String WORK_CHALLENGE_UNLOCKED_NOTIFICATION_ACTION 167 = "com.android.systemui.statusbar.work_challenge_unlocked_notification_action"; 168 169 protected CommandQueue mCommandQueue; 170 protected IStatusBarService mBarService; 171 protected H mHandler = createHandler(); 172 173 // all notifications 174 protected NotificationData mNotificationData; 175 protected NotificationStackScrollLayout mStackScroller; 176 177 protected NotificationGroupManager mGroupManager = new NotificationGroupManager(); 178 179 protected RemoteInputController mRemoteInputController; 180 181 // for heads up notifications 182 protected HeadsUpManager mHeadsUpManager; 183 184 // handling reordering 185 protected VisualStabilityManager mVisualStabilityManager = new VisualStabilityManager(); 186 187 protected int mCurrentUserId = 0; 188 final protected SparseArray<UserInfo> mCurrentProfiles = new SparseArray<UserInfo>(); 189 190 protected int mLayoutDirection = -1; // invalid 191 protected AccessibilityManager mAccessibilityManager; 192 193 // on-screen navigation buttons 194 protected NavigationBarView mNavigationBarView = null; 195 196 protected boolean mDeviceInteractive; 197 198 protected boolean mVisible; 199 protected ArraySet<Entry> mHeadsUpEntriesToRemoveOnSwitch = new ArraySet<>(); 200 protected ArraySet<Entry> mRemoteInputEntriesToRemoveOnCollapse = new ArraySet<>(); 201 202 /** 203 * Notifications with keys in this set are not actually around anymore. We kept them around 204 * when they were canceled in response to a remote input interaction. This allows us to show 205 * what you replied and allows you to continue typing into it. 206 */ 207 protected ArraySet<String> mKeysKeptForRemoteInput = new ArraySet<>(); 208 209 // mScreenOnFromKeyguard && mVisible. 210 private boolean mVisibleToUser; 211 212 private Locale mLocale; 213 private float mFontScale; 214 215 protected boolean mUseHeadsUp = false; 216 protected boolean mHeadsUpTicker = false; 217 protected boolean mDisableNotificationAlerts = false; 218 219 protected DevicePolicyManager mDevicePolicyManager; 220 protected IDreamManager mDreamManager; 221 protected PowerManager mPowerManager; 222 protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; 223 224 // public mode, private notifications, etc 225 private boolean mLockscreenPublicMode = false; 226 private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray(); 227 private final SparseBooleanArray mUsersAllowingNotifications = new SparseBooleanArray(); 228 229 private UserManager mUserManager; 230 private int mDensity; 231 232 private KeyguardManager mKeyguardManager; 233 private LockPatternUtils mLockPatternUtils; 234 235 // UI-specific methods 236 237 /** 238 * Create all windows necessary for the status bar (including navigation, overlay panels, etc) 239 * and add them to the window manager. 240 */ createAndAddWindows()241 protected abstract void createAndAddWindows(); 242 243 protected WindowManager mWindowManager; 244 protected IWindowManager mWindowManagerService; 245 refreshLayout(int layoutDirection)246 protected abstract void refreshLayout(int layoutDirection); 247 248 protected Display mDisplay; 249 250 private boolean mDeviceProvisioned = false; 251 252 protected RecentsComponent mRecents; 253 254 protected int mZenMode; 255 256 // which notification is currently being longpress-examined by the user 257 private NotificationGuts mNotificationGutsExposed; 258 259 private KeyboardShortcuts mKeyboardShortcuts; 260 261 /** 262 * The {@link StatusBarState} of the status bar. 263 */ 264 protected int mState; 265 protected boolean mBouncerShowing; 266 protected boolean mShowLockscreenNotifications; 267 protected boolean mAllowLockscreenRemoteInput; 268 269 protected NotificationOverflowContainer mKeyguardIconOverflowContainer; 270 protected DismissView mDismissView; 271 protected EmptyShadeView mEmptyShadeView; 272 273 private NotificationClicker mNotificationClicker = new NotificationClicker(); 274 275 protected AssistManager mAssistManager; 276 277 protected boolean mVrMode; 278 279 private Set<String> mNonBlockablePkgs; 280 281 @Override // NotificationData.Environment isDeviceProvisioned()282 public boolean isDeviceProvisioned() { 283 return mDeviceProvisioned; 284 } 285 286 private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() { 287 @Override 288 public void onVrStateChanged(boolean enabled) { 289 mVrMode = enabled; 290 } 291 }; 292 isDeviceInVrMode()293 public boolean isDeviceInVrMode() { 294 return mVrMode; 295 } 296 297 protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) { 298 @Override 299 public void onChange(boolean selfChange) { 300 final boolean provisioned = 0 != Settings.Global.getInt( 301 mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0); 302 if (provisioned != mDeviceProvisioned) { 303 mDeviceProvisioned = provisioned; 304 updateNotifications(); 305 } 306 final int mode = Settings.Global.getInt(mContext.getContentResolver(), 307 Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF); 308 setZenMode(mode); 309 310 updateLockscreenNotificationSetting(); 311 } 312 }; 313 314 private final ContentObserver mLockscreenSettingsObserver = new ContentObserver(mHandler) { 315 @Override 316 public void onChange(boolean selfChange) { 317 // We don't know which user changed LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS or 318 // LOCK_SCREEN_SHOW_NOTIFICATIONS, so we just dump our cache ... 319 mUsersAllowingPrivateNotifications.clear(); 320 mUsersAllowingNotifications.clear(); 321 // ... and refresh all the notifications 322 updateNotifications(); 323 } 324 }; 325 326 private RemoteViews.OnClickHandler mOnClickHandler = new RemoteViews.OnClickHandler() { 327 @Override 328 public boolean onClickHandler( 329 final View view, final PendingIntent pendingIntent, final Intent fillInIntent) { 330 if (handleRemoteInput(view, pendingIntent, fillInIntent)) { 331 return true; 332 } 333 334 if (DEBUG) { 335 Log.v(TAG, "Notification click handler invoked for intent: " + pendingIntent); 336 } 337 logActionClick(view); 338 // The intent we are sending is for the application, which 339 // won't have permission to immediately start an activity after 340 // the user switches to home. We know it is safe to do at this 341 // point, so make sure new activity switches are now allowed. 342 try { 343 ActivityManagerNative.getDefault().resumeAppSwitches(); 344 } catch (RemoteException e) { 345 } 346 final boolean isActivity = pendingIntent.isActivity(); 347 if (isActivity) { 348 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing(); 349 final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity( 350 mContext, pendingIntent.getIntent(), mCurrentUserId); 351 dismissKeyguardThenExecute(new OnDismissAction() { 352 @Override 353 public boolean onDismiss() { 354 if (keyguardShowing && !afterKeyguardGone) { 355 try { 356 ActivityManagerNative.getDefault() 357 .keyguardWaitingForActivityDrawn(); 358 ActivityManagerNative.getDefault().resumeAppSwitches(); 359 } catch (RemoteException e) { 360 } 361 } 362 363 boolean handled = superOnClickHandler(view, pendingIntent, fillInIntent); 364 overrideActivityPendingAppTransition(keyguardShowing && !afterKeyguardGone); 365 366 // close the shade if it was open 367 if (handled) { 368 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, 369 true /* force */); 370 visibilityChanged(false); 371 mAssistManager.hideAssist(); 372 } 373 374 // Wait for activity start. 375 return handled; 376 } 377 }, afterKeyguardGone); 378 return true; 379 } else { 380 return superOnClickHandler(view, pendingIntent, fillInIntent); 381 } 382 } 383 384 private void logActionClick(View view) { 385 ViewParent parent = view.getParent(); 386 String key = getNotificationKeyForParent(parent); 387 if (key == null) { 388 Log.w(TAG, "Couldn't determine notification for click."); 389 return; 390 } 391 int index = -1; 392 // If this is a default template, determine the index of the button. 393 if (view.getId() == com.android.internal.R.id.action0 && 394 parent != null && parent instanceof ViewGroup) { 395 ViewGroup actionGroup = (ViewGroup) parent; 396 index = actionGroup.indexOfChild(view); 397 } 398 try { 399 mBarService.onNotificationActionClick(key, index); 400 } catch (RemoteException e) { 401 // Ignore 402 } 403 } 404 405 private String getNotificationKeyForParent(ViewParent parent) { 406 while (parent != null) { 407 if (parent instanceof ExpandableNotificationRow) { 408 return ((ExpandableNotificationRow) parent).getStatusBarNotification().getKey(); 409 } 410 parent = parent.getParent(); 411 } 412 return null; 413 } 414 415 private boolean superOnClickHandler(View view, PendingIntent pendingIntent, 416 Intent fillInIntent) { 417 return super.onClickHandler(view, pendingIntent, fillInIntent, 418 StackId.FULLSCREEN_WORKSPACE_STACK_ID); 419 } 420 421 private boolean handleRemoteInput(View view, PendingIntent pendingIntent, Intent fillInIntent) { 422 Object tag = view.getTag(com.android.internal.R.id.remote_input_tag); 423 RemoteInput[] inputs = null; 424 if (tag instanceof RemoteInput[]) { 425 inputs = (RemoteInput[]) tag; 426 } 427 428 if (inputs == null) { 429 return false; 430 } 431 432 RemoteInput input = null; 433 434 for (RemoteInput i : inputs) { 435 if (i.getAllowFreeFormInput()) { 436 input = i; 437 } 438 } 439 440 if (input == null) { 441 return false; 442 } 443 444 ViewParent p = view.getParent(); 445 RemoteInputView riv = null; 446 while (p != null) { 447 if (p instanceof View) { 448 View pv = (View) p; 449 if (pv.isRootNamespace()) { 450 riv = (RemoteInputView) pv.findViewWithTag(RemoteInputView.VIEW_TAG); 451 break; 452 } 453 } 454 p = p.getParent(); 455 } 456 ExpandableNotificationRow row = null; 457 while (p != null) { 458 if (p instanceof ExpandableNotificationRow) { 459 row = (ExpandableNotificationRow) p; 460 break; 461 } 462 p = p.getParent(); 463 } 464 465 if (riv == null || row == null) { 466 return false; 467 } 468 469 row.setUserExpanded(true); 470 471 if (!mAllowLockscreenRemoteInput) { 472 if (isLockscreenPublicMode()) { 473 onLockedRemoteInput(row, view); 474 return true; 475 } 476 final int userId = pendingIntent.getCreatorUserHandle().getIdentifier(); 477 if (mUserManager.getUserInfo(userId).isManagedProfile() 478 && mKeyguardManager.isDeviceLocked(userId)) { 479 onLockedWorkRemoteInput(userId, row, view); 480 return true; 481 } 482 } 483 484 int width = view.getWidth(); 485 if (view instanceof TextView) { 486 // Center the reveal on the text which might be off-center from the TextView 487 TextView tv = (TextView) view; 488 if (tv.getLayout() != null) { 489 int innerWidth = (int) tv.getLayout().getLineWidth(0); 490 innerWidth += tv.getCompoundPaddingLeft() + tv.getCompoundPaddingRight(); 491 width = Math.min(width, innerWidth); 492 } 493 } 494 int cx = view.getLeft() + width / 2; 495 int cy = view.getTop() + view.getHeight() / 2; 496 int w = riv.getWidth(); 497 int h = riv.getHeight(); 498 int r = Math.max( 499 Math.max(cx + cy, cx + (h - cy)), 500 Math.max((w - cx) + cy, (w - cx) + (h - cy))); 501 502 riv.setRevealParameters(cx, cy, r); 503 riv.setPendingIntent(pendingIntent); 504 riv.setRemoteInput(inputs, input); 505 riv.focusAnimated(); 506 507 return true; 508 } 509 510 }; 511 512 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 513 @Override 514 public void onReceive(Context context, Intent intent) { 515 String action = intent.getAction(); 516 if (Intent.ACTION_USER_SWITCHED.equals(action)) { 517 mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); 518 updateCurrentProfilesCache(); 519 if (true) Log.v(TAG, "userId " + mCurrentUserId + " is in the house"); 520 521 updateLockscreenNotificationSetting(); 522 523 userSwitched(mCurrentUserId); 524 } else if (Intent.ACTION_USER_ADDED.equals(action)) { 525 updateCurrentProfilesCache(); 526 } else if (Intent.ACTION_USER_PRESENT.equals(action)) { 527 List<ActivityManager.RecentTaskInfo> recentTask = null; 528 try { 529 recentTask = ActivityManagerNative.getDefault().getRecentTasks(1, 530 ActivityManager.RECENT_WITH_EXCLUDED 531 | ActivityManager.RECENT_INCLUDE_PROFILES, 532 mCurrentUserId).getList(); 533 } catch (RemoteException e) { 534 // Abandon hope activity manager not running. 535 } 536 if (recentTask != null && recentTask.size() > 0) { 537 UserInfo user = mUserManager.getUserInfo(recentTask.get(0).userId); 538 if (user != null && user.isManagedProfile()) { 539 Toast toast = Toast.makeText(mContext, 540 R.string.managed_profile_foreground_toast, 541 Toast.LENGTH_SHORT); 542 TextView text = (TextView) toast.getView().findViewById( 543 android.R.id.message); 544 text.setCompoundDrawablesRelativeWithIntrinsicBounds( 545 R.drawable.stat_sys_managed_profile_status, 0, 0, 0); 546 int paddingPx = mContext.getResources().getDimensionPixelSize( 547 R.dimen.managed_profile_toast_padding); 548 text.setCompoundDrawablePadding(paddingPx); 549 toast.show(); 550 } 551 } 552 } else if (BANNER_ACTION_CANCEL.equals(action) || BANNER_ACTION_SETUP.equals(action)) { 553 NotificationManager noMan = (NotificationManager) 554 mContext.getSystemService(Context.NOTIFICATION_SERVICE); 555 noMan.cancel(SystemMessage.NOTE_HIDDEN_NOTIFICATIONS); 556 557 Settings.Secure.putInt(mContext.getContentResolver(), 558 Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0); 559 if (BANNER_ACTION_SETUP.equals(action)) { 560 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, 561 true /* force */); 562 mContext.startActivity(new Intent(Settings.ACTION_APP_NOTIFICATION_REDACTION) 563 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 564 565 ); 566 } 567 } else if (WORK_CHALLENGE_UNLOCKED_NOTIFICATION_ACTION.equals(action)) { 568 final IntentSender intentSender = intent.getParcelableExtra(Intent.EXTRA_INTENT); 569 final String notificationKey = intent.getStringExtra(Intent.EXTRA_INDEX); 570 if (intentSender != null) { 571 try { 572 mContext.startIntentSender(intentSender, null, 0, 0, 0); 573 } catch (IntentSender.SendIntentException e) { 574 /* ignore */ 575 } 576 } 577 if (notificationKey != null) { 578 try { 579 mBarService.onNotificationClick(notificationKey); 580 } catch (RemoteException e) { 581 /* ignore */ 582 } 583 } 584 onWorkChallengeUnlocked(); 585 } 586 } 587 }; 588 589 private final BroadcastReceiver mAllUsersReceiver = new BroadcastReceiver() { 590 @Override 591 public void onReceive(Context context, Intent intent) { 592 String action = intent.getAction(); 593 if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action) && 594 isCurrentProfile(getSendingUserId())) { 595 mUsersAllowingPrivateNotifications.clear(); 596 updateLockscreenNotificationSetting(); 597 updateNotifications(); 598 } 599 } 600 }; 601 602 private final NotificationListenerService mNotificationListener = 603 new NotificationListenerService() { 604 @Override 605 public void onListenerConnected() { 606 if (DEBUG) Log.d(TAG, "onListenerConnected"); 607 final StatusBarNotification[] notifications = getActiveNotifications(); 608 final RankingMap currentRanking = getCurrentRanking(); 609 mHandler.post(new Runnable() { 610 @Override 611 public void run() { 612 for (StatusBarNotification sbn : notifications) { 613 addNotification(sbn, currentRanking, null /* oldEntry */); 614 } 615 } 616 }); 617 } 618 619 @Override 620 public void onNotificationPosted(final StatusBarNotification sbn, 621 final RankingMap rankingMap) { 622 if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn); 623 if (sbn != null) { 624 mHandler.post(new Runnable() { 625 @Override 626 public void run() { 627 processForRemoteInput(sbn.getNotification()); 628 String key = sbn.getKey(); 629 mKeysKeptForRemoteInput.remove(key); 630 boolean isUpdate = mNotificationData.get(key) != null; 631 // In case we don't allow child notifications, we ignore children of 632 // notifications that have a summary, since we're not going to show them 633 // anyway. This is true also when the summary is canceled, 634 // because children are automatically canceled by NoMan in that case. 635 if (!ENABLE_CHILD_NOTIFICATIONS 636 && mGroupManager.isChildInGroupWithSummary(sbn)) { 637 if (DEBUG) { 638 Log.d(TAG, "Ignoring group child due to existing summary: " + sbn); 639 } 640 641 // Remove existing notification to avoid stale data. 642 if (isUpdate) { 643 removeNotification(key, rankingMap); 644 } else { 645 mNotificationData.updateRanking(rankingMap); 646 } 647 return; 648 } 649 if (isUpdate) { 650 updateNotification(sbn, rankingMap); 651 } else { 652 addNotification(sbn, rankingMap, null /* oldEntry */); 653 } 654 } 655 }); 656 } 657 } 658 659 @Override 660 public void onNotificationRemoved(StatusBarNotification sbn, 661 final RankingMap rankingMap) { 662 if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn); 663 if (sbn != null) { 664 final String key = sbn.getKey(); 665 mHandler.post(new Runnable() { 666 @Override 667 public void run() { 668 removeNotification(key, rankingMap); 669 } 670 }); 671 } 672 } 673 674 @Override 675 public void onNotificationRankingUpdate(final RankingMap rankingMap) { 676 if (DEBUG) Log.d(TAG, "onRankingUpdate"); 677 if (rankingMap != null) { 678 mHandler.post(new Runnable() { 679 @Override 680 public void run() { 681 updateNotificationRanking(rankingMap); 682 } 683 }); 684 } } 685 686 }; 687 updateCurrentProfilesCache()688 private void updateCurrentProfilesCache() { 689 synchronized (mCurrentProfiles) { 690 mCurrentProfiles.clear(); 691 if (mUserManager != null) { 692 for (UserInfo user : mUserManager.getProfiles(mCurrentUserId)) { 693 mCurrentProfiles.put(user.id, user); 694 } 695 } 696 } 697 } 698 start()699 public void start() { 700 mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE); 701 mWindowManagerService = WindowManagerGlobal.getWindowManagerService(); 702 mDisplay = mWindowManager.getDefaultDisplay(); 703 mDevicePolicyManager = (DevicePolicyManager)mContext.getSystemService( 704 Context.DEVICE_POLICY_SERVICE); 705 706 mNotificationData = new NotificationData(this); 707 708 mAccessibilityManager = (AccessibilityManager) 709 mContext.getSystemService(Context.ACCESSIBILITY_SERVICE); 710 711 mDreamManager = IDreamManager.Stub.asInterface( 712 ServiceManager.checkService(DreamService.DREAM_SERVICE)); 713 mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); 714 715 mContext.getContentResolver().registerContentObserver( 716 Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), true, 717 mSettingsObserver); 718 mContext.getContentResolver().registerContentObserver( 719 Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false, 720 mSettingsObserver); 721 mContext.getContentResolver().registerContentObserver( 722 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false, 723 mSettingsObserver, 724 UserHandle.USER_ALL); 725 if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) { 726 mContext.getContentResolver().registerContentObserver( 727 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT), 728 false, 729 mSettingsObserver, 730 UserHandle.USER_ALL); 731 } 732 733 mContext.getContentResolver().registerContentObserver( 734 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS), 735 true, 736 mLockscreenSettingsObserver, 737 UserHandle.USER_ALL); 738 739 mBarService = IStatusBarService.Stub.asInterface( 740 ServiceManager.getService(Context.STATUS_BAR_SERVICE)); 741 742 mRecents = getComponent(Recents.class); 743 744 final Configuration currentConfig = mContext.getResources().getConfiguration(); 745 mLocale = currentConfig.locale; 746 mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale); 747 mFontScale = currentConfig.fontScale; 748 mDensity = currentConfig.densityDpi; 749 750 mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); 751 mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE); 752 mLockPatternUtils = new LockPatternUtils(mContext); 753 754 // Connect in to the status bar manager service 755 mCommandQueue = new CommandQueue(this); 756 757 int[] switches = new int[9]; 758 ArrayList<IBinder> binders = new ArrayList<IBinder>(); 759 ArrayList<String> iconSlots = new ArrayList<>(); 760 ArrayList<StatusBarIcon> icons = new ArrayList<>(); 761 Rect fullscreenStackBounds = new Rect(); 762 Rect dockedStackBounds = new Rect(); 763 try { 764 mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders, 765 fullscreenStackBounds, dockedStackBounds); 766 } catch (RemoteException ex) { 767 // If the system process isn't there we're doomed anyway. 768 } 769 770 createAndAddWindows(); 771 772 mSettingsObserver.onChange(false); // set up 773 disable(switches[0], switches[6], false /* animate */); 774 setSystemUiVisibility(switches[1], switches[7], switches[8], 0xffffffff, 775 fullscreenStackBounds, dockedStackBounds); 776 topAppWindowChanged(switches[2] != 0); 777 // StatusBarManagerService has a back up of IME token and it's restored here. 778 setImeWindowStatus(binders.get(0), switches[3], switches[4], switches[5] != 0); 779 780 // Set up the initial icon state 781 int N = iconSlots.size(); 782 int viewIndex = 0; 783 for (int i=0; i < N; i++) { 784 setIcon(iconSlots.get(i), icons.get(i)); 785 } 786 787 // Set up the initial notification state. 788 try { 789 mNotificationListener.registerAsSystemService(mContext, 790 new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()), 791 UserHandle.USER_ALL); 792 } catch (RemoteException e) { 793 Log.e(TAG, "Unable to register notification listener", e); 794 } 795 796 797 if (DEBUG) { 798 Log.d(TAG, String.format( 799 "init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x", 800 icons.size(), 801 switches[0], 802 switches[1], 803 switches[2], 804 switches[3] 805 )); 806 } 807 808 mCurrentUserId = ActivityManager.getCurrentUser(); 809 setHeadsUpUser(mCurrentUserId); 810 811 IntentFilter filter = new IntentFilter(); 812 filter.addAction(Intent.ACTION_USER_SWITCHED); 813 filter.addAction(Intent.ACTION_USER_ADDED); 814 filter.addAction(Intent.ACTION_USER_PRESENT); 815 mContext.registerReceiver(mBroadcastReceiver, filter); 816 817 IntentFilter internalFilter = new IntentFilter(); 818 internalFilter.addAction(WORK_CHALLENGE_UNLOCKED_NOTIFICATION_ACTION); 819 internalFilter.addAction(BANNER_ACTION_CANCEL); 820 internalFilter.addAction(BANNER_ACTION_SETUP); 821 mContext.registerReceiver(mBroadcastReceiver, internalFilter, PERMISSION_SELF, null); 822 823 IntentFilter allUsersFilter = new IntentFilter(); 824 allUsersFilter.addAction( 825 DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); 826 mContext.registerReceiverAsUser(mAllUsersReceiver, UserHandle.ALL, allUsersFilter, 827 null, null); 828 updateCurrentProfilesCache(); 829 830 IVrManager vrManager = IVrManager.Stub.asInterface(ServiceManager.getService("vrmanager")); 831 try { 832 vrManager.registerListener(mVrStateCallbacks); 833 } catch (RemoteException e) { 834 Slog.e(TAG, "Failed to register VR mode state listener: " + e); 835 } 836 837 mNonBlockablePkgs = new ArraySet<String>(); 838 Collections.addAll(mNonBlockablePkgs, mContext.getResources().getStringArray( 839 com.android.internal.R.array.config_nonBlockableNotificationPackages)); 840 } 841 notifyUserAboutHiddenNotifications()842 protected void notifyUserAboutHiddenNotifications() { 843 if (0 != Settings.Secure.getInt(mContext.getContentResolver(), 844 Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 1)) { 845 Log.d(TAG, "user hasn't seen notification about hidden notifications"); 846 if (!mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())) { 847 Log.d(TAG, "insecure lockscreen, skipping notification"); 848 Settings.Secure.putInt(mContext.getContentResolver(), 849 Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0); 850 return; 851 } 852 Log.d(TAG, "disabling lockecreen notifications and alerting the user"); 853 // disable lockscreen notifications until user acts on the banner. 854 Settings.Secure.putInt(mContext.getContentResolver(), 855 Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0); 856 Settings.Secure.putInt(mContext.getContentResolver(), 857 Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0); 858 859 final String packageName = mContext.getPackageName(); 860 PendingIntent cancelIntent = PendingIntent.getBroadcast(mContext, 0, 861 new Intent(BANNER_ACTION_CANCEL).setPackage(packageName), 862 PendingIntent.FLAG_CANCEL_CURRENT); 863 PendingIntent setupIntent = PendingIntent.getBroadcast(mContext, 0, 864 new Intent(BANNER_ACTION_SETUP).setPackage(packageName), 865 PendingIntent.FLAG_CANCEL_CURRENT); 866 867 final int colorRes = com.android.internal.R.color.system_notification_accent_color; 868 Notification.Builder note = new Notification.Builder(mContext) 869 .setSmallIcon(R.drawable.ic_android) 870 .setContentTitle(mContext.getString(R.string.hidden_notifications_title)) 871 .setContentText(mContext.getString(R.string.hidden_notifications_text)) 872 .setPriority(Notification.PRIORITY_HIGH) 873 .setOngoing(true) 874 .setColor(mContext.getColor(colorRes)) 875 .setContentIntent(setupIntent) 876 .addAction(R.drawable.ic_close, 877 mContext.getString(R.string.hidden_notifications_cancel), 878 cancelIntent) 879 .addAction(R.drawable.ic_settings, 880 mContext.getString(R.string.hidden_notifications_setup), 881 setupIntent); 882 overrideNotificationAppName(mContext, note); 883 884 NotificationManager noMan = 885 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); 886 noMan.notify(SystemMessage.NOTE_HIDDEN_NOTIFICATIONS, note.build()); 887 } 888 } 889 userSwitched(int newUserId)890 public void userSwitched(int newUserId) { 891 setHeadsUpUser(newUserId); 892 } 893 setHeadsUpUser(int newUserId)894 protected abstract void setHeadsUpUser(int newUserId); 895 896 @Override // NotificationData.Environment isNotificationForCurrentProfiles(StatusBarNotification n)897 public boolean isNotificationForCurrentProfiles(StatusBarNotification n) { 898 final int thisUserId = mCurrentUserId; 899 final int notificationUserId = n.getUserId(); 900 if (DEBUG && MULTIUSER_DEBUG) { 901 Log.v(TAG, String.format("%s: current userid: %d, notification userid: %d", 902 n, thisUserId, notificationUserId)); 903 } 904 return isCurrentProfile(notificationUserId); 905 } 906 setNotificationShown(StatusBarNotification n)907 protected void setNotificationShown(StatusBarNotification n) { 908 setNotificationsShown(new String[]{n.getKey()}); 909 } 910 setNotificationsShown(String[] keys)911 protected void setNotificationsShown(String[] keys) { 912 try { 913 mNotificationListener.setNotificationsShown(keys); 914 } catch (RuntimeException e) { 915 Log.d(TAG, "failed setNotificationsShown: ", e); 916 } 917 } 918 isCurrentProfile(int userId)919 protected boolean isCurrentProfile(int userId) { 920 synchronized (mCurrentProfiles) { 921 return userId == UserHandle.USER_ALL || mCurrentProfiles.get(userId) != null; 922 } 923 } 924 925 @Override getCurrentMediaNotificationKey()926 public String getCurrentMediaNotificationKey() { 927 return null; 928 } 929 930 @Override getGroupManager()931 public NotificationGroupManager getGroupManager() { 932 return mGroupManager; 933 } 934 935 /** 936 * Takes the necessary steps to prepare the status bar for starting an activity, then starts it. 937 * @param action A dismiss action that is called if it's safe to start the activity. 938 * @param afterKeyguardGone Whether the action should be executed after the Keyguard is gone. 939 */ dismissKeyguardThenExecute(OnDismissAction action, boolean afterKeyguardGone)940 protected void dismissKeyguardThenExecute(OnDismissAction action, boolean afterKeyguardGone) { 941 action.onDismiss(); 942 } 943 944 @Override onConfigurationChanged(Configuration newConfig)945 protected void onConfigurationChanged(Configuration newConfig) { 946 final Locale locale = mContext.getResources().getConfiguration().locale; 947 final int ld = TextUtils.getLayoutDirectionFromLocale(locale); 948 final float fontScale = newConfig.fontScale; 949 final int density = newConfig.densityDpi; 950 if (density != mDensity || mFontScale != fontScale) { 951 onDensityOrFontScaleChanged(); 952 mDensity = density; 953 mFontScale = fontScale; 954 } 955 if (! locale.equals(mLocale) || ld != mLayoutDirection) { 956 if (DEBUG) { 957 Log.v(TAG, String.format( 958 "config changed locale/LD: %s (%d) -> %s (%d)", mLocale, mLayoutDirection, 959 locale, ld)); 960 } 961 mLocale = locale; 962 mLayoutDirection = ld; 963 refreshLayout(ld); 964 } 965 } 966 onDensityOrFontScaleChanged()967 protected void onDensityOrFontScaleChanged() { 968 ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications(); 969 for (int i = 0; i < activeNotifications.size(); i++) { 970 Entry entry = activeNotifications.get(i); 971 boolean exposedGuts = entry.row.getGuts() == mNotificationGutsExposed; 972 entry.row.reInflateViews(); 973 if (exposedGuts) { 974 mNotificationGutsExposed = entry.row.getGuts(); 975 bindGuts(entry.row); 976 } 977 inflateViews(entry, mStackScroller); 978 } 979 } 980 bindDismissListener(final ExpandableNotificationRow row)981 protected void bindDismissListener(final ExpandableNotificationRow row) { 982 row.setOnDismissListener(new View.OnClickListener() { 983 public void onClick(View v) { 984 // Accessibility feedback 985 v.announceForAccessibility( 986 mContext.getString(R.string.accessibility_notification_dismissed)); 987 performRemoveNotification(row.getStatusBarNotification()); 988 } 989 }); 990 } 991 performRemoveNotification(StatusBarNotification n)992 protected void performRemoveNotification(StatusBarNotification n) { 993 final String pkg = n.getPackageName(); 994 final String tag = n.getTag(); 995 final int id = n.getId(); 996 final int userId = n.getUserId(); 997 try { 998 mBarService.onNotificationClear(pkg, tag, id, userId); 999 if (FORCE_REMOTE_INPUT_HISTORY 1000 && mKeysKeptForRemoteInput.contains(n.getKey())) { 1001 mKeysKeptForRemoteInput.remove(n.getKey()); 1002 } 1003 removeNotification(n.getKey(), null); 1004 1005 } catch (RemoteException ex) { 1006 // system process is dead if we're here. 1007 } 1008 } 1009 1010 applyColorsAndBackgrounds(StatusBarNotification sbn, NotificationData.Entry entry)1011 protected void applyColorsAndBackgrounds(StatusBarNotification sbn, 1012 NotificationData.Entry entry) { 1013 1014 if (entry.getContentView().getId() 1015 != com.android.internal.R.id.status_bar_latest_event_content) { 1016 // Using custom RemoteViews 1017 if (entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD 1018 && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP) { 1019 entry.row.setShowingLegacyBackground(true); 1020 entry.legacy = true; 1021 } 1022 } 1023 1024 if (entry.icon != null) { 1025 entry.icon.setTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP); 1026 } 1027 } 1028 isMediaNotification(NotificationData.Entry entry)1029 public boolean isMediaNotification(NotificationData.Entry entry) { 1030 // TODO: confirm that there's a valid media key 1031 return entry.getExpandedContentView() != null && 1032 entry.getExpandedContentView() 1033 .findViewById(com.android.internal.R.id.media_actions) != null; 1034 } 1035 1036 // The (i) button in the guts that links to the system notification settings for that app startAppNotificationSettingsActivity(String packageName, final int appUid)1037 private void startAppNotificationSettingsActivity(String packageName, final int appUid) { 1038 final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS); 1039 intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName); 1040 intent.putExtra(Settings.EXTRA_APP_UID, appUid); 1041 startNotificationGutsIntent(intent, appUid); 1042 } 1043 startNotificationGutsIntent(final Intent intent, final int appUid)1044 private void startNotificationGutsIntent(final Intent intent, final int appUid) { 1045 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing(); 1046 dismissKeyguardThenExecute(new OnDismissAction() { 1047 @Override 1048 public boolean onDismiss() { 1049 AsyncTask.execute(new Runnable() { 1050 public void run() { 1051 try { 1052 if (keyguardShowing) { 1053 ActivityManagerNative.getDefault() 1054 .keyguardWaitingForActivityDrawn(); 1055 } 1056 TaskStackBuilder.create(mContext) 1057 .addNextIntentWithParentStack(intent) 1058 .startActivities(getActivityOptions(), 1059 new UserHandle(UserHandle.getUserId(appUid))); 1060 overrideActivityPendingAppTransition(keyguardShowing); 1061 } catch (RemoteException e) { 1062 } 1063 } 1064 }); 1065 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */); 1066 return true; 1067 } 1068 }, false /* afterKeyguardGone */); 1069 } 1070 bindGuts(final ExpandableNotificationRow row)1071 private void bindGuts(final ExpandableNotificationRow row) { 1072 row.inflateGuts(); 1073 final StatusBarNotification sbn = row.getStatusBarNotification(); 1074 PackageManager pmUser = getPackageManagerForUser(mContext, sbn.getUser().getIdentifier()); 1075 row.setTag(sbn.getPackageName()); 1076 final NotificationGuts guts = row.getGuts(); 1077 guts.setClosedListener(this); 1078 final String pkg = sbn.getPackageName(); 1079 String appname = pkg; 1080 Drawable pkgicon = null; 1081 int appUid = -1; 1082 try { 1083 final ApplicationInfo info = pmUser.getApplicationInfo(pkg, 1084 PackageManager.GET_UNINSTALLED_PACKAGES 1085 | PackageManager.GET_DISABLED_COMPONENTS); 1086 if (info != null) { 1087 appname = String.valueOf(pmUser.getApplicationLabel(info)); 1088 pkgicon = pmUser.getApplicationIcon(info); 1089 appUid = info.uid; 1090 } 1091 } catch (NameNotFoundException e) { 1092 // app is gone, just show package name and generic icon 1093 pkgicon = pmUser.getDefaultActivityIcon(); 1094 } 1095 1096 ((ImageView) guts.findViewById(R.id.app_icon)).setImageDrawable(pkgicon); 1097 ((TextView) guts.findViewById(R.id.pkgname)).setText(appname); 1098 1099 final TextView settingsButton = (TextView) guts.findViewById(R.id.more_settings); 1100 if (appUid >= 0) { 1101 final int appUidF = appUid; 1102 settingsButton.setOnClickListener(new View.OnClickListener() { 1103 public void onClick(View v) { 1104 MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTE_INFO); 1105 guts.resetFalsingCheck(); 1106 startAppNotificationSettingsActivity(pkg, appUidF); 1107 } 1108 }); 1109 settingsButton.setText(R.string.notification_more_settings); 1110 } else { 1111 settingsButton.setVisibility(View.GONE); 1112 } 1113 1114 guts.bindImportance(pmUser, sbn, mNonBlockablePkgs, 1115 mNotificationData.getImportance(sbn.getKey())); 1116 1117 final TextView doneButton = (TextView) guts.findViewById(R.id.done); 1118 doneButton.setText(R.string.notification_done); 1119 doneButton.setOnClickListener(new View.OnClickListener() { 1120 @Override 1121 public void onClick(View v) { 1122 // If the user has security enabled, show challenge if the setting is changed. 1123 if (guts.hasImportanceChanged() && isLockscreenPublicMode() && 1124 (mState == StatusBarState.KEYGUARD 1125 || mState == StatusBarState.SHADE_LOCKED)) { 1126 OnDismissAction dismissAction = new OnDismissAction() { 1127 @Override 1128 public boolean onDismiss() { 1129 saveImportanceCloseControls(sbn, row, guts, v); 1130 return true; 1131 } 1132 }; 1133 onLockedNotificationImportanceChange(dismissAction); 1134 } else { 1135 saveImportanceCloseControls(sbn, row, guts, v); 1136 } 1137 } 1138 }); 1139 } 1140 saveImportanceCloseControls(StatusBarNotification sbn, ExpandableNotificationRow row, NotificationGuts guts, View done)1141 private void saveImportanceCloseControls(StatusBarNotification sbn, 1142 ExpandableNotificationRow row, NotificationGuts guts, View done) { 1143 guts.resetFalsingCheck(); 1144 guts.saveImportance(sbn); 1145 1146 int[] rowLocation = new int[2]; 1147 int[] doneLocation = new int[2]; 1148 row.getLocationOnScreen(rowLocation); 1149 done.getLocationOnScreen(doneLocation); 1150 1151 final int centerX = done.getWidth() / 2; 1152 final int centerY = done.getHeight() / 2; 1153 final int x = doneLocation[0] - rowLocation[0] + centerX; 1154 final int y = doneLocation[1] - rowLocation[1] + centerY; 1155 dismissPopups(x, y); 1156 } 1157 getNotificationLongClicker()1158 protected SwipeHelper.LongPressListener getNotificationLongClicker() { 1159 return new SwipeHelper.LongPressListener() { 1160 @Override 1161 public boolean onLongPress(View v, final int x, final int y) { 1162 if (!(v instanceof ExpandableNotificationRow)) { 1163 return false; 1164 } 1165 if (v.getWindowToken() == null) { 1166 Log.e(TAG, "Trying to show notification guts, but not attached to window"); 1167 return false; 1168 } 1169 1170 final ExpandableNotificationRow row = (ExpandableNotificationRow) v; 1171 bindGuts(row); 1172 1173 // Assume we are a status_bar_notification_row 1174 final NotificationGuts guts = row.getGuts(); 1175 if (guts == null) { 1176 // This view has no guts. Examples are the more card or the dismiss all view 1177 return false; 1178 } 1179 1180 // Already showing? 1181 if (guts.getVisibility() == View.VISIBLE) { 1182 dismissPopups(x, y); 1183 return false; 1184 } 1185 1186 MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTE_CONTROLS); 1187 1188 // ensure that it's laid but not visible until actually laid out 1189 guts.setVisibility(View.INVISIBLE); 1190 // Post to ensure the the guts are properly laid out. 1191 guts.post(new Runnable() { 1192 public void run() { 1193 dismissPopups(-1 /* x */, -1 /* y */, false /* resetGear */, 1194 false /* animate */); 1195 guts.setVisibility(View.VISIBLE); 1196 final double horz = Math.max(guts.getWidth() - x, x); 1197 final double vert = Math.max(guts.getHeight() - y, y); 1198 final float r = (float) Math.hypot(horz, vert); 1199 final Animator a 1200 = ViewAnimationUtils.createCircularReveal(guts, x, y, 0, r); 1201 a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD); 1202 a.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); 1203 a.addListener(new AnimatorListenerAdapter() { 1204 @Override 1205 public void onAnimationEnd(Animator animation) { 1206 super.onAnimationEnd(animation); 1207 // Move the notification view back over the gear 1208 row.resetTranslation(); 1209 } 1210 }); 1211 a.start(); 1212 guts.setExposed(true /* exposed */, 1213 mState == StatusBarState.KEYGUARD /* needsFalsingProtection */); 1214 row.closeRemoteInput(); 1215 mStackScroller.onHeightChanged(null, true /* needsAnimation */); 1216 mNotificationGutsExposed = guts; 1217 } 1218 }); 1219 return true; 1220 } 1221 }; 1222 } 1223 1224 /** 1225 * Returns the exposed NotificationGuts or null if none are exposed. 1226 */ 1227 public NotificationGuts getExposedGuts() { 1228 return mNotificationGutsExposed; 1229 } 1230 1231 public void dismissPopups() { 1232 dismissPopups(-1 /* x */, -1 /* y */, true /* resetGear */, false /* animate */); 1233 } 1234 1235 private void dismissPopups(int x, int y) { 1236 dismissPopups(x, y, true /* resetGear */, false /* animate */); 1237 } 1238 1239 public void dismissPopups(int x, int y, boolean resetGear, boolean animate) { 1240 if (mNotificationGutsExposed != null) { 1241 mNotificationGutsExposed.closeControls(x, y, true /* notify */); 1242 } 1243 if (resetGear) { 1244 mStackScroller.resetExposedGearView(animate, true /* force */); 1245 } 1246 } 1247 1248 @Override 1249 public void onGutsClosed(NotificationGuts guts) { 1250 mStackScroller.onHeightChanged(null, true /* needsAnimation */); 1251 mNotificationGutsExposed = null; 1252 } 1253 1254 @Override 1255 public void showRecentApps(boolean triggeredFromAltTab, boolean fromHome) { 1256 int msg = MSG_SHOW_RECENT_APPS; 1257 mHandler.removeMessages(msg); 1258 mHandler.obtainMessage(msg, triggeredFromAltTab ? 1 : 0, fromHome ? 1 : 0).sendToTarget(); 1259 } 1260 1261 @Override 1262 public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) { 1263 int msg = MSG_HIDE_RECENT_APPS; 1264 mHandler.removeMessages(msg); 1265 mHandler.obtainMessage(msg, triggeredFromAltTab ? 1 : 0, 1266 triggeredFromHomeKey ? 1 : 0).sendToTarget(); 1267 } 1268 1269 @Override 1270 public void toggleRecentApps() { 1271 toggleRecents(); 1272 } 1273 1274 @Override 1275 public void toggleSplitScreen() { 1276 toggleSplitScreenMode(-1 /* metricsDockAction */, -1 /* metricsUndockAction */); 1277 } 1278 1279 @Override 1280 public void preloadRecentApps() { 1281 int msg = MSG_PRELOAD_RECENT_APPS; 1282 mHandler.removeMessages(msg); 1283 mHandler.sendEmptyMessage(msg); 1284 } 1285 1286 @Override 1287 public void cancelPreloadRecentApps() { 1288 int msg = MSG_CANCEL_PRELOAD_RECENT_APPS; 1289 mHandler.removeMessages(msg); 1290 mHandler.sendEmptyMessage(msg); 1291 } 1292 1293 @Override 1294 public void dismissKeyboardShortcutsMenu() { 1295 int msg = MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU; 1296 mHandler.removeMessages(msg); 1297 mHandler.sendEmptyMessage(msg); 1298 } 1299 1300 @Override 1301 public void toggleKeyboardShortcutsMenu(int deviceId) { 1302 int msg = MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU; 1303 mHandler.removeMessages(msg); 1304 mHandler.obtainMessage(msg, deviceId, 0).sendToTarget(); 1305 } 1306 1307 /** Jumps to the next affiliated task in the group. */ 1308 public void showNextAffiliatedTask() { 1309 int msg = MSG_SHOW_NEXT_AFFILIATED_TASK; 1310 mHandler.removeMessages(msg); 1311 mHandler.sendEmptyMessage(msg); 1312 } 1313 1314 /** Jumps to the previous affiliated task in the group. */ 1315 public void showPreviousAffiliatedTask() { 1316 int msg = MSG_SHOW_PREV_AFFILIATED_TASK; 1317 mHandler.removeMessages(msg); 1318 mHandler.sendEmptyMessage(msg); 1319 } 1320 1321 protected H createHandler() { 1322 return new H(); 1323 } 1324 1325 protected void sendCloseSystemWindows(String reason) { 1326 if (ActivityManagerNative.isSystemReady()) { 1327 try { 1328 ActivityManagerNative.getDefault().closeSystemDialogs(reason); 1329 } catch (RemoteException e) { 1330 } 1331 } 1332 } 1333 1334 protected abstract View getStatusBarView(); 1335 1336 protected View.OnTouchListener mRecentsPreloadOnTouchListener = new View.OnTouchListener() { 1337 // additional optimization when we have software system buttons - start loading the recent 1338 // tasks on touch down 1339 @Override 1340 public boolean onTouch(View v, MotionEvent event) { 1341 int action = event.getAction() & MotionEvent.ACTION_MASK; 1342 if (action == MotionEvent.ACTION_DOWN) { 1343 preloadRecents(); 1344 } else if (action == MotionEvent.ACTION_CANCEL) { 1345 cancelPreloadingRecents(); 1346 } else if (action == MotionEvent.ACTION_UP) { 1347 if (!v.isPressed()) { 1348 cancelPreloadingRecents(); 1349 } 1350 1351 } 1352 return false; 1353 } 1354 }; 1355 1356 /** 1357 * Toggle docking the app window 1358 * 1359 * @param metricsDockAction the action to log when docking is successful, or -1 to not log 1360 * anything on successful docking 1361 * @param metricsUndockAction the action to log when undocking, or -1 to not log anything when 1362 * undocking 1363 */ 1364 protected abstract void toggleSplitScreenMode(int metricsDockAction, int metricsUndockAction); 1365 1366 /** Proxy for RecentsComponent */ 1367 1368 protected void showRecents(boolean triggeredFromAltTab, boolean fromHome) { 1369 if (mRecents != null) { 1370 sendCloseSystemWindows(SYSTEM_DIALOG_REASON_RECENT_APPS); 1371 mRecents.showRecents(triggeredFromAltTab, fromHome); 1372 } 1373 } 1374 1375 protected void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) { 1376 if (mRecents != null) { 1377 mRecents.hideRecents(triggeredFromAltTab, triggeredFromHomeKey); 1378 } 1379 } 1380 1381 protected void toggleRecents() { 1382 if (mRecents != null) { 1383 mRecents.toggleRecents(mDisplay); 1384 } 1385 } 1386 1387 protected void preloadRecents() { 1388 if (mRecents != null) { 1389 mRecents.preloadRecents(); 1390 } 1391 } 1392 1393 protected void toggleKeyboardShortcuts(int deviceId) { 1394 KeyboardShortcuts.toggle(mContext, deviceId); 1395 } 1396 1397 protected void dismissKeyboardShortcuts() { 1398 KeyboardShortcuts.dismiss(); 1399 } 1400 1401 protected void cancelPreloadingRecents() { 1402 if (mRecents != null) { 1403 mRecents.cancelPreloadingRecents(); 1404 } 1405 } 1406 1407 protected void showRecentsNextAffiliatedTask() { 1408 if (mRecents != null) { 1409 mRecents.showNextAffiliatedTask(); 1410 } 1411 } 1412 1413 protected void showRecentsPreviousAffiliatedTask() { 1414 if (mRecents != null) { 1415 mRecents.showPrevAffiliatedTask(); 1416 } 1417 } 1418 1419 /** 1420 * If there is an active heads-up notification and it has a fullscreen intent, fire it now. 1421 */ 1422 public abstract void maybeEscalateHeadsUp(); 1423 1424 /** 1425 * Save the current "public" (locked and secure) state of the lockscreen. 1426 */ 1427 public void setLockscreenPublicMode(boolean publicMode) { 1428 mLockscreenPublicMode = publicMode; 1429 } 1430 1431 public boolean isLockscreenPublicMode() { 1432 return mLockscreenPublicMode; 1433 } 1434 1435 protected void onWorkChallengeUnlocked() {} 1436 1437 /** 1438 * Has the given user chosen to allow notifications to be shown even when the lockscreen is in 1439 * "public" (secure & locked) mode? 1440 */ 1441 public boolean userAllowsNotificationsInPublic(int userHandle) { 1442 if (userHandle == UserHandle.USER_ALL) { 1443 return true; 1444 } 1445 1446 if (mUsersAllowingNotifications.indexOfKey(userHandle) < 0) { 1447 final boolean allowed = 0 != Settings.Secure.getIntForUser( 1448 mContext.getContentResolver(), 1449 Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, userHandle); 1450 mUsersAllowingNotifications.append(userHandle, allowed); 1451 return allowed; 1452 } 1453 1454 return mUsersAllowingNotifications.get(userHandle); 1455 } 1456 1457 /** 1458 * Has the given user chosen to allow their private (full) notifications to be shown even 1459 * when the lockscreen is in "public" (secure & locked) mode? 1460 */ 1461 public boolean userAllowsPrivateNotificationsInPublic(int userHandle) { 1462 if (userHandle == UserHandle.USER_ALL) { 1463 return true; 1464 } 1465 1466 if (mUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) { 1467 final boolean allowedByUser = 0 != Settings.Secure.getIntForUser( 1468 mContext.getContentResolver(), 1469 Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle); 1470 final boolean allowedByDpm = adminAllowsUnredactedNotifications(userHandle); 1471 final boolean allowed = allowedByUser && allowedByDpm; 1472 mUsersAllowingPrivateNotifications.append(userHandle, allowed); 1473 return allowed; 1474 } 1475 1476 return mUsersAllowingPrivateNotifications.get(userHandle); 1477 } 1478 1479 private boolean adminAllowsUnredactedNotifications(int userHandle) { 1480 if (userHandle == UserHandle.USER_ALL) { 1481 return true; 1482 } 1483 final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(null /* admin */, 1484 userHandle); 1485 return (dpmFlags & DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS) == 0; 1486 } 1487 1488 /** 1489 * Returns true if we're on a secure lockscreen and the user wants to hide notification data. 1490 * If so, notifications should be hidden. 1491 */ 1492 @Override // NotificationData.Environment 1493 public boolean shouldHideNotifications(int userid) { 1494 return isLockscreenPublicMode() && !userAllowsNotificationsInPublic(userid); 1495 } 1496 1497 /** 1498 * Returns true if we're on a secure lockscreen and the user wants to hide notifications via 1499 * package-specific override. 1500 */ 1501 @Override // NotificationDate.Environment 1502 public boolean shouldHideNotifications(String key) { 1503 return isLockscreenPublicMode() 1504 && mNotificationData.getVisibilityOverride(key) == Notification.VISIBILITY_SECRET; 1505 } 1506 1507 /** 1508 * Returns true if we're on a secure lockscreen. 1509 */ 1510 @Override // NotificationData.Environment 1511 public boolean onSecureLockScreen() { 1512 return isLockscreenPublicMode(); 1513 } 1514 1515 public void onNotificationClear(StatusBarNotification notification) { 1516 try { 1517 mBarService.onNotificationClear( 1518 notification.getPackageName(), 1519 notification.getTag(), 1520 notification.getId(), 1521 notification.getUserId()); 1522 } catch (android.os.RemoteException ex) { 1523 // oh well 1524 } 1525 } 1526 1527 /** 1528 * Called when the notification panel layouts 1529 */ 1530 public void onPanelLaidOut() { 1531 if (mState == StatusBarState.KEYGUARD) { 1532 // Since the number of notifications is determined based on the height of the view, we 1533 // need to update them. 1534 int maxBefore = getMaxKeyguardNotifications(false /* recompute */); 1535 int maxNotifications = getMaxKeyguardNotifications(true /* recompute */); 1536 if (maxBefore != maxNotifications) { 1537 updateRowStates(); 1538 } 1539 } 1540 } 1541 1542 protected void onLockedNotificationImportanceChange(OnDismissAction dismissAction) {} 1543 1544 protected void onLockedRemoteInput(ExpandableNotificationRow row, View clickedView) {} 1545 1546 protected void onLockedWorkRemoteInput(int userId, ExpandableNotificationRow row, 1547 View clicked) {} 1548 1549 @Override 1550 public void onExpandClicked(Entry clickedEntry, boolean nowExpanded) { 1551 } 1552 1553 protected class H extends Handler { 1554 public void handleMessage(Message m) { 1555 switch (m.what) { 1556 case MSG_SHOW_RECENT_APPS: 1557 showRecents(m.arg1 > 0, m.arg2 != 0); 1558 break; 1559 case MSG_HIDE_RECENT_APPS: 1560 hideRecents(m.arg1 > 0, m.arg2 > 0); 1561 break; 1562 case MSG_TOGGLE_RECENTS_APPS: 1563 toggleRecents(); 1564 break; 1565 case MSG_PRELOAD_RECENT_APPS: 1566 preloadRecents(); 1567 break; 1568 case MSG_CANCEL_PRELOAD_RECENT_APPS: 1569 cancelPreloadingRecents(); 1570 break; 1571 case MSG_SHOW_NEXT_AFFILIATED_TASK: 1572 showRecentsNextAffiliatedTask(); 1573 break; 1574 case MSG_SHOW_PREV_AFFILIATED_TASK: 1575 showRecentsPreviousAffiliatedTask(); 1576 break; 1577 case MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU: 1578 toggleKeyboardShortcuts(m.arg1); 1579 break; 1580 case MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU: 1581 dismissKeyboardShortcuts(); 1582 break; 1583 } 1584 } 1585 } 1586 1587 protected void workAroundBadLayerDrawableOpacity(View v) { 1588 } 1589 1590 protected boolean inflateViews(Entry entry, ViewGroup parent) { 1591 PackageManager pmUser = getPackageManagerForUser(mContext, 1592 entry.notification.getUser().getIdentifier()); 1593 1594 final StatusBarNotification sbn = entry.notification; 1595 try { 1596 entry.cacheContentViews(mContext, null); 1597 } catch (RuntimeException e) { 1598 Log.e(TAG, "Unable to get notification remote views", e); 1599 return false; 1600 } 1601 1602 final RemoteViews contentView = entry.cachedContentView; 1603 final RemoteViews bigContentView = entry.cachedBigContentView; 1604 final RemoteViews headsUpContentView = entry.cachedHeadsUpContentView; 1605 final RemoteViews publicContentView = entry.cachedPublicContentView; 1606 1607 if (contentView == null) { 1608 Log.v(TAG, "no contentView for: " + sbn.getNotification()); 1609 return false; 1610 } 1611 1612 if (DEBUG) { 1613 Log.v(TAG, "publicContentView: " + publicContentView); 1614 } 1615 1616 ExpandableNotificationRow row; 1617 1618 // Stash away previous user expansion state so we can restore it at 1619 // the end. 1620 boolean hasUserChangedExpansion = false; 1621 boolean userExpanded = false; 1622 boolean userLocked = false; 1623 1624 if (entry.row != null) { 1625 row = entry.row; 1626 hasUserChangedExpansion = row.hasUserChangedExpansion(); 1627 userExpanded = row.isUserExpanded(); 1628 userLocked = row.isUserLocked(); 1629 entry.reset(); 1630 if (hasUserChangedExpansion) { 1631 row.setUserExpanded(userExpanded); 1632 } 1633 } else { 1634 // create the row view 1635 LayoutInflater inflater = (LayoutInflater) mContext.getSystemService( 1636 Context.LAYOUT_INFLATER_SERVICE); 1637 row = (ExpandableNotificationRow) inflater.inflate(R.layout.status_bar_notification_row, 1638 parent, false); 1639 row.setExpansionLogger(this, entry.notification.getKey()); 1640 row.setGroupManager(mGroupManager); 1641 row.setHeadsUpManager(mHeadsUpManager); 1642 row.setRemoteInputController(mRemoteInputController); 1643 row.setOnExpandClickListener(this); 1644 1645 // Get the app name. 1646 // Note that Notification.Builder#bindHeaderAppName has similar logic 1647 // but since this field is used in the guts, it must be accurate. 1648 // Therefore we will only show the application label, or, failing that, the 1649 // package name. No substitutions. 1650 final String pkg = sbn.getPackageName(); 1651 String appname = pkg; 1652 try { 1653 final ApplicationInfo info = pmUser.getApplicationInfo(pkg, 1654 PackageManager.GET_UNINSTALLED_PACKAGES 1655 | PackageManager.GET_DISABLED_COMPONENTS); 1656 if (info != null) { 1657 appname = String.valueOf(pmUser.getApplicationLabel(info)); 1658 } 1659 } catch (NameNotFoundException e) { 1660 // Do nothing 1661 } 1662 row.setAppName(appname); 1663 } 1664 1665 workAroundBadLayerDrawableOpacity(row); 1666 bindDismissListener(row); 1667 1668 // NB: the large icon is now handled entirely by the template 1669 1670 // bind the click event to the content area 1671 NotificationContentView contentContainer = row.getPrivateLayout(); 1672 NotificationContentView contentContainerPublic = row.getPublicLayout(); 1673 1674 row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); 1675 if (ENABLE_REMOTE_INPUT) { 1676 row.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS); 1677 } 1678 1679 mNotificationClicker.register(row, sbn); 1680 1681 // set up the adaptive layout 1682 View contentViewLocal = null; 1683 View bigContentViewLocal = null; 1684 View headsUpContentViewLocal = null; 1685 View publicViewLocal = null; 1686 try { 1687 contentViewLocal = contentView.apply( 1688 sbn.getPackageContext(mContext), 1689 contentContainer, 1690 mOnClickHandler); 1691 if (bigContentView != null) { 1692 bigContentViewLocal = bigContentView.apply( 1693 sbn.getPackageContext(mContext), 1694 contentContainer, 1695 mOnClickHandler); 1696 } 1697 if (headsUpContentView != null) { 1698 headsUpContentViewLocal = headsUpContentView.apply( 1699 sbn.getPackageContext(mContext), 1700 contentContainer, 1701 mOnClickHandler); 1702 } 1703 if (publicContentView != null) { 1704 publicViewLocal = publicContentView.apply( 1705 sbn.getPackageContext(mContext), 1706 contentContainerPublic, mOnClickHandler); 1707 } 1708 1709 if (contentViewLocal != null) { 1710 contentViewLocal.setIsRootNamespace(true); 1711 contentContainer.setContractedChild(contentViewLocal); 1712 } 1713 if (bigContentViewLocal != null) { 1714 bigContentViewLocal.setIsRootNamespace(true); 1715 contentContainer.setExpandedChild(bigContentViewLocal); 1716 } 1717 if (headsUpContentViewLocal != null) { 1718 headsUpContentViewLocal.setIsRootNamespace(true); 1719 contentContainer.setHeadsUpChild(headsUpContentViewLocal); 1720 } 1721 if (publicViewLocal != null) { 1722 publicViewLocal.setIsRootNamespace(true); 1723 contentContainerPublic.setContractedChild(publicViewLocal); 1724 } 1725 } 1726 catch (RuntimeException e) { 1727 final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()); 1728 Log.e(TAG, "couldn't inflate view for notification " + ident, e); 1729 return false; 1730 } 1731 1732 // Extract target SDK version. 1733 try { 1734 ApplicationInfo info = pmUser.getApplicationInfo(sbn.getPackageName(), 0); 1735 entry.targetSdk = info.targetSdkVersion; 1736 } catch (NameNotFoundException ex) { 1737 Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex); 1738 } 1739 entry.autoRedacted = entry.notification.getNotification().publicVersion == null; 1740 1741 if (MULTIUSER_DEBUG) { 1742 TextView debug = (TextView) row.findViewById(R.id.debug_info); 1743 if (debug != null) { 1744 debug.setVisibility(View.VISIBLE); 1745 debug.setText("CU " + mCurrentUserId +" NU " + entry.notification.getUserId()); 1746 } 1747 } 1748 entry.row = row; 1749 entry.row.setOnActivatedListener(this); 1750 entry.row.setExpandable(bigContentViewLocal != null); 1751 1752 applyColorsAndBackgrounds(sbn, entry); 1753 1754 // Restore previous flags. 1755 if (hasUserChangedExpansion) { 1756 // Note: setUserExpanded() conveniently ignores calls with 1757 // userExpanded=true if !isExpandable(). 1758 row.setUserExpanded(userExpanded); 1759 } 1760 row.setUserLocked(userLocked); 1761 row.onNotificationUpdated(entry); 1762 return true; 1763 } 1764 1765 /** 1766 * Adds RemoteInput actions from the WearableExtender; to be removed once more apps support this 1767 * via first-class API. 1768 * 1769 * TODO: Remove once enough apps specify remote inputs on their own. 1770 */ 1771 private void processForRemoteInput(Notification n) { 1772 if (!ENABLE_REMOTE_INPUT) return; 1773 1774 if (n.extras != null && n.extras.containsKey("android.wearable.EXTENSIONS") && 1775 (n.actions == null || n.actions.length == 0)) { 1776 Notification.Action viableAction = null; 1777 Notification.WearableExtender we = new Notification.WearableExtender(n); 1778 1779 List<Notification.Action> actions = we.getActions(); 1780 final int numActions = actions.size(); 1781 1782 for (int i = 0; i < numActions; i++) { 1783 Notification.Action action = actions.get(i); 1784 if (action == null) { 1785 continue; 1786 } 1787 RemoteInput[] remoteInputs = action.getRemoteInputs(); 1788 if (remoteInputs == null) { 1789 continue; 1790 } 1791 for (RemoteInput ri : remoteInputs) { 1792 if (ri.getAllowFreeFormInput()) { 1793 viableAction = action; 1794 break; 1795 } 1796 } 1797 if (viableAction != null) { 1798 break; 1799 } 1800 } 1801 1802 if (viableAction != null) { 1803 Notification.Builder rebuilder = Notification.Builder.recoverBuilder(mContext, n); 1804 rebuilder.setActions(viableAction); 1805 rebuilder.build(); // will rewrite n 1806 } 1807 } 1808 } 1809 1810 public void startPendingIntentDismissingKeyguard(final PendingIntent intent) { 1811 if (!isDeviceProvisioned()) return; 1812 1813 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing(); 1814 final boolean afterKeyguardGone = intent.isActivity() 1815 && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(), 1816 mCurrentUserId); 1817 dismissKeyguardThenExecute(new OnDismissAction() { 1818 public boolean onDismiss() { 1819 new Thread() { 1820 @Override 1821 public void run() { 1822 try { 1823 if (keyguardShowing && !afterKeyguardGone) { 1824 ActivityManagerNative.getDefault() 1825 .keyguardWaitingForActivityDrawn(); 1826 } 1827 1828 // The intent we are sending is for the application, which 1829 // won't have permission to immediately start an activity after 1830 // the user switches to home. We know it is safe to do at this 1831 // point, so make sure new activity switches are now allowed. 1832 ActivityManagerNative.getDefault().resumeAppSwitches(); 1833 } catch (RemoteException e) { 1834 } 1835 try { 1836 intent.send(null, 0, null, null, null, null, getActivityOptions()); 1837 } catch (PendingIntent.CanceledException e) { 1838 // the stack trace isn't very helpful here. 1839 // Just log the exception message. 1840 Log.w(TAG, "Sending intent failed: " + e); 1841 1842 // TODO: Dismiss Keyguard. 1843 } 1844 if (intent.isActivity()) { 1845 mAssistManager.hideAssist(); 1846 overrideActivityPendingAppTransition(keyguardShowing 1847 && !afterKeyguardGone); 1848 } 1849 } 1850 }.start(); 1851 1852 // close the shade if it was open 1853 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, 1854 true /* force */, true /* delayed */); 1855 visibilityChanged(false); 1856 1857 return true; 1858 } 1859 }, afterKeyguardGone); 1860 } 1861 1862 public void addPostCollapseAction(Runnable r) { 1863 } 1864 1865 public boolean isCollapsing() { 1866 return false; 1867 } 1868 1869 private final class NotificationClicker implements View.OnClickListener { 1870 public void onClick(final View v) { 1871 if (!(v instanceof ExpandableNotificationRow)) { 1872 Log.e(TAG, "NotificationClicker called on a view that is not a notification row."); 1873 return; 1874 } 1875 1876 final ExpandableNotificationRow row = (ExpandableNotificationRow) v; 1877 final StatusBarNotification sbn = row.getStatusBarNotification(); 1878 if (sbn == null) { 1879 Log.e(TAG, "NotificationClicker called on an unclickable notification,"); 1880 return; 1881 } 1882 1883 // Check if the notification is displaying the gear, if so slide notification back 1884 if (row.getSettingsRow() != null && row.getSettingsRow().isVisible()) { 1885 row.animateTranslateNotification(0); 1886 return; 1887 } 1888 1889 Notification notification = sbn.getNotification(); 1890 final PendingIntent intent = notification.contentIntent != null 1891 ? notification.contentIntent 1892 : notification.fullScreenIntent; 1893 final String notificationKey = sbn.getKey(); 1894 1895 // Mark notification for one frame. 1896 row.setJustClicked(true); 1897 DejankUtils.postAfterTraversal(new Runnable() { 1898 @Override 1899 public void run() { 1900 row.setJustClicked(false); 1901 } 1902 }); 1903 1904 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing(); 1905 final boolean afterKeyguardGone = intent.isActivity() 1906 && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(), 1907 mCurrentUserId); 1908 dismissKeyguardThenExecute(new OnDismissAction() { 1909 public boolean onDismiss() { 1910 if (mHeadsUpManager != null && mHeadsUpManager.isHeadsUp(notificationKey)) { 1911 // Release the HUN notification to the shade. 1912 1913 if (isPanelFullyCollapsed()) { 1914 HeadsUpManager.setIsClickedNotification(row, true); 1915 } 1916 // 1917 // In most cases, when FLAG_AUTO_CANCEL is set, the notification will 1918 // become canceled shortly by NoMan, but we can't assume that. 1919 mHeadsUpManager.releaseImmediately(notificationKey); 1920 } 1921 StatusBarNotification parentToCancel = null; 1922 if (shouldAutoCancel(sbn) && mGroupManager.isOnlyChildInGroup(sbn)) { 1923 StatusBarNotification summarySbn = mGroupManager.getLogicalGroupSummary(sbn) 1924 .getStatusBarNotification(); 1925 if (shouldAutoCancel(summarySbn)) { 1926 parentToCancel = summarySbn; 1927 } 1928 } 1929 final StatusBarNotification parentToCancelFinal = parentToCancel; 1930 new Thread() { 1931 @Override 1932 public void run() { 1933 try { 1934 if (keyguardShowing && !afterKeyguardGone) { 1935 ActivityManagerNative.getDefault() 1936 .keyguardWaitingForActivityDrawn(); 1937 } 1938 1939 // The intent we are sending is for the application, which 1940 // won't have permission to immediately start an activity after 1941 // the user switches to home. We know it is safe to do at this 1942 // point, so make sure new activity switches are now allowed. 1943 ActivityManagerNative.getDefault().resumeAppSwitches(); 1944 } catch (RemoteException e) { 1945 } 1946 if (intent != null) { 1947 // If we are launching a work activity and require to launch 1948 // separate work challenge, we defer the activity action and cancel 1949 // notification until work challenge is unlocked. 1950 if (intent.isActivity()) { 1951 final int userId = intent.getCreatorUserHandle() 1952 .getIdentifier(); 1953 if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId) 1954 && mKeyguardManager.isDeviceLocked(userId)) { 1955 boolean canBypass = false; 1956 try { 1957 canBypass = ActivityManagerNative.getDefault() 1958 .canBypassWorkChallenge(intent); 1959 } catch (RemoteException e) { 1960 } 1961 // For direct-boot aware activities, they can be shown when 1962 // the device is still locked without triggering the work 1963 // challenge. 1964 if ((!canBypass) && startWorkChallengeIfNecessary(userId, 1965 intent.getIntentSender(), notificationKey)) { 1966 // Show work challenge, do not run PendingIntent and 1967 // remove notification 1968 return; 1969 } 1970 } 1971 } 1972 try { 1973 intent.send(null, 0, null, null, null, null, 1974 getActivityOptions()); 1975 } catch (PendingIntent.CanceledException e) { 1976 // the stack trace isn't very helpful here. 1977 // Just log the exception message. 1978 Log.w(TAG, "Sending contentIntent failed: " + e); 1979 1980 // TODO: Dismiss Keyguard. 1981 } 1982 if (intent.isActivity()) { 1983 mAssistManager.hideAssist(); 1984 overrideActivityPendingAppTransition(keyguardShowing 1985 && !afterKeyguardGone); 1986 } 1987 } 1988 1989 try { 1990 mBarService.onNotificationClick(notificationKey); 1991 } catch (RemoteException ex) { 1992 // system process is dead if we're here. 1993 } 1994 if (parentToCancelFinal != null) { 1995 // We have to post it to the UI thread for synchronization 1996 mHandler.post(new Runnable() { 1997 @Override 1998 public void run() { 1999 Runnable removeRunnable = new Runnable() { 2000 @Override 2001 public void run() { 2002 performRemoveNotification(parentToCancelFinal); 2003 } 2004 }; 2005 if (isCollapsing()) { 2006 // To avoid lags we're only performing the remove 2007 // after the shade was collapsed 2008 addPostCollapseAction(removeRunnable); 2009 } else { 2010 removeRunnable.run(); 2011 } 2012 } 2013 }); 2014 } 2015 } 2016 }.start(); 2017 2018 // close the shade if it was open 2019 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, 2020 true /* force */, true /* delayed */); 2021 visibilityChanged(false); 2022 2023 return true; 2024 } 2025 }, afterKeyguardGone); 2026 } 2027 2028 private boolean shouldAutoCancel(StatusBarNotification sbn) { 2029 int flags = sbn.getNotification().flags; 2030 if ((flags & Notification.FLAG_AUTO_CANCEL) != Notification.FLAG_AUTO_CANCEL) { 2031 return false; 2032 } 2033 if ((flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) { 2034 return false; 2035 } 2036 return true; 2037 } 2038 2039 public void register(ExpandableNotificationRow row, StatusBarNotification sbn) { 2040 Notification notification = sbn.getNotification(); 2041 if (notification.contentIntent != null || notification.fullScreenIntent != null) { 2042 row.setOnClickListener(this); 2043 } else { 2044 row.setOnClickListener(null); 2045 } 2046 } 2047 } 2048 2049 public void animateCollapsePanels(int flags, boolean force) { 2050 } 2051 2052 public void animateCollapsePanels(int flags, boolean force, boolean delayed) { 2053 } 2054 2055 public void overrideActivityPendingAppTransition(boolean keyguardShowing) { 2056 if (keyguardShowing) { 2057 try { 2058 mWindowManagerService.overridePendingAppTransition(null, 0, 0, null); 2059 } catch (RemoteException e) { 2060 Log.w(TAG, "Error overriding app transition: " + e); 2061 } 2062 } 2063 } 2064 2065 protected boolean startWorkChallengeIfNecessary(int userId, IntentSender intendSender, 2066 String notificationKey) { 2067 final Intent newIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null, 2068 null, userId); 2069 if (newIntent == null) { 2070 return false; 2071 } 2072 final Intent callBackIntent = new Intent( 2073 WORK_CHALLENGE_UNLOCKED_NOTIFICATION_ACTION); 2074 callBackIntent.putExtra(Intent.EXTRA_INTENT, intendSender); 2075 callBackIntent.putExtra(Intent.EXTRA_INDEX, notificationKey); 2076 callBackIntent.setPackage(mContext.getPackageName()); 2077 2078 PendingIntent callBackPendingIntent = PendingIntent.getBroadcast( 2079 mContext, 2080 0, 2081 callBackIntent, 2082 PendingIntent.FLAG_CANCEL_CURRENT | 2083 PendingIntent.FLAG_ONE_SHOT | 2084 PendingIntent.FLAG_IMMUTABLE); 2085 newIntent.putExtra( 2086 Intent.EXTRA_INTENT, 2087 callBackPendingIntent.getIntentSender()); 2088 try { 2089 ActivityManagerNative.getDefault().startConfirmDeviceCredentialIntent(newIntent); 2090 } catch (RemoteException ex) { 2091 // ignore 2092 } 2093 return true; 2094 } 2095 2096 protected Bundle getActivityOptions() { 2097 // Anything launched from the notification shade should always go into the 2098 // fullscreen stack. 2099 ActivityOptions options = ActivityOptions.makeBasic(); 2100 options.setLaunchStackId(StackId.FULLSCREEN_WORKSPACE_STACK_ID); 2101 return options.toBundle(); 2102 } 2103 2104 protected void visibilityChanged(boolean visible) { 2105 if (mVisible != visible) { 2106 mVisible = visible; 2107 if (!visible) { 2108 dismissPopups(); 2109 } 2110 } 2111 updateVisibleToUser(); 2112 } 2113 2114 protected void updateVisibleToUser() { 2115 boolean oldVisibleToUser = mVisibleToUser; 2116 mVisibleToUser = mVisible && mDeviceInteractive; 2117 2118 if (oldVisibleToUser != mVisibleToUser) { 2119 handleVisibleToUserChanged(mVisibleToUser); 2120 } 2121 } 2122 2123 /** 2124 * The LEDs are turned off when the notification panel is shown, even just a little bit. 2125 * See also PhoneStatusBar.setPanelExpanded for another place where we attempt to do this. 2126 */ 2127 protected void handleVisibleToUserChanged(boolean visibleToUser) { 2128 try { 2129 if (visibleToUser) { 2130 boolean pinnedHeadsUp = mHeadsUpManager.hasPinnedHeadsUp(); 2131 boolean clearNotificationEffects = 2132 !isPanelFullyCollapsed() && 2133 (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED); 2134 int notificationLoad = mNotificationData.getActiveNotifications().size(); 2135 if (pinnedHeadsUp && isPanelFullyCollapsed()) { 2136 notificationLoad = 1; 2137 } else { 2138 MetricsLogger.histogram(mContext, "note_load", notificationLoad); 2139 } 2140 mBarService.onPanelRevealed(clearNotificationEffects, notificationLoad); 2141 } else { 2142 mBarService.onPanelHidden(); 2143 } 2144 } catch (RemoteException ex) { 2145 // Won't fail unless the world has ended. 2146 } 2147 } 2148 2149 /** 2150 * Clear Buzz/Beep/Blink. 2151 */ 2152 public void clearNotificationEffects() { 2153 try { 2154 mBarService.clearNotificationEffects(); 2155 } catch (RemoteException e) { 2156 // Won't fail unless the world has ended. 2157 } 2158 } 2159 2160 public abstract boolean isPanelFullyCollapsed(); 2161 2162 /** 2163 * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService 2164 * about the failure. 2165 * 2166 * WARNING: this will call back into us. Don't hold any locks. 2167 */ 2168 void handleNotificationError(StatusBarNotification n, String message) { 2169 removeNotification(n.getKey(), null); 2170 try { 2171 mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(), 2172 n.getInitialPid(), message, n.getUserId()); 2173 } catch (RemoteException ex) { 2174 // The end is nigh. 2175 } 2176 } 2177 2178 protected StatusBarNotification removeNotificationViews(String key, RankingMap ranking) { 2179 NotificationData.Entry entry = mNotificationData.remove(key, ranking); 2180 if (entry == null) { 2181 Log.w(TAG, "removeNotification for unknown key: " + key); 2182 return null; 2183 } 2184 updateNotifications(); 2185 return entry.notification; 2186 } 2187 2188 protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn) { 2189 if (DEBUG) { 2190 Log.d(TAG, "createNotificationViews(notification=" + sbn); 2191 } 2192 final StatusBarIconView iconView = createIcon(sbn); 2193 if (iconView == null) { 2194 return null; 2195 } 2196 2197 // Construct the expanded view. 2198 NotificationData.Entry entry = new NotificationData.Entry(sbn, iconView); 2199 if (!inflateViews(entry, mStackScroller)) { 2200 handleNotificationError(sbn, "Couldn't expand RemoteViews for: " + sbn); 2201 return null; 2202 } 2203 return entry; 2204 } 2205 2206 public StatusBarIconView createIcon(StatusBarNotification sbn) { 2207 // Construct the icon. 2208 Notification n = sbn.getNotification(); 2209 final StatusBarIconView iconView = new StatusBarIconView(mContext, 2210 sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), n); 2211 iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE); 2212 2213 final Icon smallIcon = n.getSmallIcon(); 2214 if (smallIcon == null) { 2215 handleNotificationError(sbn, 2216 "No small icon in notification from " + sbn.getPackageName()); 2217 return null; 2218 } 2219 final StatusBarIcon ic = new StatusBarIcon( 2220 sbn.getUser(), 2221 sbn.getPackageName(), 2222 smallIcon, 2223 n.iconLevel, 2224 n.number, 2225 StatusBarIconView.contentDescForNotification(mContext, n)); 2226 if (!iconView.set(ic)) { 2227 handleNotificationError(sbn, "Couldn't create icon: " + ic); 2228 return null; 2229 } 2230 return iconView; 2231 } 2232 2233 protected void addNotificationViews(Entry entry, RankingMap ranking) { 2234 if (entry == null) { 2235 return; 2236 } 2237 // Add the expanded view and icon. 2238 mNotificationData.add(entry, ranking); 2239 updateNotifications(); 2240 } 2241 2242 /** 2243 * @param recompute wheter the number should be recomputed 2244 * @return The number of notifications we show on Keyguard. 2245 */ 2246 protected abstract int getMaxKeyguardNotifications(boolean recompute); 2247 2248 /** 2249 * Updates expanded, dimmed and locked states of notification rows. 2250 */ 2251 protected void updateRowStates() { 2252 mKeyguardIconOverflowContainer.getIconsView().removeAllViews(); 2253 final int N = mStackScroller.getChildCount(); 2254 2255 2256 int visibleNotifications = 0; 2257 boolean onKeyguard = mState == StatusBarState.KEYGUARD; 2258 int maxNotifications = 0; 2259 if (onKeyguard) { 2260 maxNotifications = getMaxKeyguardNotifications(true /* recompute */); 2261 } 2262 Stack<ExpandableNotificationRow> stack = new Stack<>(); 2263 for (int i = N - 1; i >= 0; i--) { 2264 View child = mStackScroller.getChildAt(i); 2265 if (!(child instanceof ExpandableNotificationRow)) { 2266 continue; 2267 } 2268 stack.push((ExpandableNotificationRow) child); 2269 } 2270 while(!stack.isEmpty()) { 2271 ExpandableNotificationRow row = stack.pop(); 2272 NotificationData.Entry entry = row.getEntry(); 2273 boolean childNotification = mGroupManager.isChildInGroupWithSummary(entry.notification); 2274 if (onKeyguard) { 2275 row.setOnKeyguard(true); 2276 } else { 2277 row.setOnKeyguard(false); 2278 row.setSystemExpanded(visibleNotifications == 0 && !childNotification); 2279 } 2280 boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup( 2281 entry.notification) && !entry.row.isRemoved(); 2282 boolean childWithVisibleSummary = childNotification 2283 && mGroupManager.getGroupSummary(entry.notification).getVisibility() 2284 == View.VISIBLE; 2285 boolean showOnKeyguard = shouldShowOnKeyguard(entry.notification); 2286 if (suppressedSummary || (isLockscreenPublicMode() && !mShowLockscreenNotifications) || 2287 (onKeyguard && !childWithVisibleSummary 2288 && (visibleNotifications >= maxNotifications || !showOnKeyguard))) { 2289 entry.row.setVisibility(View.GONE); 2290 if (onKeyguard && showOnKeyguard && !childNotification && !suppressedSummary) { 2291 mKeyguardIconOverflowContainer.getIconsView().addNotification(entry); 2292 } 2293 } else { 2294 boolean wasGone = entry.row.getVisibility() == View.GONE; 2295 if (wasGone) { 2296 entry.row.setVisibility(View.VISIBLE); 2297 } 2298 if (!childNotification && !entry.row.isRemoved()) { 2299 if (wasGone) { 2300 // notify the scroller of a child addition 2301 mStackScroller.generateAddAnimation(entry.row, 2302 !showOnKeyguard /* fromMoreCard */); 2303 } 2304 visibleNotifications++; 2305 } 2306 } 2307 if (row.isSummaryWithChildren()) { 2308 List<ExpandableNotificationRow> notificationChildren = 2309 row.getNotificationChildren(); 2310 int size = notificationChildren.size(); 2311 for (int i = size - 1; i >= 0; i--) { 2312 stack.push(notificationChildren.get(i)); 2313 } 2314 } 2315 } 2316 2317 mStackScroller.updateOverflowContainerVisibility(onKeyguard 2318 && mKeyguardIconOverflowContainer.getIconsView().getChildCount() > 0); 2319 2320 mStackScroller.changeViewPosition(mDismissView, mStackScroller.getChildCount() - 1); 2321 mStackScroller.changeViewPosition(mEmptyShadeView, mStackScroller.getChildCount() - 2); 2322 mStackScroller.changeViewPosition(mKeyguardIconOverflowContainer, 2323 mStackScroller.getChildCount() - 3); 2324 } 2325 2326 public boolean shouldShowOnKeyguard(StatusBarNotification sbn) { 2327 return mShowLockscreenNotifications && !mNotificationData.isAmbient(sbn.getKey()); 2328 } 2329 2330 protected void setZenMode(int mode) { 2331 if (!isDeviceProvisioned()) return; 2332 mZenMode = mode; 2333 updateNotifications(); 2334 } 2335 2336 // extended in PhoneStatusBar 2337 protected void setShowLockscreenNotifications(boolean show) { 2338 mShowLockscreenNotifications = show; 2339 } 2340 2341 protected void setLockScreenAllowRemoteInput(boolean allowLockscreenRemoteInput) { 2342 mAllowLockscreenRemoteInput = allowLockscreenRemoteInput; 2343 } 2344 2345 private void updateLockscreenNotificationSetting() { 2346 final boolean show = Settings.Secure.getIntForUser(mContext.getContentResolver(), 2347 Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 2348 1, 2349 mCurrentUserId) != 0; 2350 final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures( 2351 null /* admin */, mCurrentUserId); 2352 final boolean allowedByDpm = (dpmFlags 2353 & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0; 2354 2355 setShowLockscreenNotifications(show && allowedByDpm); 2356 2357 if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) { 2358 final boolean remoteInput = Settings.Secure.getIntForUser(mContext.getContentResolver(), 2359 Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT, 2360 0, 2361 mCurrentUserId) != 0; 2362 final boolean remoteInputDpm = 2363 (dpmFlags & DevicePolicyManager.KEYGUARD_DISABLE_REMOTE_INPUT) == 0; 2364 2365 setLockScreenAllowRemoteInput(remoteInput && remoteInputDpm); 2366 } else { 2367 setLockScreenAllowRemoteInput(false); 2368 } 2369 } 2370 2371 protected abstract void setAreThereNotifications(); 2372 protected abstract void updateNotifications(); 2373 public abstract boolean shouldDisableNavbarGestures(); 2374 2375 public abstract void addNotification(StatusBarNotification notification, 2376 RankingMap ranking, Entry oldEntry); 2377 protected abstract void updateNotificationRanking(RankingMap ranking); 2378 public abstract void removeNotification(String key, RankingMap ranking); 2379 2380 public void updateNotification(StatusBarNotification notification, RankingMap ranking) { 2381 if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")"); 2382 2383 final String key = notification.getKey(); 2384 Entry entry = mNotificationData.get(key); 2385 if (entry == null) { 2386 return; 2387 } else { 2388 mHeadsUpEntriesToRemoveOnSwitch.remove(entry); 2389 mRemoteInputEntriesToRemoveOnCollapse.remove(entry); 2390 } 2391 2392 Notification n = notification.getNotification(); 2393 mNotificationData.updateRanking(ranking); 2394 2395 boolean applyInPlace; 2396 try { 2397 applyInPlace = entry.cacheContentViews(mContext, notification.getNotification()); 2398 } catch (RuntimeException e) { 2399 Log.e(TAG, "Unable to get notification remote views", e); 2400 applyInPlace = false; 2401 } 2402 boolean shouldPeek = shouldPeek(entry, notification); 2403 boolean alertAgain = alertAgain(entry, n); 2404 if (DEBUG) { 2405 Log.d(TAG, "applyInPlace=" + applyInPlace 2406 + " shouldPeek=" + shouldPeek 2407 + " alertAgain=" + alertAgain); 2408 } 2409 2410 final StatusBarNotification oldNotification = entry.notification; 2411 entry.notification = notification; 2412 mGroupManager.onEntryUpdated(entry, oldNotification); 2413 2414 boolean updateSuccessful = false; 2415 if (applyInPlace) { 2416 if (DEBUG) Log.d(TAG, "reusing notification for key: " + key); 2417 try { 2418 if (entry.icon != null) { 2419 // Update the icon 2420 final StatusBarIcon ic = new StatusBarIcon( 2421 notification.getUser(), 2422 notification.getPackageName(), 2423 n.getSmallIcon(), 2424 n.iconLevel, 2425 n.number, 2426 StatusBarIconView.contentDescForNotification(mContext, n)); 2427 entry.icon.setNotification(n); 2428 if (!entry.icon.set(ic)) { 2429 handleNotificationError(notification, "Couldn't update icon: " + ic); 2430 return; 2431 } 2432 } 2433 updateNotificationViews(entry, notification); 2434 updateSuccessful = true; 2435 } 2436 catch (RuntimeException e) { 2437 // It failed to apply cleanly. 2438 Log.w(TAG, "Couldn't reapply views for package " + 2439 notification.getPackageName(), e); 2440 } 2441 } 2442 if (!updateSuccessful) { 2443 if (DEBUG) Log.d(TAG, "not reusing notification for key: " + key); 2444 final StatusBarIcon ic = new StatusBarIcon( 2445 notification.getUser(), 2446 notification.getPackageName(), 2447 n.getSmallIcon(), 2448 n.iconLevel, 2449 n.number, 2450 StatusBarIconView.contentDescForNotification(mContext, n)); 2451 entry.icon.setNotification(n); 2452 entry.icon.set(ic); 2453 if (!inflateViews(entry, mStackScroller)) { 2454 handleNotificationError(notification, "Couldn't update remote views for: " 2455 + notification); 2456 } 2457 } 2458 updateHeadsUp(key, entry, shouldPeek, alertAgain); 2459 updateNotifications(); 2460 2461 if (!notification.isClearable()) { 2462 // The user may have performed a dismiss action on the notification, since it's 2463 // not clearable we should snap it back. 2464 mStackScroller.snapViewIfNeeded(entry.row); 2465 } 2466 2467 if (DEBUG) { 2468 // Is this for you? 2469 boolean isForCurrentUser = isNotificationForCurrentProfiles(notification); 2470 Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you"); 2471 } 2472 2473 setAreThereNotifications(); 2474 } 2475 2476 protected abstract void updateHeadsUp(String key, Entry entry, boolean shouldPeek, 2477 boolean alertAgain); 2478 2479 private void updateNotificationViews(Entry entry, StatusBarNotification sbn) { 2480 final RemoteViews contentView = entry.cachedContentView; 2481 final RemoteViews bigContentView = entry.cachedBigContentView; 2482 final RemoteViews headsUpContentView = entry.cachedHeadsUpContentView; 2483 final RemoteViews publicContentView = entry.cachedPublicContentView; 2484 2485 // Reapply the RemoteViews 2486 contentView.reapply(mContext, entry.getContentView(), mOnClickHandler); 2487 if (bigContentView != null && entry.getExpandedContentView() != null) { 2488 bigContentView.reapply(sbn.getPackageContext(mContext), 2489 entry.getExpandedContentView(), 2490 mOnClickHandler); 2491 } 2492 View headsUpChild = entry.getHeadsUpContentView(); 2493 if (headsUpContentView != null && headsUpChild != null) { 2494 headsUpContentView.reapply(sbn.getPackageContext(mContext), 2495 headsUpChild, mOnClickHandler); 2496 } 2497 if (publicContentView != null && entry.getPublicContentView() != null) { 2498 publicContentView.reapply(sbn.getPackageContext(mContext), 2499 entry.getPublicContentView(), mOnClickHandler); 2500 } 2501 // update the contentIntent 2502 mNotificationClicker.register(entry.row, sbn); 2503 2504 entry.row.onNotificationUpdated(entry); 2505 entry.row.resetHeight(); 2506 } 2507 2508 protected void updatePublicContentView(Entry entry, 2509 StatusBarNotification sbn) { 2510 final RemoteViews publicContentView = entry.cachedPublicContentView; 2511 View inflatedView = entry.getPublicContentView(); 2512 if (entry.autoRedacted && publicContentView != null && inflatedView != null) { 2513 final boolean disabledByPolicy = 2514 !adminAllowsUnredactedNotifications(entry.notification.getUserId()); 2515 String notificationHiddenText = mContext.getString(disabledByPolicy 2516 ? com.android.internal.R.string.notification_hidden_by_policy_text 2517 : com.android.internal.R.string.notification_hidden_text); 2518 TextView titleView = (TextView) inflatedView.findViewById(android.R.id.title); 2519 if (titleView != null 2520 && !titleView.getText().toString().equals(notificationHiddenText)) { 2521 publicContentView.setTextViewText(android.R.id.title, notificationHiddenText); 2522 publicContentView.reapply(sbn.getPackageContext(mContext), 2523 inflatedView, mOnClickHandler); 2524 entry.row.onNotificationUpdated(entry); 2525 } 2526 } 2527 } 2528 2529 protected void notifyHeadsUpScreenOff() { 2530 maybeEscalateHeadsUp(); 2531 } 2532 2533 private boolean alertAgain(Entry oldEntry, Notification newNotification) { 2534 return oldEntry == null || !oldEntry.hasInterrupted() 2535 || (newNotification.flags & Notification.FLAG_ONLY_ALERT_ONCE) == 0; 2536 } 2537 2538 protected boolean shouldPeek(Entry entry) { 2539 return shouldPeek(entry, entry.notification); 2540 } 2541 2542 protected boolean shouldPeek(Entry entry, StatusBarNotification sbn) { 2543 if (!mUseHeadsUp || isDeviceInVrMode()) { 2544 return false; 2545 } 2546 2547 if (mNotificationData.shouldFilterOut(sbn)) { 2548 if (DEBUG) Log.d(TAG, "No peeking: filtered notification: " + sbn.getKey()); 2549 return false; 2550 } 2551 2552 boolean inUse = mPowerManager.isScreenOn(); 2553 try { 2554 inUse = inUse && !mDreamManager.isDreaming(); 2555 } catch (RemoteException e) { 2556 Log.d(TAG, "failed to query dream manager", e); 2557 } 2558 2559 if (!inUse) { 2560 if (DEBUG) { 2561 Log.d(TAG, "No peeking: not in use: " + sbn.getKey()); 2562 } 2563 return false; 2564 } 2565 2566 if (mNotificationData.shouldSuppressScreenOn(sbn.getKey())) { 2567 if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey()); 2568 return false; 2569 } 2570 2571 if (entry.hasJustLaunchedFullScreenIntent()) { 2572 if (DEBUG) Log.d(TAG, "No peeking: recent fullscreen: " + sbn.getKey()); 2573 return false; 2574 } 2575 2576 if (isSnoozedPackage(sbn)) { 2577 if (DEBUG) Log.d(TAG, "No peeking: snoozed package: " + sbn.getKey()); 2578 return false; 2579 } 2580 2581 if (mNotificationData.getImportance(sbn.getKey()) < IMPORTANCE_HIGH) { 2582 if (DEBUG) Log.d(TAG, "No peeking: unimportant notification: " + sbn.getKey()); 2583 return false; 2584 } 2585 2586 if (sbn.getNotification().fullScreenIntent != null) { 2587 if (mAccessibilityManager.isTouchExplorationEnabled()) { 2588 if (DEBUG) Log.d(TAG, "No peeking: accessible fullscreen: " + sbn.getKey()); 2589 return false; 2590 } else { 2591 // we only allow head-up on the lockscreen if it doesn't have a fullscreen intent 2592 return !mStatusBarKeyguardViewManager.isShowing() 2593 || mStatusBarKeyguardViewManager.isOccluded(); 2594 } 2595 } 2596 2597 return true; 2598 } 2599 2600 protected abstract boolean isSnoozedPackage(StatusBarNotification sbn); 2601 2602 public void setInteracting(int barWindow, boolean interacting) { 2603 // hook for subclasses 2604 } 2605 2606 public void setBouncerShowing(boolean bouncerShowing) { 2607 mBouncerShowing = bouncerShowing; 2608 } 2609 2610 /** 2611 * @return Whether the security bouncer from Keyguard is showing. 2612 */ 2613 public boolean isBouncerShowing() { 2614 return mBouncerShowing; 2615 } 2616 2617 public void destroy() { 2618 mContext.unregisterReceiver(mBroadcastReceiver); 2619 try { 2620 mNotificationListener.unregisterAsSystemService(); 2621 } catch (RemoteException e) { 2622 // Ignore. 2623 } 2624 } 2625 2626 /** 2627 * @return a PackageManger for userId or if userId is < 0 (USER_ALL etc) then 2628 * return PackageManager for mContext 2629 */ 2630 public static PackageManager getPackageManagerForUser(Context context, int userId) { 2631 Context contextForUser = context; 2632 // UserHandle defines special userId as negative values, e.g. USER_ALL 2633 if (userId >= 0) { 2634 try { 2635 // Create a context for the correct user so if a package isn't installed 2636 // for user 0 we can still load information about the package. 2637 contextForUser = 2638 context.createPackageContextAsUser(context.getPackageName(), 2639 Context.CONTEXT_RESTRICTED, 2640 new UserHandle(userId)); 2641 } catch (NameNotFoundException e) { 2642 // Shouldn't fail to find the package name for system ui. 2643 } 2644 } 2645 return contextForUser.getPackageManager(); 2646 } 2647 2648 @Override 2649 public void logNotificationExpansion(String key, boolean userAction, boolean expanded) { 2650 try { 2651 mBarService.onNotificationExpansionChanged(key, userAction, expanded); 2652 } catch (RemoteException e) { 2653 // Ignore. 2654 } 2655 } 2656 2657 public boolean isKeyguardSecure() { 2658 if (mStatusBarKeyguardViewManager == null) { 2659 // startKeyguard() hasn't been called yet, so we don't know. 2660 // Make sure anything that needs to know isKeyguardSecure() checks and re-checks this 2661 // value onVisibilityChanged(). 2662 Slog.w(TAG, "isKeyguardSecure() called before startKeyguard(), returning false", 2663 new Throwable()); 2664 return false; 2665 } 2666 return mStatusBarKeyguardViewManager.isSecure(); 2667 } 2668 2669 @Override 2670 public void showAssistDisclosure() { 2671 if (mAssistManager != null) { 2672 mAssistManager.showDisclosure(); 2673 } 2674 } 2675 2676 @Override 2677 public void startAssist(Bundle args) { 2678 if (mAssistManager != null) { 2679 mAssistManager.startAssist(args); 2680 } 2681 } 2682 } 2683