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 17package com.android.systemui.statusbar; 18 19import android.animation.Animator; 20import android.animation.AnimatorListenerAdapter; 21import android.animation.TimeInterpolator; 22import android.app.ActivityManager; 23import android.app.ActivityManagerNative; 24import android.app.Notification; 25import android.app.NotificationManager; 26import android.app.PendingIntent; 27import android.app.TaskStackBuilder; 28import android.app.admin.DevicePolicyManager; 29import android.content.BroadcastReceiver; 30import android.content.ComponentName; 31import android.content.Context; 32import android.content.Intent; 33import android.content.IntentFilter; 34import android.content.pm.ApplicationInfo; 35import android.content.pm.PackageManager; 36import android.content.pm.PackageManager.NameNotFoundException; 37import android.content.pm.ResolveInfo; 38import android.content.pm.UserInfo; 39import android.content.res.Configuration; 40import android.content.res.Resources; 41import android.database.ContentObserver; 42import android.graphics.PorterDuff; 43import android.graphics.drawable.Drawable; 44import android.graphics.drawable.Icon; 45import android.os.AsyncTask; 46import android.os.Build; 47import android.os.Bundle; 48import android.os.Handler; 49import android.os.IBinder; 50import android.os.Message; 51import android.os.PowerManager; 52import android.os.RemoteException; 53import android.os.ServiceManager; 54import android.os.SystemProperties; 55import android.os.UserHandle; 56import android.os.UserManager; 57import android.provider.Settings; 58import android.service.dreams.DreamService; 59import android.service.dreams.IDreamManager; 60import android.service.notification.NotificationListenerService; 61import android.service.notification.NotificationListenerService.RankingMap; 62import android.service.notification.StatusBarNotification; 63import android.text.TextUtils; 64import android.util.Log; 65import android.util.Slog; 66import android.util.SparseArray; 67import android.util.SparseBooleanArray; 68import android.view.Display; 69import android.view.IWindowManager; 70import android.view.LayoutInflater; 71import android.view.MotionEvent; 72import android.view.View; 73import android.view.ViewAnimationUtils; 74import android.view.ViewGroup; 75import android.view.ViewParent; 76import android.view.WindowManager; 77import android.view.WindowManagerGlobal; 78import android.view.accessibility.AccessibilityManager; 79import android.view.animation.AnimationUtils; 80import android.widget.DateTimeView; 81import android.widget.ImageView; 82import android.widget.RemoteViews; 83import android.widget.TextView; 84import android.widget.Toast; 85 86import com.android.internal.logging.MetricsLogger; 87import com.android.internal.statusbar.IStatusBarService; 88import com.android.internal.statusbar.StatusBarIcon; 89import com.android.internal.statusbar.StatusBarIconList; 90import com.android.internal.util.NotificationColorUtil; 91import com.android.internal.widget.LockPatternUtils; 92import com.android.keyguard.KeyguardUpdateMonitor; 93import com.android.systemui.DejankUtils; 94import com.android.systemui.R; 95import com.android.systemui.RecentsComponent; 96import com.android.systemui.SwipeHelper; 97import com.android.systemui.SystemUI; 98import com.android.systemui.assist.AssistManager; 99import com.android.systemui.recents.Recents; 100import com.android.systemui.statusbar.NotificationData.Entry; 101import com.android.systemui.statusbar.phone.NavigationBarView; 102import com.android.systemui.statusbar.phone.NotificationGroupManager; 103import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; 104import com.android.systemui.statusbar.policy.HeadsUpManager; 105import com.android.systemui.statusbar.policy.PreviewInflater; 106import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; 107 108import java.util.ArrayList; 109import java.util.List; 110import java.util.Locale; 111 112import static com.android.keyguard.KeyguardHostView.OnDismissAction; 113 114public 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 */ 205 protected abstract void createAndAddWindows(); 206 207 protected WindowManager mWindowManager; 208 protected IWindowManager mWindowManagerService; 209 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 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 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 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 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 710 public void userSwitched(int newUserId) { 711 setHeadsUpUser(newUserId); 712 } 713 714 protected abstract void setHeadsUpUser(int newUserId); 715 716 @Override // NotificationData.Environment 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 727 protected void setNotificationShown(StatusBarNotification n) { 728 setNotificationsShown(new String[]{n.getKey()}); 729 } 730 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 739 protected boolean isCurrentProfile(int userId) { 740 synchronized (mCurrentProfiles) { 741 return userId == UserHandle.USER_ALL || mCurrentProfiles.get(userId) != null; 742 } 743 } 744 745 @Override 746 public String getCurrentMediaNotificationKey() { 747 return null; 748 } 749 750 @Override 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 */ 760 protected void dismissKeyguardThenExecute(OnDismissAction action, boolean afterKeyguardGone) { 761 action.onDismiss(); 762 } 763 764 @Override 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 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 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 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 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 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 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 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 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