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