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.animation.TimeInterpolator; 22 import android.app.ActivityManager; 23 import android.app.ActivityManagerNative; 24 import android.app.Notification; 25 import android.app.NotificationManager; 26 import android.app.PendingIntent; 27 import android.app.TaskStackBuilder; 28 import android.app.admin.DevicePolicyManager; 29 import android.content.BroadcastReceiver; 30 import android.content.ComponentName; 31 import android.content.Context; 32 import android.content.Intent; 33 import android.content.IntentFilter; 34 import android.content.pm.ApplicationInfo; 35 import android.content.pm.PackageManager; 36 import android.content.pm.PackageManager.NameNotFoundException; 37 import android.content.pm.ResolveInfo; 38 import android.content.pm.UserInfo; 39 import android.content.res.Configuration; 40 import android.content.res.Resources; 41 import android.database.ContentObserver; 42 import android.graphics.PorterDuff; 43 import android.graphics.drawable.Drawable; 44 import android.graphics.drawable.Icon; 45 import android.os.AsyncTask; 46 import android.os.Build; 47 import android.os.Bundle; 48 import android.os.Handler; 49 import android.os.IBinder; 50 import android.os.Message; 51 import android.os.PowerManager; 52 import android.os.RemoteException; 53 import android.os.ServiceManager; 54 import android.os.SystemProperties; 55 import android.os.UserHandle; 56 import android.os.UserManager; 57 import android.provider.Settings; 58 import android.service.dreams.DreamService; 59 import android.service.dreams.IDreamManager; 60 import android.service.notification.NotificationListenerService; 61 import android.service.notification.NotificationListenerService.RankingMap; 62 import android.service.notification.StatusBarNotification; 63 import android.text.TextUtils; 64 import android.util.Log; 65 import android.util.Slog; 66 import android.util.SparseArray; 67 import android.util.SparseBooleanArray; 68 import android.view.Display; 69 import android.view.IWindowManager; 70 import android.view.LayoutInflater; 71 import android.view.MotionEvent; 72 import android.view.View; 73 import android.view.ViewAnimationUtils; 74 import android.view.ViewGroup; 75 import android.view.ViewParent; 76 import android.view.WindowManager; 77 import android.view.WindowManagerGlobal; 78 import android.view.accessibility.AccessibilityManager; 79 import android.view.animation.AnimationUtils; 80 import android.widget.DateTimeView; 81 import android.widget.ImageView; 82 import android.widget.RemoteViews; 83 import android.widget.TextView; 84 import android.widget.Toast; 85 86 import com.android.internal.logging.MetricsLogger; 87 import com.android.internal.statusbar.IStatusBarService; 88 import com.android.internal.statusbar.StatusBarIcon; 89 import com.android.internal.statusbar.StatusBarIconList; 90 import com.android.internal.util.NotificationColorUtil; 91 import com.android.internal.widget.LockPatternUtils; 92 import com.android.keyguard.KeyguardUpdateMonitor; 93 import com.android.systemui.DejankUtils; 94 import com.android.systemui.R; 95 import com.android.systemui.RecentsComponent; 96 import com.android.systemui.SwipeHelper; 97 import com.android.systemui.SystemUI; 98 import com.android.systemui.assist.AssistManager; 99 import com.android.systemui.recents.Recents; 100 import com.android.systemui.statusbar.NotificationData.Entry; 101 import com.android.systemui.statusbar.phone.NavigationBarView; 102 import com.android.systemui.statusbar.phone.NotificationGroupManager; 103 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; 104 import com.android.systemui.statusbar.policy.HeadsUpManager; 105 import com.android.systemui.statusbar.policy.PreviewInflater; 106 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; 107 108 import java.util.ArrayList; 109 import java.util.List; 110 import java.util.Locale; 111 112 import static com.android.keyguard.KeyguardHostView.OnDismissAction; 113 114 public abstract class BaseStatusBar extends SystemUI implements 115 CommandQueue.Callbacks, ActivatableNotificationView.OnActivatedListener, 116 RecentsComponent.Callbacks, ExpandableNotificationRow.ExpansionLogger, 117 NotificationData.Environment { 118 public static final String TAG = "StatusBar"; 119 public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 120 public static final boolean MULTIUSER_DEBUG = false; 121 122 // STOPSHIP disable once we resolve b/18102199 123 private static final boolean NOTIFICATION_CLICK_DEBUG = true; 124 125 public static final boolean ENABLE_CHILD_NOTIFICATIONS = Build.IS_DEBUGGABLE 126 && SystemProperties.getBoolean("debug.child_notifs", false); 127 128 protected static final int MSG_SHOW_RECENT_APPS = 1019; 129 protected static final int MSG_HIDE_RECENT_APPS = 1020; 130 protected static final int MSG_TOGGLE_RECENTS_APPS = 1021; 131 protected static final int MSG_PRELOAD_RECENT_APPS = 1022; 132 protected static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 1023; 133 protected static final int MSG_SHOW_NEXT_AFFILIATED_TASK = 1024; 134 protected static final int MSG_SHOW_PREV_AFFILIATED_TASK = 1025; 135 136 protected static final boolean ENABLE_HEADS_UP = true; 137 // scores above this threshold should be displayed in heads up mode. 138 protected static final int INTERRUPTION_THRESHOLD = 10; 139 protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up"; 140 141 // Should match the value in PhoneWindowManager 142 public static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps"; 143 144 private static final String BANNER_ACTION_CANCEL = 145 "com.android.systemui.statusbar.banner_action_cancel"; 146 private static final String BANNER_ACTION_SETUP = 147 "com.android.systemui.statusbar.banner_action_setup"; 148 149 protected CommandQueue mCommandQueue; 150 protected IStatusBarService mBarService; 151 protected H mHandler = createHandler(); 152 153 // all notifications 154 protected NotificationData mNotificationData; 155 protected NotificationStackScrollLayout mStackScroller; 156 157 protected NotificationGroupManager mGroupManager = new NotificationGroupManager(); 158 159 // for heads up notifications 160 protected HeadsUpManager mHeadsUpManager; 161 162 protected int mCurrentUserId = 0; 163 final protected SparseArray<UserInfo> mCurrentProfiles = new SparseArray<UserInfo>(); 164 165 protected int mLayoutDirection = -1; // invalid 166 protected AccessibilityManager mAccessibilityManager; 167 168 // on-screen navigation buttons 169 protected NavigationBarView mNavigationBarView = null; 170 171 protected boolean mDeviceInteractive; 172 173 protected boolean mVisible; 174 175 // mScreenOnFromKeyguard && mVisible. 176 private boolean mVisibleToUser; 177 178 private Locale mLocale; 179 private float mFontScale; 180 181 protected boolean mUseHeadsUp = false; 182 protected boolean mHeadsUpTicker = false; 183 protected boolean mDisableNotificationAlerts = false; 184 185 protected DevicePolicyManager mDevicePolicyManager; 186 protected IDreamManager mDreamManager; 187 PowerManager mPowerManager; 188 protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; 189 protected int mRowMinHeight; 190 protected int mRowMaxHeight; 191 192 // public mode, private notifications, etc 193 private boolean mLockscreenPublicMode = false; 194 private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray(); 195 private NotificationColorUtil mNotificationColorUtil; 196 197 private UserManager mUserManager; 198 199 // UI-specific methods 200 201 /** 202 * Create all windows necessary for the status bar (including navigation, overlay panels, etc) 203 * and add them to the window manager. 204 */ createAndAddWindows()205 protected abstract void createAndAddWindows(); 206 207 protected WindowManager mWindowManager; 208 protected IWindowManager mWindowManagerService; 209 refreshLayout(int layoutDirection)210 protected abstract void refreshLayout(int layoutDirection); 211 212 protected Display mDisplay; 213 214 private boolean mDeviceProvisioned = false; 215 216 private RecentsComponent mRecents; 217 218 protected int mZenMode; 219 220 // which notification is currently being longpress-examined by the user 221 private NotificationGuts mNotificationGutsExposed; 222 223 private TimeInterpolator mLinearOutSlowIn, mFastOutLinearIn; 224 225 /** 226 * The {@link StatusBarState} of the status bar. 227 */ 228 protected int mState; 229 protected boolean mBouncerShowing; 230 protected boolean mShowLockscreenNotifications; 231 232 protected NotificationOverflowContainer mKeyguardIconOverflowContainer; 233 protected DismissView mDismissView; 234 protected EmptyShadeView mEmptyShadeView; 235 236 private NotificationClicker mNotificationClicker = new NotificationClicker(); 237 238 protected AssistManager mAssistManager; 239 240 @Override // NotificationData.Environment isDeviceProvisioned()241 public boolean isDeviceProvisioned() { 242 return mDeviceProvisioned; 243 } 244 245 protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) { 246 @Override 247 public void onChange(boolean selfChange) { 248 final boolean provisioned = 0 != Settings.Global.getInt( 249 mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0); 250 if (provisioned != mDeviceProvisioned) { 251 mDeviceProvisioned = provisioned; 252 updateNotifications(); 253 } 254 final int mode = Settings.Global.getInt(mContext.getContentResolver(), 255 Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF); 256 setZenMode(mode); 257 258 updateLockscreenNotificationSetting(); 259 } 260 }; 261 262 private final ContentObserver mLockscreenSettingsObserver = new ContentObserver(mHandler) { 263 @Override 264 public void onChange(boolean selfChange) { 265 // We don't know which user changed LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 266 // so we just dump our cache ... 267 mUsersAllowingPrivateNotifications.clear(); 268 // ... and refresh all the notifications 269 updateNotifications(); 270 } 271 }; 272 273 private RemoteViews.OnClickHandler mOnClickHandler = new RemoteViews.OnClickHandler() { 274 @Override 275 public boolean onClickHandler( 276 final View view, final PendingIntent pendingIntent, final Intent fillInIntent) { 277 if (DEBUG) { 278 Log.v(TAG, "Notification click handler invoked for intent: " + pendingIntent); 279 } 280 logActionClick(view); 281 // The intent we are sending is for the application, which 282 // won't have permission to immediately start an activity after 283 // the user switches to home. We know it is safe to do at this 284 // point, so make sure new activity switches are now allowed. 285 try { 286 ActivityManagerNative.getDefault().resumeAppSwitches(); 287 } catch (RemoteException e) { 288 } 289 final boolean isActivity = pendingIntent.isActivity(); 290 if (isActivity) { 291 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing(); 292 final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity( 293 mContext, pendingIntent.getIntent(), mCurrentUserId); 294 dismissKeyguardThenExecute(new OnDismissAction() { 295 @Override 296 public boolean onDismiss() { 297 if (keyguardShowing && !afterKeyguardGone) { 298 try { 299 ActivityManagerNative.getDefault() 300 .keyguardWaitingForActivityDrawn(); 301 ActivityManagerNative.getDefault().resumeAppSwitches(); 302 } catch (RemoteException e) { 303 } 304 } 305 306 boolean handled = superOnClickHandler(view, pendingIntent, fillInIntent); 307 overrideActivityPendingAppTransition(keyguardShowing && !afterKeyguardGone); 308 309 // close the shade if it was open 310 if (handled) { 311 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, 312 true /* force */); 313 visibilityChanged(false); 314 mAssistManager.hideAssist(); 315 } 316 317 // Wait for activity start. 318 return handled; 319 } 320 }, afterKeyguardGone); 321 return true; 322 } else { 323 return super.onClickHandler(view, pendingIntent, fillInIntent); 324 } 325 } 326 327 private void logActionClick(View view) { 328 ViewParent parent = view.getParent(); 329 String key = getNotificationKeyForParent(parent); 330 if (key == null) { 331 Log.w(TAG, "Couldn't determine notification for click."); 332 return; 333 } 334 int index = -1; 335 // If this is a default template, determine the index of the button. 336 if (view.getId() == com.android.internal.R.id.action0 && 337 parent != null && parent instanceof ViewGroup) { 338 ViewGroup actionGroup = (ViewGroup) parent; 339 index = actionGroup.indexOfChild(view); 340 } 341 if (NOTIFICATION_CLICK_DEBUG) { 342 Log.d(TAG, "Clicked on button " + index + " for " + key); 343 } 344 try { 345 mBarService.onNotificationActionClick(key, index); 346 } catch (RemoteException e) { 347 // Ignore 348 } 349 } 350 351 private String getNotificationKeyForParent(ViewParent parent) { 352 while (parent != null) { 353 if (parent instanceof ExpandableNotificationRow) { 354 return ((ExpandableNotificationRow) parent).getStatusBarNotification().getKey(); 355 } 356 parent = parent.getParent(); 357 } 358 return null; 359 } 360 361 private boolean superOnClickHandler(View view, PendingIntent pendingIntent, 362 Intent fillInIntent) { 363 return super.onClickHandler(view, pendingIntent, fillInIntent); 364 } 365 }; 366 367 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 368 @Override 369 public void onReceive(Context context, Intent intent) { 370 String action = intent.getAction(); 371 if (Intent.ACTION_USER_SWITCHED.equals(action)) { 372 mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); 373 updateCurrentProfilesCache(); 374 if (true) Log.v(TAG, "userId " + mCurrentUserId + " is in the house"); 375 376 updateLockscreenNotificationSetting(); 377 378 userSwitched(mCurrentUserId); 379 } else if (Intent.ACTION_USER_ADDED.equals(action)) { 380 updateCurrentProfilesCache(); 381 } else if (Intent.ACTION_USER_PRESENT.equals(action)) { 382 List<ActivityManager.RecentTaskInfo> recentTask = null; 383 try { 384 recentTask = ActivityManagerNative.getDefault().getRecentTasks(1, 385 ActivityManager.RECENT_WITH_EXCLUDED 386 | ActivityManager.RECENT_INCLUDE_PROFILES, 387 mCurrentUserId); 388 } catch (RemoteException e) { 389 // Abandon hope activity manager not running. 390 } 391 if (recentTask != null && recentTask.size() > 0) { 392 UserInfo user = mUserManager.getUserInfo(recentTask.get(0).userId); 393 if (user != null && user.isManagedProfile()) { 394 Toast toast = Toast.makeText(mContext, 395 R.string.managed_profile_foreground_toast, 396 Toast.LENGTH_SHORT); 397 TextView text = (TextView) toast.getView().findViewById( 398 android.R.id.message); 399 text.setCompoundDrawablesRelativeWithIntrinsicBounds( 400 R.drawable.stat_sys_managed_profile_status, 0, 0, 0); 401 int paddingPx = mContext.getResources().getDimensionPixelSize( 402 R.dimen.managed_profile_toast_padding); 403 text.setCompoundDrawablePadding(paddingPx); 404 toast.show(); 405 } 406 } 407 } else if (BANNER_ACTION_CANCEL.equals(action) || BANNER_ACTION_SETUP.equals(action)) { 408 NotificationManager noMan = (NotificationManager) 409 mContext.getSystemService(Context.NOTIFICATION_SERVICE); 410 noMan.cancel(R.id.notification_hidden); 411 412 Settings.Secure.putInt(mContext.getContentResolver(), 413 Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0); 414 if (BANNER_ACTION_SETUP.equals(action)) { 415 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, 416 true /* force */); 417 mContext.startActivity(new Intent(Settings.ACTION_APP_NOTIFICATION_REDACTION) 418 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 419 420 ); 421 } 422 } 423 } 424 }; 425 426 private final BroadcastReceiver mAllUsersReceiver = new BroadcastReceiver() { 427 @Override 428 public void onReceive(Context context, Intent intent) { 429 String action = intent.getAction(); 430 if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action) && 431 isCurrentProfile(getSendingUserId())) { 432 mUsersAllowingPrivateNotifications.clear(); 433 updateLockscreenNotificationSetting(); 434 updateNotifications(); 435 } 436 } 437 }; 438 439 private final NotificationListenerService mNotificationListener = 440 new NotificationListenerService() { 441 @Override 442 public void onListenerConnected() { 443 if (DEBUG) Log.d(TAG, "onListenerConnected"); 444 final StatusBarNotification[] notifications = getActiveNotifications(); 445 final RankingMap currentRanking = getCurrentRanking(); 446 mHandler.post(new Runnable() { 447 @Override 448 public void run() { 449 for (StatusBarNotification sbn : notifications) { 450 addNotification(sbn, currentRanking, null /* oldEntry */); 451 } 452 } 453 }); 454 } 455 456 @Override 457 public void onNotificationPosted(final StatusBarNotification sbn, 458 final RankingMap rankingMap) { 459 if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn); 460 if (sbn != null) { 461 mHandler.post(new Runnable() { 462 @Override 463 public void run() { 464 465 String key = sbn.getKey(); 466 boolean isUpdate = mNotificationData.get(key) != null; 467 468 // In case we don't allow child notifications, we ignore children of 469 // notifications that have a summary, since we're not going to show them 470 // anyway. This is true also when the summary is canceled, 471 // because children are automatically canceled by NoMan in that case. 472 if (!ENABLE_CHILD_NOTIFICATIONS 473 && mGroupManager.isChildInGroupWithSummary(sbn)) { 474 if (DEBUG) { 475 Log.d(TAG, "Ignoring group child due to existing summary: " + sbn); 476 } 477 478 // Remove existing notification to avoid stale data. 479 if (isUpdate) { 480 removeNotification(key, rankingMap); 481 } else { 482 mNotificationData.updateRanking(rankingMap); 483 } 484 return; 485 } 486 if (isUpdate) { 487 updateNotification(sbn, rankingMap); 488 } else { 489 addNotification(sbn, rankingMap, null /* oldEntry */); 490 } 491 } 492 }); 493 } 494 } 495 496 @Override 497 public void onNotificationRemoved(StatusBarNotification sbn, 498 final RankingMap rankingMap) { 499 if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn); 500 if (sbn != null) { 501 final String key = sbn.getKey(); 502 mHandler.post(new Runnable() { 503 @Override 504 public void run() { 505 removeNotification(key, rankingMap); 506 } 507 }); 508 } 509 } 510 511 @Override 512 public void onNotificationRankingUpdate(final RankingMap rankingMap) { 513 if (DEBUG) Log.d(TAG, "onRankingUpdate"); 514 if (rankingMap != null) { 515 mHandler.post(new Runnable() { 516 @Override 517 public void run() { 518 updateNotificationRanking(rankingMap); 519 } 520 }); 521 } } 522 523 }; 524 updateCurrentProfilesCache()525 private void updateCurrentProfilesCache() { 526 synchronized (mCurrentProfiles) { 527 mCurrentProfiles.clear(); 528 if (mUserManager != null) { 529 for (UserInfo user : mUserManager.getProfiles(mCurrentUserId)) { 530 mCurrentProfiles.put(user.id, user); 531 } 532 } 533 } 534 } 535 start()536 public void start() { 537 mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE); 538 mWindowManagerService = WindowManagerGlobal.getWindowManagerService(); 539 mDisplay = mWindowManager.getDefaultDisplay(); 540 mDevicePolicyManager = (DevicePolicyManager)mContext.getSystemService( 541 Context.DEVICE_POLICY_SERVICE); 542 543 mNotificationColorUtil = NotificationColorUtil.getInstance(mContext); 544 545 mNotificationData = new NotificationData(this); 546 547 mAccessibilityManager = (AccessibilityManager) 548 mContext.getSystemService(Context.ACCESSIBILITY_SERVICE); 549 550 mDreamManager = IDreamManager.Stub.asInterface( 551 ServiceManager.checkService(DreamService.DREAM_SERVICE)); 552 mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); 553 554 mContext.getContentResolver().registerContentObserver( 555 Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), true, 556 mSettingsObserver); 557 mContext.getContentResolver().registerContentObserver( 558 Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false, 559 mSettingsObserver); 560 mContext.getContentResolver().registerContentObserver( 561 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false, 562 mSettingsObserver, 563 UserHandle.USER_ALL); 564 565 mContext.getContentResolver().registerContentObserver( 566 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS), 567 true, 568 mLockscreenSettingsObserver, 569 UserHandle.USER_ALL); 570 571 mBarService = IStatusBarService.Stub.asInterface( 572 ServiceManager.getService(Context.STATUS_BAR_SERVICE)); 573 574 mRecents = getComponent(Recents.class); 575 mRecents.setCallback(this); 576 577 final Configuration currentConfig = mContext.getResources().getConfiguration(); 578 mLocale = currentConfig.locale; 579 mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale); 580 mFontScale = currentConfig.fontScale; 581 582 mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); 583 584 mLinearOutSlowIn = AnimationUtils.loadInterpolator(mContext, 585 android.R.interpolator.linear_out_slow_in); 586 mFastOutLinearIn = AnimationUtils.loadInterpolator(mContext, 587 android.R.interpolator.fast_out_linear_in); 588 589 // Connect in to the status bar manager service 590 StatusBarIconList iconList = new StatusBarIconList(); 591 mCommandQueue = new CommandQueue(this, iconList); 592 593 int[] switches = new int[8]; 594 ArrayList<IBinder> binders = new ArrayList<IBinder>(); 595 try { 596 mBarService.registerStatusBar(mCommandQueue, iconList, switches, binders); 597 } catch (RemoteException ex) { 598 // If the system process isn't there we're doomed anyway. 599 } 600 601 createAndAddWindows(); 602 603 mSettingsObserver.onChange(false); // set up 604 disable(switches[0], switches[6], false /* animate */); 605 setSystemUiVisibility(switches[1], 0xffffffff); 606 topAppWindowChanged(switches[2] != 0); 607 // StatusBarManagerService has a back up of IME token and it's restored here. 608 setImeWindowStatus(binders.get(0), switches[3], switches[4], switches[5] != 0); 609 610 // Set up the initial icon state 611 int N = iconList.size(); 612 int viewIndex = 0; 613 for (int i=0; i<N; i++) { 614 StatusBarIcon icon = iconList.getIcon(i); 615 if (icon != null) { 616 addIcon(iconList.getSlot(i), i, viewIndex, icon); 617 viewIndex++; 618 } 619 } 620 621 // Set up the initial notification state. 622 try { 623 mNotificationListener.registerAsSystemService(mContext, 624 new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()), 625 UserHandle.USER_ALL); 626 } catch (RemoteException e) { 627 Log.e(TAG, "Unable to register notification listener", e); 628 } 629 630 631 if (DEBUG) { 632 Log.d(TAG, String.format( 633 "init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x", 634 iconList.size(), 635 switches[0], 636 switches[1], 637 switches[2], 638 switches[3] 639 )); 640 } 641 642 mCurrentUserId = ActivityManager.getCurrentUser(); 643 setHeadsUpUser(mCurrentUserId); 644 645 IntentFilter filter = new IntentFilter(); 646 filter.addAction(Intent.ACTION_USER_SWITCHED); 647 filter.addAction(Intent.ACTION_USER_ADDED); 648 filter.addAction(Intent.ACTION_USER_PRESENT); 649 filter.addAction(BANNER_ACTION_CANCEL); 650 filter.addAction(BANNER_ACTION_SETUP); 651 mContext.registerReceiver(mBroadcastReceiver, filter); 652 653 IntentFilter allUsersFilter = new IntentFilter(); 654 allUsersFilter.addAction( 655 DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); 656 mContext.registerReceiverAsUser(mAllUsersReceiver, UserHandle.ALL, allUsersFilter, 657 null, null); 658 updateCurrentProfilesCache(); 659 } 660 notifyUserAboutHiddenNotifications()661 protected void notifyUserAboutHiddenNotifications() { 662 if (0 != Settings.Secure.getInt(mContext.getContentResolver(), 663 Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 1)) { 664 Log.d(TAG, "user hasn't seen notification about hidden notifications"); 665 final LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext); 666 if (!lockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())) { 667 Log.d(TAG, "insecure lockscreen, skipping notification"); 668 Settings.Secure.putInt(mContext.getContentResolver(), 669 Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0); 670 return; 671 } 672 Log.d(TAG, "disabling lockecreen notifications and alerting the user"); 673 // disable lockscreen notifications until user acts on the banner. 674 Settings.Secure.putInt(mContext.getContentResolver(), 675 Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0); 676 Settings.Secure.putInt(mContext.getContentResolver(), 677 Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0); 678 679 final String packageName = mContext.getPackageName(); 680 PendingIntent cancelIntent = PendingIntent.getBroadcast(mContext, 0, 681 new Intent(BANNER_ACTION_CANCEL).setPackage(packageName), 682 PendingIntent.FLAG_CANCEL_CURRENT); 683 PendingIntent setupIntent = PendingIntent.getBroadcast(mContext, 0, 684 new Intent(BANNER_ACTION_SETUP).setPackage(packageName), 685 PendingIntent.FLAG_CANCEL_CURRENT); 686 687 final Resources res = mContext.getResources(); 688 final int colorRes = com.android.internal.R.color.system_notification_accent_color; 689 Notification.Builder note = new Notification.Builder(mContext) 690 .setSmallIcon(R.drawable.ic_android) 691 .setContentTitle(mContext.getString(R.string.hidden_notifications_title)) 692 .setContentText(mContext.getString(R.string.hidden_notifications_text)) 693 .setPriority(Notification.PRIORITY_HIGH) 694 .setOngoing(true) 695 .setColor(mContext.getColor(colorRes)) 696 .setContentIntent(setupIntent) 697 .addAction(R.drawable.ic_close, 698 mContext.getString(R.string.hidden_notifications_cancel), 699 cancelIntent) 700 .addAction(R.drawable.ic_settings, 701 mContext.getString(R.string.hidden_notifications_setup), 702 setupIntent); 703 704 NotificationManager noMan = 705 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); 706 noMan.notify(R.id.notification_hidden, note.build()); 707 } 708 } 709 userSwitched(int newUserId)710 public void userSwitched(int newUserId) { 711 setHeadsUpUser(newUserId); 712 } 713 setHeadsUpUser(int newUserId)714 protected abstract void setHeadsUpUser(int newUserId); 715 716 @Override // NotificationData.Environment isNotificationForCurrentProfiles(StatusBarNotification n)717 public boolean isNotificationForCurrentProfiles(StatusBarNotification n) { 718 final int thisUserId = mCurrentUserId; 719 final int notificationUserId = n.getUserId(); 720 if (DEBUG && MULTIUSER_DEBUG) { 721 Log.v(TAG, String.format("%s: current userid: %d, notification userid: %d", 722 n, thisUserId, notificationUserId)); 723 } 724 return isCurrentProfile(notificationUserId); 725 } 726 setNotificationShown(StatusBarNotification n)727 protected void setNotificationShown(StatusBarNotification n) { 728 setNotificationsShown(new String[]{n.getKey()}); 729 } 730 setNotificationsShown(String[] keys)731 protected void setNotificationsShown(String[] keys) { 732 try { 733 mNotificationListener.setNotificationsShown(keys); 734 } catch (RuntimeException e) { 735 Log.d(TAG, "failed setNotificationsShown: ", e); 736 } 737 } 738 isCurrentProfile(int userId)739 protected boolean isCurrentProfile(int userId) { 740 synchronized (mCurrentProfiles) { 741 return userId == UserHandle.USER_ALL || mCurrentProfiles.get(userId) != null; 742 } 743 } 744 745 @Override getCurrentMediaNotificationKey()746 public String getCurrentMediaNotificationKey() { 747 return null; 748 } 749 750 @Override getGroupManager()751 public NotificationGroupManager getGroupManager() { 752 return mGroupManager; 753 } 754 755 /** 756 * Takes the necessary steps to prepare the status bar for starting an activity, then starts it. 757 * @param action A dismiss action that is called if it's safe to start the activity. 758 * @param afterKeyguardGone Whether the action should be executed after the Keyguard is gone. 759 */ dismissKeyguardThenExecute(OnDismissAction action, boolean afterKeyguardGone)760 protected void dismissKeyguardThenExecute(OnDismissAction action, boolean afterKeyguardGone) { 761 action.onDismiss(); 762 } 763 764 @Override onConfigurationChanged(Configuration newConfig)765 protected void onConfigurationChanged(Configuration newConfig) { 766 final Locale locale = mContext.getResources().getConfiguration().locale; 767 final int ld = TextUtils.getLayoutDirectionFromLocale(locale); 768 final float fontScale = newConfig.fontScale; 769 770 if (! locale.equals(mLocale) || ld != mLayoutDirection || fontScale != mFontScale) { 771 if (DEBUG) { 772 Log.v(TAG, String.format( 773 "config changed locale/LD: %s (%d) -> %s (%d)", mLocale, mLayoutDirection, 774 locale, ld)); 775 } 776 mLocale = locale; 777 mLayoutDirection = ld; 778 refreshLayout(ld); 779 } 780 } 781 updateNotificationVetoButton(View row, StatusBarNotification n)782 protected View updateNotificationVetoButton(View row, StatusBarNotification n) { 783 View vetoButton = row.findViewById(R.id.veto); 784 if (n.isClearable()) { 785 final String _pkg = n.getPackageName(); 786 final String _tag = n.getTag(); 787 final int _id = n.getId(); 788 final int _userId = n.getUserId(); 789 vetoButton.setOnClickListener(new View.OnClickListener() { 790 public void onClick(View v) { 791 // Accessibility feedback 792 v.announceForAccessibility( 793 mContext.getString(R.string.accessibility_notification_dismissed)); 794 try { 795 mBarService.onNotificationClear(_pkg, _tag, _id, _userId); 796 797 } catch (RemoteException ex) { 798 // system process is dead if we're here. 799 } 800 } 801 }); 802 vetoButton.setVisibility(View.VISIBLE); 803 } else { 804 vetoButton.setVisibility(View.GONE); 805 } 806 vetoButton.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); 807 return vetoButton; 808 } 809 810 applyColorsAndBackgrounds(StatusBarNotification sbn, NotificationData.Entry entry)811 protected void applyColorsAndBackgrounds(StatusBarNotification sbn, 812 NotificationData.Entry entry) { 813 814 if (entry.getContentView().getId() 815 != com.android.internal.R.id.status_bar_latest_event_content) { 816 // Using custom RemoteViews 817 if (entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD 818 && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP) { 819 entry.row.setShowingLegacyBackground(true); 820 entry.legacy = true; 821 } 822 } else { 823 // Using platform templates 824 final int color = sbn.getNotification().color; 825 if (isMediaNotification(entry)) { 826 entry.row.setTintColor(color == Notification.COLOR_DEFAULT 827 ? mContext.getColor( 828 R.color.notification_material_background_media_default_color) 829 : color); 830 } 831 } 832 833 if (entry.icon != null) { 834 entry.icon.setTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP); 835 } 836 } 837 isMediaNotification(NotificationData.Entry entry)838 public boolean isMediaNotification(NotificationData.Entry entry) { 839 // TODO: confirm that there's a valid media key 840 return entry.getExpandedContentView() != null && 841 entry.getExpandedContentView() 842 .findViewById(com.android.internal.R.id.media_actions) != null; 843 } 844 845 // The gear button in the guts that links to the app's own notification settings startAppOwnNotificationSettingsActivity(Intent intent, final int notificationId, final String notificationTag, final int appUid)846 private void startAppOwnNotificationSettingsActivity(Intent intent, 847 final int notificationId, final String notificationTag, final int appUid) { 848 intent.putExtra("notification_id", notificationId); 849 intent.putExtra("notification_tag", notificationTag); 850 startNotificationGutsIntent(intent, appUid); 851 } 852 853 // The (i) button in the guts that links to the system notification settings for that app startAppNotificationSettingsActivity(String packageName, final int appUid)854 private void startAppNotificationSettingsActivity(String packageName, final int appUid) { 855 final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS); 856 intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName); 857 intent.putExtra(Settings.EXTRA_APP_UID, appUid); 858 startNotificationGutsIntent(intent, appUid); 859 } 860 startNotificationGutsIntent(final Intent intent, final int appUid)861 private void startNotificationGutsIntent(final Intent intent, final int appUid) { 862 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing(); 863 dismissKeyguardThenExecute(new OnDismissAction() { 864 @Override 865 public boolean onDismiss() { 866 AsyncTask.execute(new Runnable() { 867 public void run() { 868 try { 869 if (keyguardShowing) { 870 ActivityManagerNative.getDefault() 871 .keyguardWaitingForActivityDrawn(); 872 } 873 TaskStackBuilder.create(mContext) 874 .addNextIntentWithParentStack(intent) 875 .startActivities(null, 876 new UserHandle(UserHandle.getUserId(appUid))); 877 overrideActivityPendingAppTransition(keyguardShowing); 878 } catch (RemoteException e) { 879 } 880 } 881 }); 882 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */); 883 return true; 884 } 885 }, false /* afterKeyguardGone */); 886 } 887 bindGuts(ExpandableNotificationRow row)888 private void bindGuts(ExpandableNotificationRow row) { 889 row.inflateGuts(); 890 final StatusBarNotification sbn = row.getStatusBarNotification(); 891 PackageManager pmUser = getPackageManagerForUser( 892 sbn.getUser().getIdentifier()); 893 row.setTag(sbn.getPackageName()); 894 final View guts = row.getGuts(); 895 final String pkg = sbn.getPackageName(); 896 String appname = pkg; 897 Drawable pkgicon = null; 898 int appUid = -1; 899 try { 900 final ApplicationInfo info = pmUser.getApplicationInfo(pkg, 901 PackageManager.GET_UNINSTALLED_PACKAGES 902 | PackageManager.GET_DISABLED_COMPONENTS); 903 if (info != null) { 904 appname = String.valueOf(pmUser.getApplicationLabel(info)); 905 pkgicon = pmUser.getApplicationIcon(info); 906 appUid = info.uid; 907 } 908 } catch (NameNotFoundException e) { 909 // app is gone, just show package name and generic icon 910 pkgicon = pmUser.getDefaultActivityIcon(); 911 } 912 ((ImageView) row.findViewById(android.R.id.icon)).setImageDrawable(pkgicon); 913 ((DateTimeView) row.findViewById(R.id.timestamp)).setTime(sbn.getPostTime()); 914 ((TextView) row.findViewById(R.id.pkgname)).setText(appname); 915 final View settingsButton = guts.findViewById(R.id.notification_inspect_item); 916 final View appSettingsButton 917 = guts.findViewById(R.id.notification_inspect_app_provided_settings); 918 if (appUid >= 0) { 919 final int appUidF = appUid; 920 settingsButton.setOnClickListener(new View.OnClickListener() { 921 public void onClick(View v) { 922 MetricsLogger.action(mContext, MetricsLogger.ACTION_NOTE_INFO); 923 startAppNotificationSettingsActivity(pkg, appUidF); 924 } 925 }); 926 927 final Intent appSettingsQueryIntent 928 = new Intent(Intent.ACTION_MAIN) 929 .addCategory(Notification.INTENT_CATEGORY_NOTIFICATION_PREFERENCES) 930 .setPackage(pkg); 931 List<ResolveInfo> infos = pmUser.queryIntentActivities(appSettingsQueryIntent, 0); 932 if (infos.size() > 0) { 933 appSettingsButton.setVisibility(View.VISIBLE); 934 appSettingsButton.setContentDescription( 935 mContext.getResources().getString( 936 R.string.status_bar_notification_app_settings_title, 937 appname 938 )); 939 final Intent appSettingsLaunchIntent = new Intent(appSettingsQueryIntent) 940 .setClassName(pkg, infos.get(0).activityInfo.name); 941 appSettingsButton.setOnClickListener(new View.OnClickListener() { 942 public void onClick(View v) { 943 MetricsLogger.action(mContext, MetricsLogger.ACTION_APP_NOTE_SETTINGS); 944 startAppOwnNotificationSettingsActivity(appSettingsLaunchIntent, 945 sbn.getId(), 946 sbn.getTag(), 947 appUidF); 948 } 949 }); 950 } else { 951 appSettingsButton.setVisibility(View.GONE); 952 } 953 } else { 954 settingsButton.setVisibility(View.GONE); 955 appSettingsButton.setVisibility(View.GONE); 956 } 957 958 } 959 getNotificationLongClicker()960 protected SwipeHelper.LongPressListener getNotificationLongClicker() { 961 return new SwipeHelper.LongPressListener() { 962 @Override 963 public boolean onLongPress(View v, int x, int y) { 964 dismissPopups(); 965 966 if (!(v instanceof ExpandableNotificationRow)) { 967 return false; 968 } 969 if (v.getWindowToken() == null) { 970 Log.e(TAG, "Trying to show notification guts, but not attached to window"); 971 return false; 972 } 973 974 ExpandableNotificationRow row = (ExpandableNotificationRow) v; 975 bindGuts(row); 976 977 // Assume we are a status_bar_notification_row 978 final NotificationGuts guts = row.getGuts(); 979 if (guts == null) { 980 // This view has no guts. Examples are the more card or the dismiss all view 981 return false; 982 } 983 984 // Already showing? 985 if (guts.getVisibility() == View.VISIBLE) { 986 Log.e(TAG, "Trying to show notification guts, but already visible"); 987 return false; 988 } 989 990 MetricsLogger.action(mContext, MetricsLogger.ACTION_NOTE_CONTROLS); 991 guts.setVisibility(View.VISIBLE); 992 final double horz = Math.max(guts.getWidth() - x, x); 993 final double vert = Math.max(guts.getActualHeight() - y, y); 994 final float r = (float) Math.hypot(horz, vert); 995 final Animator a 996 = ViewAnimationUtils.createCircularReveal(guts, x, y, 0, r); 997 a.setDuration(400); 998 a.setInterpolator(mLinearOutSlowIn); 999 a.start(); 1000 1001 mNotificationGutsExposed = guts; 1002 1003 return true; 1004 } 1005 }; 1006 } 1007 1008 public void dismissPopups() { 1009 if (mNotificationGutsExposed != null) { 1010 final NotificationGuts v = mNotificationGutsExposed; 1011 mNotificationGutsExposed = null; 1012 1013 if (v.getWindowToken() == null) return; 1014 1015 final int x = (v.getLeft() + v.getRight()) / 2; 1016 final int y = (v.getTop() + v.getActualHeight() / 2); 1017 final Animator a = ViewAnimationUtils.createCircularReveal(v, 1018 x, y, x, 0); 1019 a.setDuration(200); 1020 a.setInterpolator(mFastOutLinearIn); 1021 a.addListener(new AnimatorListenerAdapter() { 1022 @Override 1023 public void onAnimationEnd(Animator animation) { 1024 super.onAnimationEnd(animation); 1025 v.setVisibility(View.GONE); 1026 } 1027 }); 1028 a.start(); 1029 } 1030 } 1031 1032 @Override 1033 public void showRecentApps(boolean triggeredFromAltTab) { 1034 int msg = MSG_SHOW_RECENT_APPS; 1035 mHandler.removeMessages(msg); 1036 mHandler.obtainMessage(msg, triggeredFromAltTab ? 1 : 0, 0).sendToTarget(); 1037 } 1038 1039 @Override 1040 public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) { 1041 int msg = MSG_HIDE_RECENT_APPS; 1042 mHandler.removeMessages(msg); 1043 mHandler.obtainMessage(msg, triggeredFromAltTab ? 1 : 0, 1044 triggeredFromHomeKey ? 1 : 0).sendToTarget(); 1045 } 1046 1047 @Override 1048 public void toggleRecentApps() { 1049 toggleRecents(); 1050 } 1051 1052 @Override 1053 public void preloadRecentApps() { 1054 int msg = MSG_PRELOAD_RECENT_APPS; 1055 mHandler.removeMessages(msg); 1056 mHandler.sendEmptyMessage(msg); 1057 } 1058 1059 @Override 1060 public void cancelPreloadRecentApps() { 1061 int msg = MSG_CANCEL_PRELOAD_RECENT_APPS; 1062 mHandler.removeMessages(msg); 1063 mHandler.sendEmptyMessage(msg); 1064 } 1065 1066 /** Jumps to the next affiliated task in the group. */ 1067 public void showNextAffiliatedTask() { 1068 int msg = MSG_SHOW_NEXT_AFFILIATED_TASK; 1069 mHandler.removeMessages(msg); 1070 mHandler.sendEmptyMessage(msg); 1071 } 1072 1073 /** Jumps to the previous affiliated task in the group. */ 1074 public void showPreviousAffiliatedTask() { 1075 int msg = MSG_SHOW_PREV_AFFILIATED_TASK; 1076 mHandler.removeMessages(msg); 1077 mHandler.sendEmptyMessage(msg); 1078 } 1079 1080 protected H createHandler() { 1081 return new H(); 1082 } 1083 1084 static void sendCloseSystemWindows(Context context, String reason) { 1085 if (ActivityManagerNative.isSystemReady()) { 1086 try { 1087 ActivityManagerNative.getDefault().closeSystemDialogs(reason); 1088 } catch (RemoteException e) { 1089 } 1090 } 1091 } 1092 1093 protected abstract View getStatusBarView(); 1094 1095 protected View.OnTouchListener mRecentsPreloadOnTouchListener = new View.OnTouchListener() { 1096 // additional optimization when we have software system buttons - start loading the recent 1097 // tasks on touch down 1098 @Override 1099 public boolean onTouch(View v, MotionEvent event) { 1100 int action = event.getAction() & MotionEvent.ACTION_MASK; 1101 if (action == MotionEvent.ACTION_DOWN) { 1102 preloadRecents(); 1103 } else if (action == MotionEvent.ACTION_CANCEL) { 1104 cancelPreloadingRecents(); 1105 } else if (action == MotionEvent.ACTION_UP) { 1106 if (!v.isPressed()) { 1107 cancelPreloadingRecents(); 1108 } 1109 1110 } 1111 return false; 1112 } 1113 }; 1114 1115 /** Proxy for RecentsComponent */ 1116 1117 protected void showRecents(boolean triggeredFromAltTab) { 1118 if (mRecents != null) { 1119 sendCloseSystemWindows(mContext, SYSTEM_DIALOG_REASON_RECENT_APPS); 1120 mRecents.showRecents(triggeredFromAltTab, getStatusBarView()); 1121 } 1122 } 1123 1124 protected void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) { 1125 if (mRecents != null) { 1126 mRecents.hideRecents(triggeredFromAltTab, triggeredFromHomeKey); 1127 } 1128 } 1129 1130 protected void toggleRecents() { 1131 if (mRecents != null) { 1132 sendCloseSystemWindows(mContext, SYSTEM_DIALOG_REASON_RECENT_APPS); 1133 mRecents.toggleRecents(mDisplay, mLayoutDirection, getStatusBarView()); 1134 } 1135 } 1136 1137 protected void preloadRecents() { 1138 if (mRecents != null) { 1139 mRecents.preloadRecents(); 1140 } 1141 } 1142 1143 protected void cancelPreloadingRecents() { 1144 if (mRecents != null) { 1145 mRecents.cancelPreloadingRecents(); 1146 } 1147 } 1148 1149 protected void showRecentsNextAffiliatedTask() { 1150 if (mRecents != null) { 1151 mRecents.showNextAffiliatedTask(); 1152 } 1153 } 1154 1155 protected void showRecentsPreviousAffiliatedTask() { 1156 if (mRecents != null) { 1157 mRecents.showPrevAffiliatedTask(); 1158 } 1159 } 1160 1161 @Override 1162 public void onVisibilityChanged(boolean visible) { 1163 // Do nothing 1164 } 1165 1166 /** 1167 * If there is an active heads-up notification and it has a fullscreen intent, fire it now. 1168 */ 1169 public abstract void maybeEscalateHeadsUp(); 1170 1171 /** 1172 * Save the current "public" (locked and secure) state of the lockscreen. 1173 */ 1174 public void setLockscreenPublicMode(boolean publicMode) { 1175 mLockscreenPublicMode = publicMode; 1176 } 1177 1178 public boolean isLockscreenPublicMode() { 1179 return mLockscreenPublicMode; 1180 } 1181 1182 /** 1183 * Has the given user chosen to allow their private (full) notifications to be shown even 1184 * when the lockscreen is in "public" (secure & locked) mode? 1185 */ 1186 public boolean userAllowsPrivateNotificationsInPublic(int userHandle) { 1187 if (userHandle == UserHandle.USER_ALL) { 1188 return true; 1189 } 1190 1191 if (mUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) { 1192 final boolean allowedByUser = 0 != Settings.Secure.getIntForUser( 1193 mContext.getContentResolver(), 1194 Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle); 1195 final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(null /* admin */, 1196 userHandle); 1197 final boolean allowedByDpm = (dpmFlags 1198 & DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS) == 0; 1199 final boolean allowed = allowedByUser && allowedByDpm; 1200 mUsersAllowingPrivateNotifications.append(userHandle, allowed); 1201 return allowed; 1202 } 1203 1204 return mUsersAllowingPrivateNotifications.get(userHandle); 1205 } 1206 1207 /** 1208 * Returns true if we're on a secure lockscreen and the user wants to hide "sensitive" 1209 * notification data. If so, private notifications should show their (possibly 1210 * auto-generated) publicVersion, and secret notifications should be totally invisible. 1211 */ 1212 @Override // NotificationData.Environment 1213 public boolean shouldHideSensitiveContents(int userid) { 1214 return isLockscreenPublicMode() && !userAllowsPrivateNotificationsInPublic(userid); 1215 } 1216 1217 public void onNotificationClear(StatusBarNotification notification) { 1218 try { 1219 mBarService.onNotificationClear( 1220 notification.getPackageName(), 1221 notification.getTag(), 1222 notification.getId(), 1223 notification.getUserId()); 1224 } catch (android.os.RemoteException ex) { 1225 // oh well 1226 } 1227 } 1228 1229 protected class H extends Handler { 1230 public void handleMessage(Message m) { 1231 switch (m.what) { 1232 case MSG_SHOW_RECENT_APPS: 1233 showRecents(m.arg1 > 0); 1234 break; 1235 case MSG_HIDE_RECENT_APPS: 1236 hideRecents(m.arg1 > 0, m.arg2 > 0); 1237 break; 1238 case MSG_TOGGLE_RECENTS_APPS: 1239 toggleRecents(); 1240 break; 1241 case MSG_PRELOAD_RECENT_APPS: 1242 preloadRecents(); 1243 break; 1244 case MSG_CANCEL_PRELOAD_RECENT_APPS: 1245 cancelPreloadingRecents(); 1246 break; 1247 case MSG_SHOW_NEXT_AFFILIATED_TASK: 1248 showRecentsNextAffiliatedTask(); 1249 break; 1250 case MSG_SHOW_PREV_AFFILIATED_TASK: 1251 showRecentsPreviousAffiliatedTask(); 1252 break; 1253 } 1254 } 1255 } 1256 1257 protected void workAroundBadLayerDrawableOpacity(View v) { 1258 } 1259 1260 protected boolean inflateViews(Entry entry, ViewGroup parent) { 1261 PackageManager pmUser = getPackageManagerForUser( 1262 entry.notification.getUser().getIdentifier()); 1263 1264 int maxHeight = mRowMaxHeight; 1265 final StatusBarNotification sbn = entry.notification; 1266 RemoteViews contentView = sbn.getNotification().contentView; 1267 RemoteViews bigContentView = sbn.getNotification().bigContentView; 1268 RemoteViews headsUpContentView = sbn.getNotification().headsUpContentView; 1269 1270 if (contentView == null) { 1271 return false; 1272 } 1273 1274 if (DEBUG) { 1275 Log.v(TAG, "publicNotification: " + sbn.getNotification().publicVersion); 1276 } 1277 1278 Notification publicNotification = sbn.getNotification().publicVersion; 1279 1280 ExpandableNotificationRow row; 1281 1282 // Stash away previous user expansion state so we can restore it at 1283 // the end. 1284 boolean hasUserChangedExpansion = false; 1285 boolean userExpanded = false; 1286 boolean userLocked = false; 1287 1288 if (entry.row != null) { 1289 row = entry.row; 1290 hasUserChangedExpansion = row.hasUserChangedExpansion(); 1291 userExpanded = row.isUserExpanded(); 1292 userLocked = row.isUserLocked(); 1293 entry.reset(); 1294 if (hasUserChangedExpansion) { 1295 row.setUserExpanded(userExpanded); 1296 } 1297 } else { 1298 // create the row view 1299 LayoutInflater inflater = (LayoutInflater) mContext.getSystemService( 1300 Context.LAYOUT_INFLATER_SERVICE); 1301 row = (ExpandableNotificationRow) inflater.inflate(R.layout.status_bar_notification_row, 1302 parent, false); 1303 row.setExpansionLogger(this, entry.notification.getKey()); 1304 row.setGroupManager(mGroupManager); 1305 } 1306 1307 workAroundBadLayerDrawableOpacity(row); 1308 View vetoButton = updateNotificationVetoButton(row, sbn); 1309 vetoButton.setContentDescription(mContext.getString( 1310 R.string.accessibility_remove_notification)); 1311 1312 // NB: the large icon is now handled entirely by the template 1313 1314 // bind the click event to the content area 1315 NotificationContentView contentContainer = row.getPrivateLayout(); 1316 NotificationContentView contentContainerPublic = row.getPublicLayout(); 1317 1318 row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); 1319 1320 mNotificationClicker.register(row, sbn); 1321 1322 // set up the adaptive layout 1323 View contentViewLocal = null; 1324 View bigContentViewLocal = null; 1325 View headsUpContentViewLocal = null; 1326 try { 1327 contentViewLocal = contentView.apply( 1328 sbn.getPackageContext(mContext), 1329 contentContainer, 1330 mOnClickHandler); 1331 if (bigContentView != null) { 1332 bigContentViewLocal = bigContentView.apply( 1333 sbn.getPackageContext(mContext), 1334 contentContainer, 1335 mOnClickHandler); 1336 } 1337 if (headsUpContentView != null) { 1338 headsUpContentViewLocal = headsUpContentView.apply( 1339 sbn.getPackageContext(mContext), 1340 contentContainer, 1341 mOnClickHandler); 1342 } 1343 } 1344 catch (RuntimeException e) { 1345 final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()); 1346 Log.e(TAG, "couldn't inflate view for notification " + ident, e); 1347 return false; 1348 } 1349 1350 if (contentViewLocal != null) { 1351 contentViewLocal.setIsRootNamespace(true); 1352 contentContainer.setContractedChild(contentViewLocal); 1353 } 1354 if (bigContentViewLocal != null) { 1355 bigContentViewLocal.setIsRootNamespace(true); 1356 contentContainer.setExpandedChild(bigContentViewLocal); 1357 } 1358 if (headsUpContentViewLocal != null) { 1359 headsUpContentViewLocal.setIsRootNamespace(true); 1360 contentContainer.setHeadsUpChild(headsUpContentViewLocal); 1361 } 1362 1363 // now the public version 1364 View publicViewLocal = null; 1365 if (publicNotification != null) { 1366 try { 1367 publicViewLocal = publicNotification.contentView.apply( 1368 sbn.getPackageContext(mContext), 1369 contentContainerPublic, mOnClickHandler); 1370 1371 if (publicViewLocal != null) { 1372 publicViewLocal.setIsRootNamespace(true); 1373 contentContainerPublic.setContractedChild(publicViewLocal); 1374 } 1375 } 1376 catch (RuntimeException e) { 1377 final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()); 1378 Log.e(TAG, "couldn't inflate public view for notification " + ident, e); 1379 publicViewLocal = null; 1380 } 1381 } 1382 1383 // Extract target SDK version. 1384 try { 1385 ApplicationInfo info = pmUser.getApplicationInfo(sbn.getPackageName(), 0); 1386 entry.targetSdk = info.targetSdkVersion; 1387 } catch (NameNotFoundException ex) { 1388 Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex); 1389 } 1390 1391 if (publicViewLocal == null) { 1392 // Add a basic notification template 1393 publicViewLocal = LayoutInflater.from(mContext).inflate( 1394 R.layout.notification_public_default, 1395 contentContainerPublic, false); 1396 publicViewLocal.setIsRootNamespace(true); 1397 1398 final TextView title = (TextView) publicViewLocal.findViewById(R.id.title); 1399 try { 1400 title.setText(pmUser.getApplicationLabel( 1401 pmUser.getApplicationInfo(entry.notification.getPackageName(), 0))); 1402 } catch (NameNotFoundException e) { 1403 title.setText(entry.notification.getPackageName()); 1404 } 1405 1406 final ImageView icon = (ImageView) publicViewLocal.findViewById(R.id.icon); 1407 final ImageView profileBadge = (ImageView) publicViewLocal.findViewById( 1408 R.id.profile_badge_line3); 1409 1410 final StatusBarIcon ic = new StatusBarIcon( 1411 entry.notification.getUser(), 1412 entry.notification.getPackageName(), 1413 entry.notification.getNotification().getSmallIcon(), 1414 entry.notification.getNotification().iconLevel, 1415 entry.notification.getNotification().number, 1416 entry.notification.getNotification().tickerText); 1417 1418 Drawable iconDrawable = StatusBarIconView.getIcon(mContext, ic); 1419 icon.setImageDrawable(iconDrawable); 1420 if (entry.targetSdk >= Build.VERSION_CODES.LOLLIPOP 1421 || mNotificationColorUtil.isGrayscaleIcon(iconDrawable)) { 1422 icon.setBackgroundResource( 1423 com.android.internal.R.drawable.notification_icon_legacy_bg); 1424 int padding = mContext.getResources().getDimensionPixelSize( 1425 com.android.internal.R.dimen.notification_large_icon_circle_padding); 1426 icon.setPadding(padding, padding, padding, padding); 1427 if (sbn.getNotification().color != Notification.COLOR_DEFAULT) { 1428 icon.getBackground().setColorFilter( 1429 sbn.getNotification().color, PorterDuff.Mode.SRC_ATOP); 1430 } 1431 } 1432 1433 if (profileBadge != null) { 1434 Drawable profileDrawable = mContext.getPackageManager().getUserBadgeForDensity( 1435 entry.notification.getUser(), 0); 1436 if (profileDrawable != null) { 1437 profileBadge.setImageDrawable(profileDrawable); 1438 profileBadge.setVisibility(View.VISIBLE); 1439 } else { 1440 profileBadge.setVisibility(View.GONE); 1441 } 1442 } 1443 1444 final View privateTime = contentViewLocal.findViewById(com.android.internal.R.id.time); 1445 final DateTimeView time = (DateTimeView) publicViewLocal.findViewById(R.id.time); 1446 if (privateTime != null && privateTime.getVisibility() == View.VISIBLE) { 1447 time.setVisibility(View.VISIBLE); 1448 time.setTime(entry.notification.getNotification().when); 1449 } 1450 1451 final TextView text = (TextView) publicViewLocal.findViewById(R.id.text); 1452 if (text != null) { 1453 text.setText(R.string.notification_hidden_text); 1454 text.setTextAppearance(mContext, 1455 R.style.TextAppearance_Material_Notification_Parenthetical); 1456 } 1457 1458 int topPadding = Notification.Builder.calculateTopPadding(mContext, 1459 false /* hasThreeLines */, 1460 mContext.getResources().getConfiguration().fontScale); 1461 title.setPadding(0, topPadding, 0, 0); 1462 1463 contentContainerPublic.setContractedChild(publicViewLocal); 1464 entry.autoRedacted = true; 1465 } 1466 1467 if (MULTIUSER_DEBUG) { 1468 TextView debug = (TextView) row.findViewById(R.id.debug_info); 1469 if (debug != null) { 1470 debug.setVisibility(View.VISIBLE); 1471 debug.setText("CU " + mCurrentUserId +" NU " + entry.notification.getUserId()); 1472 } 1473 } 1474 entry.row = row; 1475 entry.row.setHeightRange(mRowMinHeight, maxHeight); 1476 entry.row.setOnActivatedListener(this); 1477 entry.row.setExpandable(bigContentViewLocal != null); 1478 1479 applyColorsAndBackgrounds(sbn, entry); 1480 1481 // Restore previous flags. 1482 if (hasUserChangedExpansion) { 1483 // Note: setUserExpanded() conveniently ignores calls with 1484 // userExpanded=true if !isExpandable(). 1485 row.setUserExpanded(userExpanded); 1486 } 1487 row.setUserLocked(userLocked); 1488 row.setStatusBarNotification(entry.notification); 1489 1490 return true; 1491 } 1492 1493 public void startPendingIntentDismissingKeyguard(final PendingIntent intent) { 1494 if (!isDeviceProvisioned()) return; 1495 1496 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing(); 1497 final boolean afterKeyguardGone = intent.isActivity() 1498 && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(), 1499 mCurrentUserId); 1500 dismissKeyguardThenExecute(new OnDismissAction() { 1501 public boolean onDismiss() { 1502 new Thread() { 1503 @Override 1504 public void run() { 1505 try { 1506 if (keyguardShowing && !afterKeyguardGone) { 1507 ActivityManagerNative.getDefault() 1508 .keyguardWaitingForActivityDrawn(); 1509 } 1510 1511 // The intent we are sending is for the application, which 1512 // won't have permission to immediately start an activity after 1513 // the user switches to home. We know it is safe to do at this 1514 // point, so make sure new activity switches are now allowed. 1515 ActivityManagerNative.getDefault().resumeAppSwitches(); 1516 } catch (RemoteException e) { 1517 } 1518 1519 try { 1520 intent.send(); 1521 } catch (PendingIntent.CanceledException e) { 1522 // the stack trace isn't very helpful here. 1523 // Just log the exception message. 1524 Log.w(TAG, "Sending intent failed: " + e); 1525 1526 // TODO: Dismiss Keyguard. 1527 } 1528 if (intent.isActivity()) { 1529 mAssistManager.hideAssist(); 1530 overrideActivityPendingAppTransition(keyguardShowing 1531 && !afterKeyguardGone); 1532 } 1533 } 1534 }.start(); 1535 1536 // close the shade if it was open 1537 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, 1538 true /* force */, true /* delayed */); 1539 visibilityChanged(false); 1540 1541 return true; 1542 } 1543 }, afterKeyguardGone); 1544 } 1545 1546 private final class NotificationClicker implements View.OnClickListener { 1547 public void onClick(final View v) { 1548 if (!(v instanceof ExpandableNotificationRow)) { 1549 Log.e(TAG, "NotificationClicker called on a view that is not a notification row."); 1550 return; 1551 } 1552 1553 final ExpandableNotificationRow row = (ExpandableNotificationRow) v; 1554 final StatusBarNotification sbn = row.getStatusBarNotification(); 1555 if (sbn == null) { 1556 Log.e(TAG, "NotificationClicker called on an unclickable notification,"); 1557 return; 1558 } 1559 1560 final PendingIntent intent = sbn.getNotification().contentIntent; 1561 final String notificationKey = sbn.getKey(); 1562 1563 // Mark notification for one frame. 1564 row.setJustClicked(true); 1565 DejankUtils.postAfterTraversal(new Runnable() { 1566 @Override 1567 public void run() { 1568 row.setJustClicked(false); 1569 } 1570 }); 1571 1572 if (NOTIFICATION_CLICK_DEBUG) { 1573 Log.d(TAG, "Clicked on content of " + notificationKey); 1574 } 1575 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing(); 1576 final boolean afterKeyguardGone = intent.isActivity() 1577 && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(), 1578 mCurrentUserId); 1579 dismissKeyguardThenExecute(new OnDismissAction() { 1580 public boolean onDismiss() { 1581 if (mHeadsUpManager != null && mHeadsUpManager.isHeadsUp(notificationKey)) { 1582 // Release the HUN notification to the shade. 1583 // 1584 // In most cases, when FLAG_AUTO_CANCEL is set, the notification will 1585 // become canceled shortly by NoMan, but we can't assume that. 1586 HeadsUpManager.setIsClickedNotification(row, true); 1587 mHeadsUpManager.releaseImmediately(notificationKey); 1588 } 1589 new Thread() { 1590 @Override 1591 public void run() { 1592 try { 1593 if (keyguardShowing && !afterKeyguardGone) { 1594 ActivityManagerNative.getDefault() 1595 .keyguardWaitingForActivityDrawn(); 1596 } 1597 1598 // The intent we are sending is for the application, which 1599 // won't have permission to immediately start an activity after 1600 // the user switches to home. We know it is safe to do at this 1601 // point, so make sure new activity switches are now allowed. 1602 ActivityManagerNative.getDefault().resumeAppSwitches(); 1603 } catch (RemoteException e) { 1604 } 1605 1606 if (intent != null) { 1607 try { 1608 intent.send(); 1609 } catch (PendingIntent.CanceledException e) { 1610 // the stack trace isn't very helpful here. 1611 // Just log the exception message. 1612 Log.w(TAG, "Sending contentIntent failed: " + e); 1613 1614 // TODO: Dismiss Keyguard. 1615 } 1616 if (intent.isActivity()) { 1617 mAssistManager.hideAssist(); 1618 overrideActivityPendingAppTransition(keyguardShowing 1619 && !afterKeyguardGone); 1620 } 1621 } 1622 1623 try { 1624 mBarService.onNotificationClick(notificationKey); 1625 } catch (RemoteException ex) { 1626 // system process is dead if we're here. 1627 } 1628 } 1629 }.start(); 1630 1631 // close the shade if it was open 1632 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, 1633 true /* force */, true /* delayed */); 1634 visibilityChanged(false); 1635 1636 return true; 1637 } 1638 }, afterKeyguardGone); 1639 } 1640 1641 public void register(ExpandableNotificationRow row, StatusBarNotification sbn) { 1642 final PendingIntent contentIntent = sbn.getNotification().contentIntent; 1643 if (contentIntent != null) { 1644 row.setOnClickListener(this); 1645 } else { 1646 row.setOnClickListener(null); 1647 } 1648 } 1649 } 1650 1651 public void animateCollapsePanels(int flags, boolean force) { 1652 } 1653 1654 public void animateCollapsePanels(int flags, boolean force, boolean delayed) { 1655 } 1656 1657 public void overrideActivityPendingAppTransition(boolean keyguardShowing) { 1658 if (keyguardShowing) { 1659 try { 1660 mWindowManagerService.overridePendingAppTransition(null, 0, 0, null); 1661 } catch (RemoteException e) { 1662 Log.w(TAG, "Error overriding app transition: " + e); 1663 } 1664 } 1665 } 1666 1667 protected void visibilityChanged(boolean visible) { 1668 if (mVisible != visible) { 1669 mVisible = visible; 1670 if (!visible) { 1671 dismissPopups(); 1672 } 1673 } 1674 updateVisibleToUser(); 1675 } 1676 1677 protected void updateVisibleToUser() { 1678 boolean oldVisibleToUser = mVisibleToUser; 1679 mVisibleToUser = mVisible && mDeviceInteractive; 1680 1681 if (oldVisibleToUser != mVisibleToUser) { 1682 handleVisibleToUserChanged(mVisibleToUser); 1683 } 1684 } 1685 1686 /** 1687 * The LEDs are turned off when the notification panel is shown, even just a little bit. 1688 */ 1689 protected void handleVisibleToUserChanged(boolean visibleToUser) { 1690 try { 1691 if (visibleToUser) { 1692 boolean pinnedHeadsUp = mHeadsUpManager.hasPinnedHeadsUp(); 1693 boolean clearNotificationEffects = 1694 ((mShowLockscreenNotifications && mState == StatusBarState.KEYGUARD) || 1695 (!pinnedHeadsUp && (mState == StatusBarState.SHADE 1696 || mState == StatusBarState.SHADE_LOCKED))); 1697 int notificationLoad = mNotificationData.getActiveNotifications().size(); 1698 if (pinnedHeadsUp && isPanelFullyCollapsed()) { 1699 notificationLoad = 1; 1700 } else { 1701 MetricsLogger.histogram(mContext, "note_load", notificationLoad); 1702 } 1703 mBarService.onPanelRevealed(clearNotificationEffects, notificationLoad); 1704 } else { 1705 mBarService.onPanelHidden(); 1706 } 1707 } catch (RemoteException ex) { 1708 // Won't fail unless the world has ended. 1709 } 1710 } 1711 1712 /** 1713 * Clear Buzz/Beep/Blink. 1714 */ 1715 public void clearNotificationEffects() { 1716 try { 1717 mBarService.clearNotificationEffects(); 1718 } catch (RemoteException e) { 1719 // Won't fail unless the world has ended. 1720 } 1721 } 1722 1723 protected abstract boolean isPanelFullyCollapsed(); 1724 1725 /** 1726 * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService 1727 * about the failure. 1728 * 1729 * WARNING: this will call back into us. Don't hold any locks. 1730 */ 1731 void handleNotificationError(StatusBarNotification n, String message) { 1732 removeNotification(n.getKey(), null); 1733 try { 1734 mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(), 1735 n.getInitialPid(), message, n.getUserId()); 1736 } catch (RemoteException ex) { 1737 // The end is nigh. 1738 } 1739 } 1740 1741 protected StatusBarNotification removeNotificationViews(String key, RankingMap ranking) { 1742 NotificationData.Entry entry = mNotificationData.remove(key, ranking); 1743 if (entry == null) { 1744 Log.w(TAG, "removeNotification for unknown key: " + key); 1745 return null; 1746 } 1747 updateNotifications(); 1748 return entry.notification; 1749 } 1750 1751 protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn) { 1752 if (DEBUG) { 1753 Log.d(TAG, "createNotificationViews(notification=" + sbn); 1754 } 1755 final StatusBarIconView iconView = createIcon(sbn); 1756 if (iconView == null) { 1757 return null; 1758 } 1759 1760 // Construct the expanded view. 1761 NotificationData.Entry entry = new NotificationData.Entry(sbn, iconView); 1762 if (!inflateViews(entry, mStackScroller)) { 1763 handleNotificationError(sbn, "Couldn't expand RemoteViews for: " + sbn); 1764 return null; 1765 } 1766 return entry; 1767 } 1768 1769 protected StatusBarIconView createIcon(StatusBarNotification sbn) { 1770 // Construct the icon. 1771 Notification n = sbn.getNotification(); 1772 final StatusBarIconView iconView = new StatusBarIconView(mContext, 1773 sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), n); 1774 iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE); 1775 1776 final Icon smallIcon = n.getSmallIcon(); 1777 if (smallIcon == null) { 1778 handleNotificationError(sbn, 1779 "No small icon in notification from " + sbn.getPackageName()); 1780 return null; 1781 } 1782 final StatusBarIcon ic = new StatusBarIcon( 1783 sbn.getUser(), 1784 sbn.getPackageName(), 1785 smallIcon, 1786 n.iconLevel, 1787 n.number, 1788 n.tickerText); 1789 if (!iconView.set(ic)) { 1790 handleNotificationError(sbn, "Couldn't create icon: " + ic); 1791 return null; 1792 } 1793 return iconView; 1794 } 1795 1796 protected void addNotificationViews(Entry entry, RankingMap ranking) { 1797 if (entry == null) { 1798 return; 1799 } 1800 // Add the expanded view and icon. 1801 mNotificationData.add(entry, ranking); 1802 updateNotifications(); 1803 } 1804 1805 /** 1806 * @return The number of notifications we show on Keyguard. 1807 */ 1808 protected abstract int getMaxKeyguardNotifications(); 1809 1810 /** 1811 * Updates expanded, dimmed and locked states of notification rows. 1812 */ 1813 protected void updateRowStates() { 1814 int maxKeyguardNotifications = getMaxKeyguardNotifications(); 1815 mKeyguardIconOverflowContainer.getIconsView().removeAllViews(); 1816 1817 ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications(); 1818 final int N = activeNotifications.size(); 1819 1820 int visibleNotifications = 0; 1821 boolean onKeyguard = mState == StatusBarState.KEYGUARD; 1822 for (int i = 0; i < N; i++) { 1823 NotificationData.Entry entry = activeNotifications.get(i); 1824 if (onKeyguard) { 1825 entry.row.setExpansionDisabled(true); 1826 } else { 1827 entry.row.setExpansionDisabled(false); 1828 if (!entry.row.isUserLocked()) { 1829 boolean top = (i == 0); 1830 entry.row.setSystemExpanded(top); 1831 } 1832 } 1833 boolean isInvisibleChild = !mGroupManager.isVisible(entry.notification); 1834 boolean showOnKeyguard = shouldShowOnKeyguard(entry.notification); 1835 if ((isLockscreenPublicMode() && !mShowLockscreenNotifications) || 1836 (onKeyguard && (visibleNotifications >= maxKeyguardNotifications 1837 || !showOnKeyguard || isInvisibleChild))) { 1838 entry.row.setVisibility(View.GONE); 1839 if (onKeyguard && showOnKeyguard && !isInvisibleChild) { 1840 mKeyguardIconOverflowContainer.getIconsView().addNotification(entry); 1841 } 1842 } else { 1843 boolean wasGone = entry.row.getVisibility() == View.GONE; 1844 entry.row.setVisibility(View.VISIBLE); 1845 if (!isInvisibleChild) { 1846 if (wasGone) { 1847 // notify the scroller of a child addition 1848 mStackScroller.generateAddAnimation(entry.row, true /* fromMoreCard */); 1849 } 1850 visibleNotifications++; 1851 } 1852 } 1853 } 1854 1855 mStackScroller.updateOverflowContainerVisibility(onKeyguard 1856 && mKeyguardIconOverflowContainer.getIconsView().getChildCount() > 0); 1857 1858 mStackScroller.changeViewPosition(mDismissView, mStackScroller.getChildCount() - 1); 1859 mStackScroller.changeViewPosition(mEmptyShadeView, mStackScroller.getChildCount() - 2); 1860 mStackScroller.changeViewPosition(mKeyguardIconOverflowContainer, 1861 mStackScroller.getChildCount() - 3); 1862 } 1863 1864 private boolean shouldShowOnKeyguard(StatusBarNotification sbn) { 1865 return mShowLockscreenNotifications && !mNotificationData.isAmbient(sbn.getKey()); 1866 } 1867 1868 protected void setZenMode(int mode) { 1869 if (!isDeviceProvisioned()) return; 1870 mZenMode = mode; 1871 updateNotifications(); 1872 } 1873 1874 // extended in PhoneStatusBar 1875 protected void setShowLockscreenNotifications(boolean show) { 1876 mShowLockscreenNotifications = show; 1877 } 1878 1879 private void updateLockscreenNotificationSetting() { 1880 final boolean show = Settings.Secure.getIntForUser(mContext.getContentResolver(), 1881 Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1882 1, 1883 mCurrentUserId) != 0; 1884 final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures( 1885 null /* admin */, mCurrentUserId); 1886 final boolean allowedByDpm = (dpmFlags 1887 & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0; 1888 setShowLockscreenNotifications(show && allowedByDpm); 1889 } 1890 1891 protected abstract void setAreThereNotifications(); 1892 protected abstract void updateNotifications(); 1893 public abstract boolean shouldDisableNavbarGestures(); 1894 1895 public abstract void addNotification(StatusBarNotification notification, 1896 RankingMap ranking, Entry oldEntry); 1897 protected abstract void updateNotificationRanking(RankingMap ranking); 1898 public abstract void removeNotification(String key, RankingMap ranking); 1899 1900 public void updateNotification(StatusBarNotification notification, RankingMap ranking) { 1901 if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")"); 1902 1903 final String key = notification.getKey(); 1904 Entry entry = mNotificationData.get(key); 1905 if (entry == null) { 1906 return; 1907 } 1908 1909 Notification n = notification.getNotification(); 1910 if (DEBUG) { 1911 logUpdate(entry, n); 1912 } 1913 boolean applyInPlace = shouldApplyInPlace(entry, n); 1914 boolean shouldInterrupt = shouldInterrupt(entry, notification); 1915 boolean alertAgain = alertAgain(entry, n); 1916 1917 entry.notification = notification; 1918 mGroupManager.onEntryUpdated(entry, entry.notification); 1919 1920 boolean updateSuccessful = false; 1921 if (applyInPlace) { 1922 if (DEBUG) Log.d(TAG, "reusing notification for key: " + key); 1923 try { 1924 if (entry.icon != null) { 1925 // Update the icon 1926 final StatusBarIcon ic = new StatusBarIcon( 1927 notification.getUser(), 1928 notification.getPackageName(), 1929 n.getSmallIcon(), 1930 n.iconLevel, 1931 n.number, 1932 n.tickerText); 1933 entry.icon.setNotification(n); 1934 if (!entry.icon.set(ic)) { 1935 handleNotificationError(notification, "Couldn't update icon: " + ic); 1936 return; 1937 } 1938 } 1939 updateNotificationViews(entry, notification); 1940 updateSuccessful = true; 1941 } 1942 catch (RuntimeException e) { 1943 // It failed to apply cleanly. 1944 Log.w(TAG, "Couldn't reapply views for package " + n.contentView.getPackage(), e); 1945 } 1946 } 1947 if (!updateSuccessful) { 1948 if (DEBUG) Log.d(TAG, "not reusing notification for key: " + key); 1949 final StatusBarIcon ic = new StatusBarIcon( 1950 notification.getUser(), 1951 notification.getPackageName(), 1952 n.getSmallIcon(), 1953 n.iconLevel, 1954 n.number, 1955 n.tickerText); 1956 entry.icon.setNotification(n); 1957 entry.icon.set(ic); 1958 inflateViews(entry, mStackScroller); 1959 } 1960 updateHeadsUp(key, entry, shouldInterrupt, alertAgain); 1961 mNotificationData.updateRanking(ranking); 1962 updateNotifications(); 1963 1964 // Update the veto button accordingly (and as a result, whether this row is 1965 // swipe-dismissable) 1966 updateNotificationVetoButton(entry.row, notification); 1967 1968 if (DEBUG) { 1969 // Is this for you? 1970 boolean isForCurrentUser = isNotificationForCurrentProfiles(notification); 1971 Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you"); 1972 } 1973 1974 setAreThereNotifications(); 1975 } 1976 1977 protected abstract void updateHeadsUp(String key, Entry entry, boolean shouldInterrupt, 1978 boolean alertAgain); 1979 1980 private void logUpdate(Entry oldEntry, Notification n) { 1981 StatusBarNotification oldNotification = oldEntry.notification; 1982 Log.d(TAG, "old notification: when=" + oldNotification.getNotification().when 1983 + " ongoing=" + oldNotification.isOngoing() 1984 + " expanded=" + oldEntry.getContentView() 1985 + " contentView=" + oldNotification.getNotification().contentView 1986 + " bigContentView=" + oldNotification.getNotification().bigContentView 1987 + " publicView=" + oldNotification.getNotification().publicVersion 1988 + " rowParent=" + oldEntry.row.getParent()); 1989 Log.d(TAG, "new notification: when=" + n.when 1990 + " ongoing=" + oldNotification.isOngoing() 1991 + " contentView=" + n.contentView 1992 + " bigContentView=" + n.bigContentView 1993 + " publicView=" + n.publicVersion); 1994 } 1995 1996 /** 1997 * @return whether we can just reapply the RemoteViews from a notification in-place when it is 1998 * updated 1999 */ 2000 private boolean shouldApplyInPlace(Entry entry, Notification n) { 2001 StatusBarNotification oldNotification = entry.notification; 2002 // XXX: modify when we do something more intelligent with the two content views 2003 final RemoteViews oldContentView = oldNotification.getNotification().contentView; 2004 final RemoteViews contentView = n.contentView; 2005 final RemoteViews oldBigContentView = oldNotification.getNotification().bigContentView; 2006 final RemoteViews bigContentView = n.bigContentView; 2007 final RemoteViews oldHeadsUpContentView 2008 = oldNotification.getNotification().headsUpContentView; 2009 final RemoteViews headsUpContentView = n.headsUpContentView; 2010 final Notification oldPublicNotification = oldNotification.getNotification().publicVersion; 2011 final RemoteViews oldPublicContentView = oldPublicNotification != null 2012 ? oldPublicNotification.contentView : null; 2013 final Notification publicNotification = n.publicVersion; 2014 final RemoteViews publicContentView = publicNotification != null 2015 ? publicNotification.contentView : null; 2016 boolean contentsUnchanged = entry.getContentView() != null 2017 && contentView.getPackage() != null 2018 && oldContentView.getPackage() != null 2019 && oldContentView.getPackage().equals(contentView.getPackage()) 2020 && oldContentView.getLayoutId() == contentView.getLayoutId(); 2021 // large view may be null 2022 boolean bigContentsUnchanged = 2023 (entry.getExpandedContentView() == null && bigContentView == null) 2024 || ((entry.getExpandedContentView() != null && bigContentView != null) 2025 && bigContentView.getPackage() != null 2026 && oldBigContentView.getPackage() != null 2027 && oldBigContentView.getPackage().equals(bigContentView.getPackage()) 2028 && oldBigContentView.getLayoutId() == bigContentView.getLayoutId()); 2029 boolean headsUpContentsUnchanged = 2030 (oldHeadsUpContentView == null && headsUpContentView == null) 2031 || ((oldHeadsUpContentView != null && headsUpContentView != null) 2032 && headsUpContentView.getPackage() != null 2033 && oldHeadsUpContentView.getPackage() != null 2034 && oldHeadsUpContentView.getPackage().equals(headsUpContentView.getPackage()) 2035 && oldHeadsUpContentView.getLayoutId() == headsUpContentView.getLayoutId()); 2036 boolean publicUnchanged = 2037 (oldPublicContentView == null && publicContentView == null) 2038 || ((oldPublicContentView != null && publicContentView != null) 2039 && publicContentView.getPackage() != null 2040 && oldPublicContentView.getPackage() != null 2041 && oldPublicContentView.getPackage().equals(publicContentView.getPackage()) 2042 && oldPublicContentView.getLayoutId() == publicContentView.getLayoutId()); 2043 return contentsUnchanged && bigContentsUnchanged && headsUpContentsUnchanged 2044 && publicUnchanged; 2045 } 2046 2047 private void updateNotificationViews(Entry entry, StatusBarNotification notification) { 2048 final RemoteViews contentView = notification.getNotification().contentView; 2049 final RemoteViews bigContentView = notification.getNotification().bigContentView; 2050 final RemoteViews headsUpContentView = notification.getNotification().headsUpContentView; 2051 final Notification publicVersion = notification.getNotification().publicVersion; 2052 final RemoteViews publicContentView = publicVersion != null ? publicVersion.contentView 2053 : null; 2054 2055 // Reapply the RemoteViews 2056 contentView.reapply(mContext, entry.getContentView(), mOnClickHandler); 2057 if (bigContentView != null && entry.getExpandedContentView() != null) { 2058 bigContentView.reapply(notification.getPackageContext(mContext), 2059 entry.getExpandedContentView(), 2060 mOnClickHandler); 2061 } 2062 View headsUpChild = entry.getHeadsUpContentView(); 2063 if (headsUpContentView != null && headsUpChild != null) { 2064 headsUpContentView.reapply(notification.getPackageContext(mContext), 2065 headsUpChild, mOnClickHandler); 2066 } 2067 if (publicContentView != null && entry.getPublicContentView() != null) { 2068 publicContentView.reapply(notification.getPackageContext(mContext), 2069 entry.getPublicContentView(), mOnClickHandler); 2070 } 2071 // update the contentIntent 2072 mNotificationClicker.register(entry.row, notification); 2073 2074 entry.row.setStatusBarNotification(notification); 2075 entry.row.notifyContentUpdated(); 2076 entry.row.resetHeight(); 2077 } 2078 2079 protected void notifyHeadsUpScreenOff() { 2080 maybeEscalateHeadsUp(); 2081 } 2082 2083 private boolean alertAgain(Entry oldEntry, Notification newNotification) { 2084 return oldEntry == null || !oldEntry.hasInterrupted() 2085 || (newNotification.flags & Notification.FLAG_ONLY_ALERT_ONCE) == 0; 2086 } 2087 2088 protected boolean shouldInterrupt(Entry entry) { 2089 return shouldInterrupt(entry, entry.notification); 2090 } 2091 2092 protected boolean shouldInterrupt(Entry entry, StatusBarNotification sbn) { 2093 if (mNotificationData.shouldFilterOut(sbn)) { 2094 if (DEBUG) { 2095 Log.d(TAG, "Skipping HUN check for " + sbn.getKey() + " since it's filtered out."); 2096 } 2097 return false; 2098 } 2099 2100 if (isSnoozedPackage(sbn)) { 2101 return false; 2102 } 2103 2104 Notification notification = sbn.getNotification(); 2105 // some predicates to make the boolean logic legible 2106 boolean isNoisy = (notification.defaults & Notification.DEFAULT_SOUND) != 0 2107 || (notification.defaults & Notification.DEFAULT_VIBRATE) != 0 2108 || notification.sound != null 2109 || notification.vibrate != null; 2110 boolean isHighPriority = sbn.getScore() >= INTERRUPTION_THRESHOLD; 2111 boolean isFullscreen = notification.fullScreenIntent != null; 2112 boolean hasTicker = mHeadsUpTicker && !TextUtils.isEmpty(notification.tickerText); 2113 boolean isAllowed = notification.extras.getInt(Notification.EXTRA_AS_HEADS_UP, 2114 Notification.HEADS_UP_ALLOWED) != Notification.HEADS_UP_NEVER; 2115 boolean accessibilityForcesLaunch = isFullscreen 2116 && mAccessibilityManager.isTouchExplorationEnabled(); 2117 boolean justLaunchedFullScreenIntent = entry.hasJustLaunchedFullScreenIntent(); 2118 2119 boolean interrupt = (isFullscreen || (isHighPriority && (isNoisy || hasTicker))) 2120 && isAllowed 2121 && !accessibilityForcesLaunch 2122 && !justLaunchedFullScreenIntent 2123 && mPowerManager.isScreenOn() 2124 && (!mStatusBarKeyguardViewManager.isShowing() 2125 || mStatusBarKeyguardViewManager.isOccluded()) 2126 && !mStatusBarKeyguardViewManager.isInputRestricted(); 2127 try { 2128 interrupt = interrupt && !mDreamManager.isDreaming(); 2129 } catch (RemoteException e) { 2130 Log.d(TAG, "failed to query dream manager", e); 2131 } 2132 if (DEBUG) Log.d(TAG, "interrupt: " + interrupt); 2133 return interrupt; 2134 } 2135 2136 protected abstract boolean isSnoozedPackage(StatusBarNotification sbn); 2137 2138 public void setInteracting(int barWindow, boolean interacting) { 2139 // hook for subclasses 2140 } 2141 2142 public void setBouncerShowing(boolean bouncerShowing) { 2143 mBouncerShowing = bouncerShowing; 2144 } 2145 2146 /** 2147 * @return Whether the security bouncer from Keyguard is showing. 2148 */ 2149 public boolean isBouncerShowing() { 2150 return mBouncerShowing; 2151 } 2152 2153 public void destroy() { 2154 mContext.unregisterReceiver(mBroadcastReceiver); 2155 try { 2156 mNotificationListener.unregisterAsSystemService(); 2157 } catch (RemoteException e) { 2158 // Ignore. 2159 } 2160 } 2161 2162 /** 2163 * @return a PackageManger for userId or if userId is < 0 (USER_ALL etc) then 2164 * return PackageManager for mContext 2165 */ 2166 protected PackageManager getPackageManagerForUser(int userId) { 2167 Context contextForUser = mContext; 2168 // UserHandle defines special userId as negative values, e.g. USER_ALL 2169 if (userId >= 0) { 2170 try { 2171 // Create a context for the correct user so if a package isn't installed 2172 // for user 0 we can still load information about the package. 2173 contextForUser = 2174 mContext.createPackageContextAsUser(mContext.getPackageName(), 2175 Context.CONTEXT_RESTRICTED, 2176 new UserHandle(userId)); 2177 } catch (NameNotFoundException e) { 2178 // Shouldn't fail to find the package name for system ui. 2179 } 2180 } 2181 return contextForUser.getPackageManager(); 2182 } 2183 2184 @Override 2185 public void logNotificationExpansion(String key, boolean userAction, boolean expanded) { 2186 try { 2187 mBarService.onNotificationExpansionChanged(key, userAction, expanded); 2188 } catch (RemoteException e) { 2189 // Ignore. 2190 } 2191 } 2192 2193 public boolean isKeyguardSecure() { 2194 if (mStatusBarKeyguardViewManager == null) { 2195 // startKeyguard() hasn't been called yet, so we don't know. 2196 // Make sure anything that needs to know isKeyguardSecure() checks and re-checks this 2197 // value onVisibilityChanged(). 2198 Slog.w(TAG, "isKeyguardSecure() called before startKeyguard(), returning false", 2199 new Throwable()); 2200 return false; 2201 } 2202 return mStatusBarKeyguardViewManager.isSecure(); 2203 } 2204 2205 @Override 2206 public void showAssistDisclosure() { 2207 if (mAssistManager != null) { 2208 mAssistManager.showDisclosure(); 2209 } 2210 } 2211 2212 @Override 2213 public void startAssist(Bundle args) { 2214 if (mAssistManager != null) { 2215 mAssistManager.startAssist(args); 2216 } 2217 } 2218 } 2219