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