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