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