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