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