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