2 * Copyright (C) 2010 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.systemui.statusbar;
19 import android.animation.Animator;
20 import android.animation.AnimatorListenerAdapter;
21 import android.animation.TimeInterpolator;
22 import android.app.ActivityManager;
23 import android.app.ActivityManagerNative;
24 import android.app.Notification;
25 import android.app.NotificationManager;
26 import android.app.PendingIntent;
27 import android.app.TaskStackBuilder;
28 import android.app.admin.DevicePolicyManager;
29 import android.content.BroadcastReceiver;
30 import android.content.ComponentName;
31 import android.content.Context;
32 import android.content.Intent;
33 import android.content.IntentFilter;
34 import android.content.pm.ApplicationInfo;
35 import android.content.pm.PackageManager;
36 import android.content.pm.PackageManager.NameNotFoundException;
37 import android.content.pm.ResolveInfo;
38 import android.content.pm.UserInfo;
39 import android.content.res.Configuration;
40 import android.content.res.Resources;
41 import android.database.ContentObserver;
42 import android.graphics.PorterDuff;
43 import android.graphics.drawable.Drawable;
44 import android.os.AsyncTask;
45 import android.os.Build;
46 import android.os.Handler;
47 import android.os.IBinder;
48 import android.os.Message;
49 import android.os.PowerManager;
50 import android.os.RemoteException;
51 import android.os.ServiceManager;
52 import android.os.UserHandle;
53 import android.os.UserManager;
54 import android.provider.Settings;
55 import android.service.dreams.DreamService;
56 import android.service.dreams.IDreamManager;
57 import android.service.notification.NotificationListenerService;
58 import android.service.notification.NotificationListenerService.RankingMap;
59 import android.service.notification.StatusBarNotification;
60 import android.text.TextUtils;
61 import android.util.Log;
62 import android.util.Slog;
63 import android.util.SparseArray;
64 import android.util.SparseBooleanArray;
65 import android.view.Display;
66 import android.view.IWindowManager;
67 import android.view.LayoutInflater;
68 import android.view.MotionEvent;
69 import android.view.View;
70 import android.view.ViewAnimationUtils;
71 import android.view.ViewGroup;
72 import android.view.ViewGroup.LayoutParams;
73 import android.view.ViewParent;
74 import android.view.ViewStub;
75 import android.view.WindowManager;
76 import android.view.WindowManagerGlobal;
77 import android.view.accessibility.AccessibilityManager;
78 import android.view.animation.AnimationUtils;
79 import android.widget.DateTimeView;
80 import android.widget.ImageView;
81 import android.widget.LinearLayout;
82 import android.widget.RemoteViews;
83 import android.widget.TextView;
85 import com.android.internal.statusbar.IStatusBarService;
86 import com.android.internal.statusbar.StatusBarIcon;
87 import com.android.internal.statusbar.StatusBarIconList;
88 import com.android.internal.util.NotificationColorUtil;
89 import com.android.internal.widget.LockPatternUtils;
90 import com.android.systemui.R;
91 import com.android.systemui.RecentsComponent;
92 import com.android.systemui.SearchPanelView;
93 import com.android.systemui.SwipeHelper;
94 import com.android.systemui.SystemUI;
95 import com.android.systemui.statusbar.NotificationData.Entry;
96 import com.android.systemui.statusbar.phone.NavigationBarView;
97 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
98 import com.android.systemui.statusbar.policy.HeadsUpNotificationView;
99 import com.android.systemui.statusbar.policy.PreviewInflater;
100 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
102 import java.util.ArrayList;
103 import java.util.List;
104 import java.util.Locale;
106 import static com.android.keyguard.KeyguardHostView.OnDismissAction;
108 public abstract class BaseStatusBar extends SystemUI implements
109 CommandQueue.Callbacks, ActivatableNotificationView.OnActivatedListener,
110 RecentsComponent.Callbacks, ExpandableNotificationRow.ExpansionLogger,
111 NotificationData.Environment {
112 public static final String TAG = "StatusBar";
113 public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
114 public static final boolean MULTIUSER_DEBUG = false;
116 // STOPSHIP disable once we resolve b/18102199
117 private static final boolean NOTIFICATION_CLICK_DEBUG = true;
119 protected static final int MSG_SHOW_RECENT_APPS = 1019;
120 protected static final int MSG_HIDE_RECENT_APPS = 1020;
121 protected static final int MSG_TOGGLE_RECENTS_APPS = 1021;
122 protected static final int MSG_PRELOAD_RECENT_APPS = 1022;
123 protected static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 1023;
124 protected static final int MSG_SHOW_NEXT_AFFILIATED_TASK = 1024;
125 protected static final int MSG_SHOW_PREV_AFFILIATED_TASK = 1025;
126 protected static final int MSG_CLOSE_SEARCH_PANEL = 1027;
127 protected static final int MSG_SHOW_HEADS_UP = 1028;
128 protected static final int MSG_HIDE_HEADS_UP = 1029;
129 protected static final int MSG_ESCALATE_HEADS_UP = 1030;
130 protected static final int MSG_DECAY_HEADS_UP = 1031;
132 protected static final boolean ENABLE_HEADS_UP = true;
133 // scores above this threshold should be displayed in heads up mode.
134 protected static final int INTERRUPTION_THRESHOLD = 10;
135 protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
137 // Should match the value in PhoneWindowManager
138 public static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
140 public static final int EXPANDED_LEAVE_ALONE = -10000;
141 public static final int EXPANDED_FULL_OPEN = -10001;
143 private static final int HIDDEN_NOTIFICATION_ID = 10000;
144 private static final String BANNER_ACTION_CANCEL =
145 "com.android.systemui.statusbar.banner_action_cancel";
146 private static final String BANNER_ACTION_SETUP =
147 "com.android.systemui.statusbar.banner_action_setup";
149 protected CommandQueue mCommandQueue;
150 protected IStatusBarService mBarService;
151 protected H mHandler = createHandler();
154 protected NotificationData mNotificationData;
155 protected NotificationStackScrollLayout mStackScroller;
157 // for heads up notifications
158 protected HeadsUpNotificationView mHeadsUpNotificationView;
159 protected int mHeadsUpNotificationDecay;
162 protected SearchPanelView mSearchPanelView;
164 protected int mCurrentUserId = 0;
165 final protected SparseArray<UserInfo> mCurrentProfiles = new SparseArray<UserInfo>();
167 protected int mLayoutDirection = -1; // invalid
168 protected AccessibilityManager mAccessibilityManager;
170 // on-screen navigation buttons
171 protected NavigationBarView mNavigationBarView = null;
173 protected Boolean mScreenOn;
175 // The second field is a bit different from the first one because it only listens to screen on/
176 // screen of events from Keyguard. We need this so we don't have a race condition with the
177 // broadcast. In the future, we should remove the first field altogether and rename the second
179 protected boolean mScreenOnFromKeyguard;
181 protected boolean mVisible;
183 // mScreenOnFromKeyguard && mVisible.
184 private boolean mVisibleToUser;
186 private Locale mLocale;
187 private float mFontScale;
189 protected boolean mUseHeadsUp = false;
190 protected boolean mHeadsUpTicker = false;
191 protected boolean mDisableNotificationAlerts = false;
193 protected DevicePolicyManager mDevicePolicyManager;
194 protected IDreamManager mDreamManager;
195 PowerManager mPowerManager;
196 protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
197 protected int mRowMinHeight;
198 protected int mRowMaxHeight;
200 // public mode, private notifications, etc
201 private boolean mLockscreenPublicMode = false;
202 private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray();
203 private NotificationColorUtil mNotificationColorUtil;
205 private UserManager mUserManager;
207 // UI-specific methods
210 * Create all windows necessary for the status bar (including navigation, overlay panels, etc)
211 * and add them to the window manager.
213 protected abstract void createAndAddWindows();
215 protected WindowManager mWindowManager;
216 protected IWindowManager mWindowManagerService;
218 protected abstract void refreshLayout(int layoutDirection);
220 protected Display mDisplay;
222 private boolean mDeviceProvisioned = false;
224 private RecentsComponent mRecents;
226 protected int mZenMode;
228 // which notification is currently being longpress-examined by the user
229 private NotificationGuts mNotificationGutsExposed;
231 private TimeInterpolator mLinearOutSlowIn, mFastOutLinearIn;
234 * The {@link StatusBarState} of the status bar.
236 protected int mState;
237 protected boolean mBouncerShowing;
238 protected boolean mShowLockscreenNotifications;
240 protected NotificationOverflowContainer mKeyguardIconOverflowContainer;
241 protected DismissView mDismissView;
242 protected EmptyShadeView mEmptyShadeView;
244 @Override // NotificationData.Environment
245 public boolean isDeviceProvisioned() {
246 return mDeviceProvisioned;
249 protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
251 public void onChange(boolean selfChange) {
252 final boolean provisioned = 0 != Settings.Global.getInt(
253 mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0);
254 if (provisioned != mDeviceProvisioned) {
255 mDeviceProvisioned = provisioned;
256 updateNotifications();
258 final int mode = Settings.Global.getInt(mContext.getContentResolver(),
259 Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
262 updateLockscreenNotificationSetting();
266 private final ContentObserver mLockscreenSettingsObserver = new ContentObserver(mHandler) {
268 public void onChange(boolean selfChange) {
269 // We don't know which user changed LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
270 // so we just dump our cache ...
271 mUsersAllowingPrivateNotifications.clear();
272 // ... and refresh all the notifications
273 updateNotifications();
277 private RemoteViews.OnClickHandler mOnClickHandler = new RemoteViews.OnClickHandler() {
279 public boolean onClickHandler(
280 final View view, final PendingIntent pendingIntent, final Intent fillInIntent) {
282 Log.v(TAG, "Notification click handler invoked for intent: " + pendingIntent);
284 logActionClick(view);
285 // The intent we are sending is for the application, which
286 // won't have permission to immediately start an activity after
287 // the user switches to home. We know it is safe to do at this
288 // point, so make sure new activity switches are now allowed.
290 ActivityManagerNative.getDefault().resumeAppSwitches();
291 } catch (RemoteException e) {
293 final boolean isActivity = pendingIntent.isActivity();
295 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
296 final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity(
297 mContext, pendingIntent.getIntent(), mCurrentUserId);
298 dismissKeyguardThenExecute(new OnDismissAction() {
300 public boolean onDismiss() {
301 if (keyguardShowing && !afterKeyguardGone) {
303 ActivityManagerNative.getDefault()
304 .keyguardWaitingForActivityDrawn();
305 } catch (RemoteException e) {
309 boolean handled = superOnClickHandler(view, pendingIntent, fillInIntent);
310 overrideActivityPendingAppTransition(keyguardShowing && !afterKeyguardGone);
312 // close the shade if it was open
314 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
316 visibilityChanged(false);
318 // Wait for activity start.
321 }, afterKeyguardGone);
324 return super.onClickHandler(view, pendingIntent, fillInIntent);
328 private void logActionClick(View view) {
329 ViewParent parent = view.getParent();
330 String key = getNotificationKeyForParent(parent);
332 Log.w(TAG, "Couldn't determine notification for click.");
336 // If this is a default template, determine the index of the button.
337 if (view.getId() == com.android.internal.R.id.action0 &&
338 parent != null && parent instanceof ViewGroup) {
339 ViewGroup actionGroup = (ViewGroup) parent;
340 index = actionGroup.indexOfChild(view);
342 if (NOTIFICATION_CLICK_DEBUG) {
343 Log.d(TAG, "Clicked on button " + index + " for " + key);
346 mBarService.onNotificationActionClick(key, index);
347 } catch (RemoteException e) {
352 private String getNotificationKeyForParent(ViewParent parent) {
353 while (parent != null) {
354 if (parent instanceof ExpandableNotificationRow) {
355 return ((ExpandableNotificationRow) parent).getStatusBarNotification().getKey();
357 parent = parent.getParent();
362 private boolean superOnClickHandler(View view, PendingIntent pendingIntent,
363 Intent fillInIntent) {
364 return super.onClickHandler(view, pendingIntent, fillInIntent);
368 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
370 public void onReceive(Context context, Intent intent) {
371 String action = intent.getAction();
372 if (Intent.ACTION_USER_SWITCHED.equals(action)) {
373 mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
374 updateCurrentProfilesCache();
375 if (true) Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
377 updateLockscreenNotificationSetting();
379 userSwitched(mCurrentUserId);
380 } else if (Intent.ACTION_USER_ADDED.equals(action)) {
381 updateCurrentProfilesCache();
382 } else if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(
384 mUsersAllowingPrivateNotifications.clear();
385 updateLockscreenNotificationSetting();
386 updateNotifications();
387 } else if (BANNER_ACTION_CANCEL.equals(action) || BANNER_ACTION_SETUP.equals(action)) {
388 NotificationManager noMan = (NotificationManager)
389 mContext.getSystemService(Context.NOTIFICATION_SERVICE);
390 noMan.cancel(HIDDEN_NOTIFICATION_ID);
392 Settings.Secure.putInt(mContext.getContentResolver(),
393 Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
394 if (BANNER_ACTION_SETUP.equals(action)) {
395 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
397 mContext.startActivity(new Intent(Settings.ACTION_APP_NOTIFICATION_REDACTION)
398 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
406 private final NotificationListenerService mNotificationListener =
407 new NotificationListenerService() {
409 public void onListenerConnected() {
410 if (DEBUG) Log.d(TAG, "onListenerConnected");
411 final StatusBarNotification[] notifications = getActiveNotifications();
412 final RankingMap currentRanking = getCurrentRanking();
413 mHandler.post(new Runnable() {
416 for (StatusBarNotification sbn : notifications) {
417 addNotification(sbn, currentRanking);
424 public void onNotificationPosted(final StatusBarNotification sbn,
425 final RankingMap rankingMap) {
426 if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
427 mHandler.post(new Runnable() {
430 Notification n = sbn.getNotification();
431 boolean isUpdate = mNotificationData.get(sbn.getKey()) != null
432 || isHeadsUp(sbn.getKey());
434 // Ignore children of notifications that have a summary, since we're not
435 // going to show them anyway. This is true also when the summary is canceled,
436 // because children are automatically canceled by NoMan in that case.
437 if (n.isGroupChild() &&
438 mNotificationData.isGroupWithSummary(sbn.getGroupKey())) {
440 Log.d(TAG, "Ignoring group child due to existing summary: " + sbn);
443 // Remove existing notification to avoid stale data.
445 removeNotification(sbn.getKey(), rankingMap);
447 mNotificationData.updateRanking(rankingMap);
452 updateNotification(sbn, rankingMap);
454 addNotification(sbn, rankingMap);
461 public void onNotificationRemoved(final StatusBarNotification sbn,
462 final RankingMap rankingMap) {
463 if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn);
464 mHandler.post(new Runnable() {
467 removeNotification(sbn.getKey(), rankingMap);
473 public void onNotificationRankingUpdate(final RankingMap rankingMap) {
474 if (DEBUG) Log.d(TAG, "onRankingUpdate");
475 mHandler.post(new Runnable() {
478 updateNotificationRanking(rankingMap);
485 private void updateCurrentProfilesCache() {
486 synchronized (mCurrentProfiles) {
487 mCurrentProfiles.clear();
488 if (mUserManager != null) {
489 for (UserInfo user : mUserManager.getProfiles(mCurrentUserId)) {
490 mCurrentProfiles.put(user.id, user);
496 public void start() {
497 mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
498 mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
499 mDisplay = mWindowManager.getDefaultDisplay();
500 mDevicePolicyManager = (DevicePolicyManager)mContext.getSystemService(
501 Context.DEVICE_POLICY_SERVICE);
503 mNotificationColorUtil = NotificationColorUtil.getInstance(mContext);
505 mNotificationData = new NotificationData(this);
507 mAccessibilityManager = (AccessibilityManager)
508 mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
510 mDreamManager = IDreamManager.Stub.asInterface(
511 ServiceManager.checkService(DreamService.DREAM_SERVICE));
512 mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
514 mSettingsObserver.onChange(false); // set up
515 mContext.getContentResolver().registerContentObserver(
516 Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), true,
518 mContext.getContentResolver().registerContentObserver(
519 Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false,
521 mContext.getContentResolver().registerContentObserver(
522 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false,
524 UserHandle.USER_ALL);
526 mContext.getContentResolver().registerContentObserver(
527 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
529 mLockscreenSettingsObserver,
530 UserHandle.USER_ALL);
532 mBarService = IStatusBarService.Stub.asInterface(
533 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
535 mRecents = getComponent(RecentsComponent.class);
536 mRecents.setCallback(this);
538 final Configuration currentConfig = mContext.getResources().getConfiguration();
539 mLocale = currentConfig.locale;
540 mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale);
541 mFontScale = currentConfig.fontScale;
543 mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
545 mLinearOutSlowIn = AnimationUtils.loadInterpolator(mContext,
546 android.R.interpolator.linear_out_slow_in);
547 mFastOutLinearIn = AnimationUtils.loadInterpolator(mContext,
548 android.R.interpolator.fast_out_linear_in);
550 // Connect in to the status bar manager service
551 StatusBarIconList iconList = new StatusBarIconList();
552 mCommandQueue = new CommandQueue(this, iconList);
554 int[] switches = new int[8];
555 ArrayList<IBinder> binders = new ArrayList<IBinder>();
557 mBarService.registerStatusBar(mCommandQueue, iconList, switches, binders);
558 } catch (RemoteException ex) {
559 // If the system process isn't there we're doomed anyway.
562 createAndAddWindows();
564 disable(switches[0], false /* animate */);
565 setSystemUiVisibility(switches[1], 0xffffffff);
566 topAppWindowChanged(switches[2] != 0);
567 // StatusBarManagerService has a back up of IME token and it's restored here.
568 setImeWindowStatus(binders.get(0), switches[3], switches[4], switches[5] != 0);
570 // Set up the initial icon state
571 int N = iconList.size();
573 for (int i=0; i<N; i++) {
574 StatusBarIcon icon = iconList.getIcon(i);
576 addIcon(iconList.getSlot(i), i, viewIndex, icon);
581 // Set up the initial notification state.
583 mNotificationListener.registerAsSystemService(mContext,
584 new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),
585 UserHandle.USER_ALL);
586 } catch (RemoteException e) {
587 Log.e(TAG, "Unable to register notification listener", e);
592 Log.d(TAG, String.format(
593 "init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x",
602 mCurrentUserId = ActivityManager.getCurrentUser();
603 setHeadsUpUser(mCurrentUserId);
605 IntentFilter filter = new IntentFilter();
606 filter.addAction(Intent.ACTION_USER_SWITCHED);
607 filter.addAction(Intent.ACTION_USER_ADDED);
608 filter.addAction(BANNER_ACTION_CANCEL);
609 filter.addAction(BANNER_ACTION_SETUP);
610 filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
611 mContext.registerReceiver(mBroadcastReceiver, filter);
613 updateCurrentProfilesCache();
616 protected void notifyUserAboutHiddenNotifications() {
617 if (0 != Settings.Secure.getInt(mContext.getContentResolver(),
618 Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 1)) {
619 Log.d(TAG, "user hasn't seen notification about hidden notifications");
620 final LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
621 if (!lockPatternUtils.isSecure()) {
622 Log.d(TAG, "insecure lockscreen, skipping notification");
623 Settings.Secure.putInt(mContext.getContentResolver(),
624 Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
627 Log.d(TAG, "disabling lockecreen notifications and alerting the user");
628 // disable lockscreen notifications until user acts on the banner.
629 Settings.Secure.putInt(mContext.getContentResolver(),
630 Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
631 Settings.Secure.putInt(mContext.getContentResolver(),
632 Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0);
634 final String packageName = mContext.getPackageName();
635 PendingIntent cancelIntent = PendingIntent.getBroadcast(mContext, 0,
636 new Intent(BANNER_ACTION_CANCEL).setPackage(packageName),
637 PendingIntent.FLAG_CANCEL_CURRENT);
638 PendingIntent setupIntent = PendingIntent.getBroadcast(mContext, 0,
639 new Intent(BANNER_ACTION_SETUP).setPackage(packageName),
640 PendingIntent.FLAG_CANCEL_CURRENT);
642 final Resources res = mContext.getResources();
643 final int colorRes = com.android.internal.R.color.system_notification_accent_color;
644 Notification.Builder note = new Notification.Builder(mContext)
645 .setSmallIcon(R.drawable.ic_android)
646 .setContentTitle(mContext.getString(R.string.hidden_notifications_title))
647 .setContentText(mContext.getString(R.string.hidden_notifications_text))
648 .setPriority(Notification.PRIORITY_HIGH)
650 .setColor(res.getColor(colorRes))
651 .setContentIntent(setupIntent)
652 .addAction(R.drawable.ic_close,
653 mContext.getString(R.string.hidden_notifications_cancel),
655 .addAction(R.drawable.ic_settings,
656 mContext.getString(R.string.hidden_notifications_setup),
659 NotificationManager noMan =
660 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
661 noMan.notify(HIDDEN_NOTIFICATION_ID, note.build());
665 public void userSwitched(int newUserId) {
666 setHeadsUpUser(newUserId);
669 private void setHeadsUpUser(int newUserId) {
670 if (mHeadsUpNotificationView != null) {
671 mHeadsUpNotificationView.setUser(newUserId);
675 public boolean isHeadsUp(String key) {
676 return mHeadsUpNotificationView != null && mHeadsUpNotificationView.isShowing(key);
679 @Override // NotificationData.Environment
680 public boolean isNotificationForCurrentProfiles(StatusBarNotification n) {
681 final int thisUserId = mCurrentUserId;
682 final int notificationUserId = n.getUserId();
683 if (DEBUG && MULTIUSER_DEBUG) {
684 Log.v(TAG, String.format("%s: current userid: %d, notification userid: %d",
685 n, thisUserId, notificationUserId));
687 return isCurrentProfile(notificationUserId);
690 protected boolean isCurrentProfile(int userId) {
691 synchronized (mCurrentProfiles) {
692 return userId == UserHandle.USER_ALL || mCurrentProfiles.get(userId) != null;
697 public String getCurrentMediaNotificationKey() {
702 * Takes the necessary steps to prepare the status bar for starting an activity, then starts it.
703 * @param action A dismiss action that is called if it's safe to start the activity.
704 * @param afterKeyguardGone Whether the action should be executed after the Keyguard is gone.
706 protected void dismissKeyguardThenExecute(OnDismissAction action, boolean afterKeyguardGone) {
711 protected void onConfigurationChanged(Configuration newConfig) {
712 final Locale locale = mContext.getResources().getConfiguration().locale;
713 final int ld = TextUtils.getLayoutDirectionFromLocale(locale);
714 final float fontScale = newConfig.fontScale;
716 if (! locale.equals(mLocale) || ld != mLayoutDirection || fontScale != mFontScale) {
718 Log.v(TAG, String.format(
719 "config changed locale/LD: %s (%d) -> %s (%d)", mLocale, mLayoutDirection,
723 mLayoutDirection = ld;
728 protected View updateNotificationVetoButton(View row, StatusBarNotification n) {
729 View vetoButton = row.findViewById(R.id.veto);
730 if (n.isClearable() || (mHeadsUpNotificationView.getEntry() != null
731 && mHeadsUpNotificationView.getEntry().row == row)) {
732 final String _pkg = n.getPackageName();
733 final String _tag = n.getTag();
734 final int _id = n.getId();
735 final int _userId = n.getUserId();
736 vetoButton.setOnClickListener(new View.OnClickListener() {
737 public void onClick(View v) {
738 // Accessibility feedback
739 v.announceForAccessibility(
740 mContext.getString(R.string.accessibility_notification_dismissed));
742 mBarService.onNotificationClear(_pkg, _tag, _id, _userId);
744 } catch (RemoteException ex) {
745 // system process is dead if we're here.
749 vetoButton.setVisibility(View.VISIBLE);
751 vetoButton.setVisibility(View.GONE);
753 vetoButton.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
758 protected void applyColorsAndBackgrounds(StatusBarNotification sbn,
759 NotificationData.Entry entry) {
761 if (entry.expanded.getId() != com.android.internal.R.id.status_bar_latest_event_content) {
762 // Using custom RemoteViews
763 if (entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD
764 && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP) {
765 entry.row.setShowingLegacyBackground(true);
769 // Using platform templates
770 final int color = sbn.getNotification().color;
771 if (isMediaNotification(entry)) {
772 entry.row.setTintColor(color == Notification.COLOR_DEFAULT
773 ? mContext.getResources().getColor(
774 R.color.notification_material_background_media_default_color)
779 if (entry.icon != null) {
780 if (entry.targetSdk >= Build.VERSION_CODES.LOLLIPOP) {
781 entry.icon.setColorFilter(mContext.getResources().getColor(android.R.color.white));
783 entry.icon.setColorFilter(null);
788 public boolean isMediaNotification(NotificationData.Entry entry) {
789 // TODO: confirm that there's a valid media key
790 return entry.expandedBig != null &&
791 entry.expandedBig.findViewById(com.android.internal.R.id.media_actions) != null;
794 // The gear button in the guts that links to the app's own notification settings
795 private void startAppOwnNotificationSettingsActivity(Intent intent,
796 final int notificationId, final String notificationTag, final int appUid) {
797 intent.putExtra("notification_id", notificationId);
798 intent.putExtra("notification_tag", notificationTag);
799 startNotificationGutsIntent(intent, appUid);
802 // The (i) button in the guts that links to the system notification settings for that app
803 private void startAppNotificationSettingsActivity(String packageName, final int appUid) {
804 final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
805 intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
806 intent.putExtra(Settings.EXTRA_APP_UID, appUid);
807 startNotificationGutsIntent(intent, appUid);
810 private void startNotificationGutsIntent(final Intent intent, final int appUid) {
811 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
812 dismissKeyguardThenExecute(new OnDismissAction() {
814 public boolean onDismiss() {
815 AsyncTask.execute(new Runnable() {
818 if (keyguardShowing) {
819 ActivityManagerNative.getDefault()
820 .keyguardWaitingForActivityDrawn();
822 TaskStackBuilder.create(mContext)
823 .addNextIntentWithParentStack(intent)
824 .startActivities(null,
825 new UserHandle(UserHandle.getUserId(appUid)));
826 overrideActivityPendingAppTransition(keyguardShowing);
827 } catch (RemoteException e) {
831 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
834 }, false /* afterKeyguardGone */);
837 private void inflateGuts(ExpandableNotificationRow row) {
838 ViewStub stub = (ViewStub) row.findViewById(R.id.notification_guts_stub);
842 final StatusBarNotification sbn = row.getStatusBarNotification();
843 PackageManager pmUser = getPackageManagerForUser(
844 sbn.getUser().getIdentifier());
845 row.setTag(sbn.getPackageName());
846 final View guts = row.findViewById(R.id.notification_guts);
847 final String pkg = sbn.getPackageName();
848 String appname = pkg;
849 Drawable pkgicon = null;
852 final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
853 PackageManager.GET_UNINSTALLED_PACKAGES
854 | PackageManager.GET_DISABLED_COMPONENTS);
856 appname = String.valueOf(pmUser.getApplicationLabel(info));
857 pkgicon = pmUser.getApplicationIcon(info);
860 } catch (NameNotFoundException e) {
861 // app is gone, just show package name and generic icon
862 pkgicon = pmUser.getDefaultActivityIcon();
864 ((ImageView) row.findViewById(android.R.id.icon)).setImageDrawable(pkgicon);
865 ((DateTimeView) row.findViewById(R.id.timestamp)).setTime(sbn.getPostTime());
866 ((TextView) row.findViewById(R.id.pkgname)).setText(appname);
867 final View settingsButton = guts.findViewById(R.id.notification_inspect_item);
868 final View appSettingsButton
869 = guts.findViewById(R.id.notification_inspect_app_provided_settings);
871 final int appUidF = appUid;
872 settingsButton.setOnClickListener(new View.OnClickListener() {
873 public void onClick(View v) {
874 startAppNotificationSettingsActivity(pkg, appUidF);
878 final Intent appSettingsQueryIntent
879 = new Intent(Intent.ACTION_MAIN)
880 .addCategory(Notification.INTENT_CATEGORY_NOTIFICATION_PREFERENCES)
882 List<ResolveInfo> infos = pmUser.queryIntentActivities(appSettingsQueryIntent, 0);
883 if (infos.size() > 0) {
884 appSettingsButton.setVisibility(View.VISIBLE);
885 appSettingsButton.setContentDescription(
886 mContext.getResources().getString(
887 R.string.status_bar_notification_app_settings_title,
890 final Intent appSettingsLaunchIntent = new Intent(appSettingsQueryIntent)
891 .setClassName(pkg, infos.get(0).activityInfo.name);
892 appSettingsButton.setOnClickListener(new View.OnClickListener() {
893 public void onClick(View v) {
894 startAppOwnNotificationSettingsActivity(appSettingsLaunchIntent,
901 appSettingsButton.setVisibility(View.GONE);
904 settingsButton.setVisibility(View.GONE);
905 appSettingsButton.setVisibility(View.GONE);
910 protected SwipeHelper.LongPressListener getNotificationLongClicker() {
911 return new SwipeHelper.LongPressListener() {
913 public boolean onLongPress(View v, int x, int y) {
916 if (!(v instanceof ExpandableNotificationRow)) {
919 if (v.getWindowToken() == null) {
920 Log.e(TAG, "Trying to show notification guts, but not attached to window");
924 inflateGuts((ExpandableNotificationRow) v);
926 // Assume we are a status_bar_notification_row
927 final NotificationGuts guts = (NotificationGuts) v.findViewById(
928 R.id.notification_guts);
930 // This view has no guts. Examples are the more card or the dismiss all view
935 if (guts.getVisibility() == View.VISIBLE) {
936 Log.e(TAG, "Trying to show notification guts, but already visible");
940 guts.setVisibility(View.VISIBLE);
941 final double horz = Math.max(guts.getWidth() - x, x);
942 final double vert = Math.max(guts.getActualHeight() - y, y);
943 final float r = (float) Math.hypot(horz, vert);
945 = ViewAnimationUtils.createCircularReveal(guts, x, y, 0, r);
947 a.setInterpolator(mLinearOutSlowIn);
950 mNotificationGutsExposed = guts;
957 public void dismissPopups() {
958 if (mNotificationGutsExposed != null) {
959 final NotificationGuts v = mNotificationGutsExposed;
960 mNotificationGutsExposed = null;
962 if (v.getWindowToken() == null) return;
964 final int x = (v.getLeft() + v.getRight()) / 2;
965 final int y = (v.getTop() + v.getActualHeight() / 2);
966 final Animator a = ViewAnimationUtils.createCircularReveal(v,
969 a.setInterpolator(mFastOutLinearIn);
970 a.addListener(new AnimatorListenerAdapter() {
972 public void onAnimationEnd(Animator animation) {
973 super.onAnimationEnd(animation);
974 v.setVisibility(View.GONE);
981 public void onHeadsUpDismissed() {
985 public void showRecentApps(boolean triggeredFromAltTab) {
986 int msg = MSG_SHOW_RECENT_APPS;
987 mHandler.removeMessages(msg);
988 mHandler.obtainMessage(msg, triggeredFromAltTab ? 1 : 0, 0).sendToTarget();
992 public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
993 int msg = MSG_HIDE_RECENT_APPS;
994 mHandler.removeMessages(msg);
995 mHandler.obtainMessage(msg, triggeredFromAltTab ? 1 : 0,
996 triggeredFromHomeKey ? 1 : 0).sendToTarget();
1000 public void toggleRecentApps() {
1001 int msg = MSG_TOGGLE_RECENTS_APPS;
1002 mHandler.removeMessages(msg);
1003 mHandler.sendEmptyMessage(msg);
1007 public void preloadRecentApps() {
1008 int msg = MSG_PRELOAD_RECENT_APPS;
1009 mHandler.removeMessages(msg);
1010 mHandler.sendEmptyMessage(msg);
1014 public void cancelPreloadRecentApps() {
1015 int msg = MSG_CANCEL_PRELOAD_RECENT_APPS;
1016 mHandler.removeMessages(msg);
1017 mHandler.sendEmptyMessage(msg);
1020 /** Jumps to the next affiliated task in the group. */
1021 public void showNextAffiliatedTask() {
1022 int msg = MSG_SHOW_NEXT_AFFILIATED_TASK;
1023 mHandler.removeMessages(msg);
1024 mHandler.sendEmptyMessage(msg);
1027 /** Jumps to the previous affiliated task in the group. */
1028 public void showPreviousAffiliatedTask() {
1029 int msg = MSG_SHOW_PREV_AFFILIATED_TASK;
1030 mHandler.removeMessages(msg);
1031 mHandler.sendEmptyMessage(msg);
1035 public void showSearchPanel() {
1036 if (mSearchPanelView != null && mSearchPanelView.isAssistantAvailable()) {
1037 mSearchPanelView.show(true, true);
1042 public void hideSearchPanel() {
1043 int msg = MSG_CLOSE_SEARCH_PANEL;
1044 mHandler.removeMessages(msg);
1045 mHandler.sendEmptyMessage(msg);
1048 protected abstract WindowManager.LayoutParams getSearchLayoutParams(
1049 LayoutParams layoutParams);
1051 protected void updateSearchPanel() {
1053 boolean visible = false;
1054 if (mSearchPanelView != null) {
1055 visible = mSearchPanelView.isShowing();
1056 mWindowManager.removeView(mSearchPanelView);
1059 // Provide SearchPanel with a temporary parent to allow layout params to work.
1060 LinearLayout tmpRoot = new LinearLayout(mContext);
1061 mSearchPanelView = (SearchPanelView) LayoutInflater.from(mContext).inflate(
1062 R.layout.status_bar_search_panel, tmpRoot, false);
1063 mSearchPanelView.setOnTouchListener(
1064 new TouchOutsideListener(MSG_CLOSE_SEARCH_PANEL, mSearchPanelView));
1065 mSearchPanelView.setVisibility(View.GONE);
1066 boolean vertical = mNavigationBarView != null && mNavigationBarView.isVertical();
1067 mSearchPanelView.setHorizontal(vertical);
1069 WindowManager.LayoutParams lp = getSearchLayoutParams(mSearchPanelView.getLayoutParams());
1071 mWindowManager.addView(mSearchPanelView, lp);
1072 mSearchPanelView.setBar(this);
1074 mSearchPanelView.show(true, false);
1078 protected H createHandler() {
1082 static void sendCloseSystemWindows(Context context, String reason) {
1083 if (ActivityManagerNative.isSystemReady()) {
1085 ActivityManagerNative.getDefault().closeSystemDialogs(reason);
1086 } catch (RemoteException e) {
1091 protected abstract View getStatusBarView();
1093 protected View.OnTouchListener mRecentsPreloadOnTouchListener = new View.OnTouchListener() {
1094 // additional optimization when we have software system buttons - start loading the recent
1095 // tasks on touch down
1097 public boolean onTouch(View v, MotionEvent event) {
1098 int action = event.getAction() & MotionEvent.ACTION_MASK;
1099 if (action == MotionEvent.ACTION_DOWN) {
1101 } else if (action == MotionEvent.ACTION_CANCEL) {
1102 cancelPreloadingRecents();
1103 } else if (action == MotionEvent.ACTION_UP) {
1104 if (!v.isPressed()) {
1105 cancelPreloadingRecents();
1113 /** Proxy for RecentsComponent */
1115 protected void showRecents(boolean triggeredFromAltTab) {
1116 if (mRecents != null) {
1117 sendCloseSystemWindows(mContext, SYSTEM_DIALOG_REASON_RECENT_APPS);
1118 mRecents.showRecents(triggeredFromAltTab, getStatusBarView());
1122 protected void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
1123 if (mRecents != null) {
1124 mRecents.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
1128 protected void toggleRecents() {
1129 if (mRecents != null) {
1130 sendCloseSystemWindows(mContext, SYSTEM_DIALOG_REASON_RECENT_APPS);
1131 mRecents.toggleRecents(mDisplay, mLayoutDirection, getStatusBarView());
1135 protected void preloadRecents() {
1136 if (mRecents != null) {
1137 mRecents.preloadRecents();
1141 protected void cancelPreloadingRecents() {
1142 if (mRecents != null) {
1143 mRecents.cancelPreloadingRecents();
1147 protected void showRecentsNextAffiliatedTask() {
1148 if (mRecents != null) {
1149 mRecents.showNextAffiliatedTask();
1153 protected void showRecentsPreviousAffiliatedTask() {
1154 if (mRecents != null) {
1155 mRecents.showPrevAffiliatedTask();
1160 public void onVisibilityChanged(boolean visible) {
1164 public abstract void resetHeadsUpDecayTimer();
1166 public abstract void scheduleHeadsUpOpen();
1168 public abstract void scheduleHeadsUpClose();
1170 public abstract void scheduleHeadsUpEscalation();
1173 * Save the current "public" (locked and secure) state of the lockscreen.
1175 public void setLockscreenPublicMode(boolean publicMode) {
1176 mLockscreenPublicMode = publicMode;
1179 public boolean isLockscreenPublicMode() {
1180 return mLockscreenPublicMode;
1184 * Has the given user chosen to allow their private (full) notifications to be shown even
1185 * when the lockscreen is in "public" (secure & locked) mode?
1187 public boolean userAllowsPrivateNotificationsInPublic(int userHandle) {
1188 if (userHandle == UserHandle.USER_ALL) {
1192 if (mUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
1193 final boolean allowed = 0 != Settings.Secure.getIntForUser(
1194 mContext.getContentResolver(),
1195 Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle);
1196 final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(null /* admin */,
1198 final boolean allowedByDpm = (dpmFlags
1199 & DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS) == 0;
1200 mUsersAllowingPrivateNotifications.append(userHandle, allowed && allowedByDpm);
1204 return mUsersAllowingPrivateNotifications.get(userHandle);
1208 * Returns true if we're on a secure lockscreen and the user wants to hide "sensitive"
1209 * notification data. If so, private notifications should show their (possibly
1210 * auto-generated) publicVersion, and secret notifications should be totally invisible.
1212 @Override // NotificationData.Environment
1213 public boolean shouldHideSensitiveContents(int userid) {
1214 return isLockscreenPublicMode() && !userAllowsPrivateNotificationsInPublic(userid);
1217 public void onNotificationClear(StatusBarNotification notification) {
1219 mBarService.onNotificationClear(
1220 notification.getPackageName(),
1221 notification.getTag(),
1222 notification.getId(),
1223 notification.getUserId());
1224 } catch (android.os.RemoteException ex) {
1229 protected class H extends Handler {
1230 public void handleMessage(Message m) {
1232 case MSG_SHOW_RECENT_APPS:
1233 showRecents(m.arg1 > 0);
1235 case MSG_HIDE_RECENT_APPS:
1236 hideRecents(m.arg1 > 0, m.arg2 > 0);
1238 case MSG_TOGGLE_RECENTS_APPS:
1241 case MSG_PRELOAD_RECENT_APPS:
1244 case MSG_CANCEL_PRELOAD_RECENT_APPS:
1245 cancelPreloadingRecents();
1247 case MSG_SHOW_NEXT_AFFILIATED_TASK:
1248 showRecentsNextAffiliatedTask();
1250 case MSG_SHOW_PREV_AFFILIATED_TASK:
1251 showRecentsPreviousAffiliatedTask();
1253 case MSG_CLOSE_SEARCH_PANEL:
1254 if (DEBUG) Log.d(TAG, "closing search panel");
1255 if (mSearchPanelView != null && mSearchPanelView.isShowing()) {
1256 mSearchPanelView.show(false, true);
1263 public class TouchOutsideListener implements View.OnTouchListener {
1265 private StatusBarPanel mPanel;
1267 public TouchOutsideListener(int msg, StatusBarPanel panel) {
1272 public boolean onTouch(View v, MotionEvent ev) {
1273 final int action = ev.getAction();
1274 if (action == MotionEvent.ACTION_OUTSIDE
1275 || (action == MotionEvent.ACTION_DOWN
1276 && !mPanel.isInContentArea((int)ev.getX(), (int)ev.getY()))) {
1277 mHandler.removeMessages(mMsg);
1278 mHandler.sendEmptyMessage(mMsg);
1285 protected void workAroundBadLayerDrawableOpacity(View v) {
1288 private boolean inflateViews(NotificationData.Entry entry, ViewGroup parent) {
1289 return inflateViews(entry, parent, false);
1292 protected boolean inflateViewsForHeadsUp(NotificationData.Entry entry, ViewGroup parent) {
1293 return inflateViews(entry, parent, true);
1296 private boolean inflateViews(NotificationData.Entry entry, ViewGroup parent, boolean isHeadsUp) {
1297 PackageManager pmUser = getPackageManagerForUser(
1298 entry.notification.getUser().getIdentifier());
1300 int maxHeight = mRowMaxHeight;
1301 final StatusBarNotification sbn = entry.notification;
1302 RemoteViews contentView = sbn.getNotification().contentView;
1303 RemoteViews bigContentView = sbn.getNotification().bigContentView;
1307 mContext.getResources().getDimensionPixelSize(R.dimen.notification_mid_height);
1308 bigContentView = sbn.getNotification().headsUpContentView;
1311 if (contentView == null) {
1316 Log.v(TAG, "publicNotification: " + sbn.getNotification().publicVersion);
1319 Notification publicNotification = sbn.getNotification().publicVersion;
1321 ExpandableNotificationRow row;
1323 // Stash away previous user expansion state so we can restore it at
1325 boolean hasUserChangedExpansion = false;
1326 boolean userExpanded = false;
1327 boolean userLocked = false;
1329 if (entry.row != null) {
1331 hasUserChangedExpansion = row.hasUserChangedExpansion();
1332 userExpanded = row.isUserExpanded();
1333 userLocked = row.isUserLocked();
1335 if (hasUserChangedExpansion) {
1336 row.setUserExpanded(userExpanded);
1339 // create the row view
1340 LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
1341 Context.LAYOUT_INFLATER_SERVICE);
1342 row = (ExpandableNotificationRow) inflater.inflate(R.layout.status_bar_notification_row,
1344 row.setExpansionLogger(this, entry.notification.getKey());
1347 workAroundBadLayerDrawableOpacity(row);
1348 View vetoButton = updateNotificationVetoButton(row, sbn);
1349 vetoButton.setContentDescription(mContext.getString(
1350 R.string.accessibility_remove_notification));
1352 // NB: the large icon is now handled entirely by the template
1354 // bind the click event to the content area
1355 NotificationContentView expanded =
1356 (NotificationContentView) row.findViewById(R.id.expanded);
1357 NotificationContentView expandedPublic =
1358 (NotificationContentView) row.findViewById(R.id.expandedPublic);
1360 row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
1362 PendingIntent contentIntent = sbn.getNotification().contentIntent;
1363 if (contentIntent != null) {
1364 final View.OnClickListener listener = makeClicker(contentIntent, sbn.getKey(),
1366 row.setOnClickListener(listener);
1368 row.setOnClickListener(null);
1371 // set up the adaptive layout
1372 View contentViewLocal = null;
1373 View bigContentViewLocal = null;
1375 contentViewLocal = contentView.apply(mContext, expanded,
1377 if (bigContentView != null) {
1378 bigContentViewLocal = bigContentView.apply(mContext, expanded,
1382 catch (RuntimeException e) {
1383 final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId());
1384 Log.e(TAG, "couldn't inflate view for notification " + ident, e);
1388 if (contentViewLocal != null) {
1389 contentViewLocal.setIsRootNamespace(true);
1390 expanded.setContractedChild(contentViewLocal);
1392 if (bigContentViewLocal != null) {
1393 bigContentViewLocal.setIsRootNamespace(true);
1394 expanded.setExpandedChild(bigContentViewLocal);
1397 // now the public version
1398 View publicViewLocal = null;
1399 if (publicNotification != null) {
1401 publicViewLocal = publicNotification.contentView.apply(mContext, expandedPublic,
1404 if (publicViewLocal != null) {
1405 publicViewLocal.setIsRootNamespace(true);
1406 expandedPublic.setContractedChild(publicViewLocal);
1409 catch (RuntimeException e) {
1410 final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId());
1411 Log.e(TAG, "couldn't inflate public view for notification " + ident, e);
1412 publicViewLocal = null;
1416 // Extract target SDK version.
1418 ApplicationInfo info = pmUser.getApplicationInfo(sbn.getPackageName(), 0);
1419 entry.targetSdk = info.targetSdkVersion;
1420 } catch (NameNotFoundException ex) {
1421 Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex);
1424 if (publicViewLocal == null) {
1425 // Add a basic notification template
1426 publicViewLocal = LayoutInflater.from(mContext).inflate(
1427 R.layout.notification_public_default,
1428 expandedPublic, false);
1429 publicViewLocal.setIsRootNamespace(true);
1430 expandedPublic.setContractedChild(publicViewLocal);
1432 final TextView title = (TextView) publicViewLocal.findViewById(R.id.title);
1434 title.setText(pmUser.getApplicationLabel(
1435 pmUser.getApplicationInfo(entry.notification.getPackageName(), 0)));
1436 } catch (NameNotFoundException e) {
1437 title.setText(entry.notification.getPackageName());
1440 final ImageView icon = (ImageView) publicViewLocal.findViewById(R.id.icon);
1441 final ImageView profileBadge = (ImageView) publicViewLocal.findViewById(
1442 R.id.profile_badge_line3);
1444 final StatusBarIcon ic = new StatusBarIcon(entry.notification.getPackageName(),
1445 entry.notification.getUser(),
1446 entry.notification.getNotification().icon,
1447 entry.notification.getNotification().iconLevel,
1448 entry.notification.getNotification().number,
1449 entry.notification.getNotification().tickerText);
1451 Drawable iconDrawable = StatusBarIconView.getIcon(mContext, ic);
1452 icon.setImageDrawable(iconDrawable);
1453 if (entry.targetSdk >= Build.VERSION_CODES.LOLLIPOP
1454 || mNotificationColorUtil.isGrayscaleIcon(iconDrawable)) {
1455 icon.setBackgroundResource(
1456 com.android.internal.R.drawable.notification_icon_legacy_bg);
1457 int padding = mContext.getResources().getDimensionPixelSize(
1458 com.android.internal.R.dimen.notification_large_icon_circle_padding);
1459 icon.setPadding(padding, padding, padding, padding);
1460 if (sbn.getNotification().color != Notification.COLOR_DEFAULT) {
1461 icon.getBackground().setColorFilter(
1462 sbn.getNotification().color, PorterDuff.Mode.SRC_ATOP);
1466 if (profileBadge != null) {
1467 Drawable profileDrawable = mContext.getPackageManager().getUserBadgeForDensity(
1468 entry.notification.getUser(), 0);
1469 if (profileDrawable != null) {
1470 profileBadge.setImageDrawable(profileDrawable);
1471 profileBadge.setVisibility(View.VISIBLE);
1473 profileBadge.setVisibility(View.GONE);
1477 final View privateTime = contentViewLocal.findViewById(com.android.internal.R.id.time);
1478 final DateTimeView time = (DateTimeView) publicViewLocal.findViewById(R.id.time);
1479 if (privateTime != null && privateTime.getVisibility() == View.VISIBLE) {
1480 time.setVisibility(View.VISIBLE);
1481 time.setTime(entry.notification.getNotification().when);
1484 final TextView text = (TextView) publicViewLocal.findViewById(R.id.text);
1486 text.setText(R.string.notification_hidden_text);
1487 text.setTextAppearance(mContext,
1488 R.style.TextAppearance_Material_Notification_Parenthetical);
1491 int topPadding = Notification.Builder.calculateTopPadding(mContext,
1492 false /* hasThreeLines */,
1493 mContext.getResources().getConfiguration().fontScale);
1494 title.setPadding(0, topPadding, 0, 0);
1496 entry.autoRedacted = true;
1499 if (MULTIUSER_DEBUG) {
1500 TextView debug = (TextView) row.findViewById(R.id.debug_info);
1501 if (debug != null) {
1502 debug.setVisibility(View.VISIBLE);
1503 debug.setText("CU " + mCurrentUserId +" NU " + entry.notification.getUserId());
1507 entry.row.setHeightRange(mRowMinHeight, maxHeight);
1508 entry.row.setOnActivatedListener(this);
1509 entry.expanded = contentViewLocal;
1510 entry.expandedPublic = publicViewLocal;
1511 entry.setBigContentView(bigContentViewLocal);
1513 applyColorsAndBackgrounds(sbn, entry);
1515 // Restore previous flags.
1516 if (hasUserChangedExpansion) {
1517 // Note: setUserExpanded() conveniently ignores calls with
1518 // userExpanded=true if !isExpandable().
1519 row.setUserExpanded(userExpanded);
1521 row.setUserLocked(userLocked);
1522 row.setStatusBarNotification(entry.notification);
1526 public NotificationClicker makeClicker(PendingIntent intent, String notificationKey,
1528 return new NotificationClicker(intent, notificationKey, forHun);
1531 protected class NotificationClicker implements View.OnClickListener {
1532 private PendingIntent mIntent;
1533 private final String mNotificationKey;
1534 private boolean mIsHeadsUp;
1536 public NotificationClicker(PendingIntent intent, String notificationKey, boolean forHun) {
1538 mNotificationKey = notificationKey;
1539 mIsHeadsUp = forHun;
1542 public void onClick(final View v) {
1543 if (NOTIFICATION_CLICK_DEBUG) {
1544 Log.d(TAG, "Clicked on content of " + mNotificationKey);
1546 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
1547 final boolean afterKeyguardGone = mIntent.isActivity()
1548 && PreviewInflater.wouldLaunchResolverActivity(mContext, mIntent.getIntent(),
1550 dismissKeyguardThenExecute(new OnDismissAction() {
1551 public boolean onDismiss() {
1553 // Release the HUN notification to the shade.
1555 // In most cases, when FLAG_AUTO_CANCEL is set, the notification will
1556 // become canceled shortly by NoMan, but we can't assume that.
1557 mHeadsUpNotificationView.releaseAndClose();
1563 if (keyguardShowing && !afterKeyguardGone) {
1564 ActivityManagerNative.getDefault()
1565 .keyguardWaitingForActivityDrawn();
1568 // The intent we are sending is for the application, which
1569 // won't have permission to immediately start an activity after
1570 // the user switches to home. We know it is safe to do at this
1571 // point, so make sure new activity switches are now allowed.
1572 ActivityManagerNative.getDefault().resumeAppSwitches();
1573 } catch (RemoteException e) {
1576 if (mIntent != null) {
1579 } catch (PendingIntent.CanceledException e) {
1580 // the stack trace isn't very helpful here.
1581 // Just log the exception message.
1582 Log.w(TAG, "Sending contentIntent failed: " + e);
1584 // TODO: Dismiss Keyguard.
1586 if (mIntent.isActivity()) {
1587 overrideActivityPendingAppTransition(keyguardShowing
1588 && !afterKeyguardGone);
1593 mBarService.onNotificationClick(mNotificationKey);
1594 } catch (RemoteException ex) {
1595 // system process is dead if we're here.
1600 // close the shade if it was open
1601 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
1603 visibilityChanged(false);
1605 return mIntent != null && mIntent.isActivity();
1607 }, afterKeyguardGone);
1611 public void animateCollapsePanels(int flags, boolean force) {
1614 public void overrideActivityPendingAppTransition(boolean keyguardShowing) {
1615 if (keyguardShowing) {
1617 mWindowManagerService.overridePendingAppTransition(null, 0, 0, null);
1618 } catch (RemoteException e) {
1619 Log.w(TAG, "Error overriding app transition: " + e);
1624 protected void visibilityChanged(boolean visible) {
1625 if (mVisible != visible) {
1631 updateVisibleToUser();
1634 protected void updateVisibleToUser() {
1635 boolean oldVisibleToUser = mVisibleToUser;
1636 mVisibleToUser = mVisible && mScreenOnFromKeyguard;
1638 if (oldVisibleToUser != mVisibleToUser) {
1639 handleVisibleToUserChanged(mVisibleToUser);
1644 * The LEDs are turned off when the notification panel is shown, even just a little bit.
1645 * This was added last-minute and is inconsistent with the way the rest of the notifications
1646 * are handled, because the notification isn't really cancelled. The lights are just
1647 * turned off. If any other notifications happen, the lights will turn back on. Steve says
1648 * this is what he wants. (see bug 1131461)
1650 protected void handleVisibleToUserChanged(boolean visibleToUser) {
1652 if (visibleToUser) {
1653 // Only stop blinking, vibrating, ringing when the user went into the shade
1654 // manually (SHADE or SHADE_LOCKED).
1655 boolean clearNotificationEffects =
1656 (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED);
1657 mBarService.onPanelRevealed(clearNotificationEffects);
1659 mBarService.onPanelHidden();
1661 } catch (RemoteException ex) {
1662 // Won't fail unless the world has ended.
1667 * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
1668 * about the failure.
1670 * WARNING: this will call back into us. Don't hold any locks.
1672 void handleNotificationError(StatusBarNotification n, String message) {
1673 removeNotification(n.getKey(), null);
1675 mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(),
1676 n.getInitialPid(), message, n.getUserId());
1677 } catch (RemoteException ex) {
1682 protected StatusBarNotification removeNotificationViews(String key, RankingMap ranking) {
1683 NotificationData.Entry entry = mNotificationData.remove(key, ranking);
1684 if (entry == null) {
1685 Log.w(TAG, "removeNotification for unknown key: " + key);
1688 updateNotifications();
1689 return entry.notification;
1692 protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn) {
1694 Log.d(TAG, "createNotificationViews(notification=" + sbn);
1696 // Construct the icon.
1697 Notification n = sbn.getNotification();
1698 final StatusBarIconView iconView = new StatusBarIconView(mContext,
1699 sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), n);
1700 iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
1702 final StatusBarIcon ic = new StatusBarIcon(sbn.getPackageName(),
1708 if (!iconView.set(ic)) {
1709 handleNotificationError(sbn, "Couldn't create icon: " + ic);
1712 // Construct the expanded view.
1713 NotificationData.Entry entry = new NotificationData.Entry(sbn, iconView);
1714 if (!inflateViews(entry, mStackScroller)) {
1715 handleNotificationError(sbn, "Couldn't expand RemoteViews for: " + sbn);
1721 protected void addNotificationViews(Entry entry, RankingMap ranking) {
1722 if (entry == null) {
1725 // Add the expanded view and icon.
1726 mNotificationData.add(entry, ranking);
1727 updateNotifications();
1731 * @return The number of notifications we show on Keyguard.
1733 protected abstract int getMaxKeyguardNotifications();
1736 * Updates expanded, dimmed and locked states of notification rows.
1738 protected void updateRowStates() {
1739 int maxKeyguardNotifications = getMaxKeyguardNotifications();
1740 mKeyguardIconOverflowContainer.getIconsView().removeAllViews();
1742 ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
1743 final int N = activeNotifications.size();
1745 int visibleNotifications = 0;
1746 boolean onKeyguard = mState == StatusBarState.KEYGUARD;
1747 for (int i = 0; i < N; i++) {
1748 NotificationData.Entry entry = activeNotifications.get(i);
1750 entry.row.setExpansionDisabled(true);
1752 entry.row.setExpansionDisabled(false);
1753 if (!entry.row.isUserLocked()) {
1754 boolean top = (i == 0);
1755 entry.row.setSystemExpanded(top);
1758 boolean showOnKeyguard = shouldShowOnKeyguard(entry.notification);
1759 if ((isLockscreenPublicMode() && !mShowLockscreenNotifications) ||
1760 (onKeyguard && (visibleNotifications >= maxKeyguardNotifications
1761 || !showOnKeyguard))) {
1762 entry.row.setVisibility(View.GONE);
1763 if (onKeyguard && showOnKeyguard) {
1764 mKeyguardIconOverflowContainer.getIconsView().addNotification(entry);
1767 boolean wasGone = entry.row.getVisibility() == View.GONE;
1768 entry.row.setVisibility(View.VISIBLE);
1770 // notify the scroller of a child addition
1771 mStackScroller.generateAddAnimation(entry.row, true /* fromMoreCard */);
1773 visibleNotifications++;
1777 if (onKeyguard && mKeyguardIconOverflowContainer.getIconsView().getChildCount() > 0) {
1778 mKeyguardIconOverflowContainer.setVisibility(View.VISIBLE);
1780 mKeyguardIconOverflowContainer.setVisibility(View.GONE);
1783 mStackScroller.changeViewPosition(mKeyguardIconOverflowContainer,
1784 mStackScroller.getChildCount() - 3);
1785 mStackScroller.changeViewPosition(mEmptyShadeView, mStackScroller.getChildCount() - 2);
1786 mStackScroller.changeViewPosition(mDismissView, mStackScroller.getChildCount() - 1);
1789 private boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
1790 return mShowLockscreenNotifications && !mNotificationData.isAmbient(sbn.getKey());
1793 protected void setZenMode(int mode) {
1794 if (!isDeviceProvisioned()) return;
1796 updateNotifications();
1799 // extended in PhoneStatusBar
1800 protected void setShowLockscreenNotifications(boolean show) {
1801 mShowLockscreenNotifications = show;
1804 private void updateLockscreenNotificationSetting() {
1805 final boolean show = Settings.Secure.getIntForUser(mContext.getContentResolver(),
1806 Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
1808 mCurrentUserId) != 0;
1809 final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(
1810 null /* admin */, mCurrentUserId);
1811 final boolean allowedByDpm = (dpmFlags
1812 & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0;
1813 setShowLockscreenNotifications(show && allowedByDpm);
1816 protected abstract void haltTicker();
1817 protected abstract void setAreThereNotifications();
1818 protected abstract void updateNotifications();
1819 protected abstract void tick(StatusBarNotification n, boolean firstTime);
1820 protected abstract void updateExpandedViewPos(int expandedPosition);
1821 protected abstract boolean shouldDisableNavbarGestures();
1823 public abstract void addNotification(StatusBarNotification notification,
1824 RankingMap ranking);
1825 protected abstract void updateNotificationRanking(RankingMap ranking);
1826 public abstract void removeNotification(String key, RankingMap ranking);
1828 public void updateNotification(StatusBarNotification notification, RankingMap ranking) {
1829 if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
1831 final String key = notification.getKey();
1832 boolean wasHeadsUp = isHeadsUp(key);
1835 oldEntry = mHeadsUpNotificationView.getEntry();
1837 oldEntry = mNotificationData.get(key);
1839 if (oldEntry == null) {
1843 final StatusBarNotification oldNotification = oldEntry.notification;
1845 // XXX: modify when we do something more intelligent with the two content views
1846 final RemoteViews oldContentView = oldNotification.getNotification().contentView;
1847 Notification n = notification.getNotification();
1848 final RemoteViews contentView = n.contentView;
1849 final RemoteViews oldBigContentView = oldNotification.getNotification().bigContentView;
1850 final RemoteViews bigContentView = n.bigContentView;
1851 final RemoteViews oldHeadsUpContentView = oldNotification.getNotification().headsUpContentView;
1852 final RemoteViews headsUpContentView = n.headsUpContentView;
1853 final Notification oldPublicNotification = oldNotification.getNotification().publicVersion;
1854 final RemoteViews oldPublicContentView = oldPublicNotification != null
1855 ? oldPublicNotification.contentView : null;
1856 final Notification publicNotification = n.publicVersion;
1857 final RemoteViews publicContentView = publicNotification != null
1858 ? publicNotification.contentView : null;
1861 Log.d(TAG, "old notification: when=" + oldNotification.getNotification().when
1862 + " ongoing=" + oldNotification.isOngoing()
1863 + " expanded=" + oldEntry.expanded
1864 + " contentView=" + oldContentView
1865 + " bigContentView=" + oldBigContentView
1866 + " publicView=" + oldPublicContentView
1867 + " rowParent=" + oldEntry.row.getParent());
1868 Log.d(TAG, "new notification: when=" + n.when
1869 + " ongoing=" + oldNotification.isOngoing()
1870 + " contentView=" + contentView
1871 + " bigContentView=" + bigContentView
1872 + " publicView=" + publicContentView);
1875 // Can we just reapply the RemoteViews in place?
1878 boolean contentsUnchanged = oldEntry.expanded != null
1879 && contentView.getPackage() != null
1880 && oldContentView.getPackage() != null
1881 && oldContentView.getPackage().equals(contentView.getPackage())
1882 && oldContentView.getLayoutId() == contentView.getLayoutId();
1883 // large view may be null
1884 boolean bigContentsUnchanged =
1885 (oldEntry.getBigContentView() == null && bigContentView == null)
1886 || ((oldEntry.getBigContentView() != null && bigContentView != null)
1887 && bigContentView.getPackage() != null
1888 && oldBigContentView.getPackage() != null
1889 && oldBigContentView.getPackage().equals(bigContentView.getPackage())
1890 && oldBigContentView.getLayoutId() == bigContentView.getLayoutId());
1891 boolean headsUpContentsUnchanged =
1892 (oldHeadsUpContentView == null && headsUpContentView == null)
1893 || ((oldHeadsUpContentView != null && headsUpContentView != null)
1894 && headsUpContentView.getPackage() != null
1895 && oldHeadsUpContentView.getPackage() != null
1896 && oldHeadsUpContentView.getPackage().equals(headsUpContentView.getPackage())
1897 && oldHeadsUpContentView.getLayoutId() == headsUpContentView.getLayoutId());
1898 boolean publicUnchanged =
1899 (oldPublicContentView == null && publicContentView == null)
1900 || ((oldPublicContentView != null && publicContentView != null)
1901 && publicContentView.getPackage() != null
1902 && oldPublicContentView.getPackage() != null
1903 && oldPublicContentView.getPackage().equals(publicContentView.getPackage())
1904 && oldPublicContentView.getLayoutId() == publicContentView.getLayoutId());
1905 boolean updateTicker = n.tickerText != null
1906 && !TextUtils.equals(n.tickerText,
1907 oldEntry.notification.getNotification().tickerText);
1909 final boolean shouldInterrupt = shouldInterrupt(notification);
1910 final boolean alertAgain = alertAgain(oldEntry, n);
1911 boolean updateSuccessful = false;
1912 if (contentsUnchanged && bigContentsUnchanged && headsUpContentsUnchanged
1913 && publicUnchanged) {
1914 if (DEBUG) Log.d(TAG, "reusing notification for key: " + key);
1915 oldEntry.notification = notification;
1917 if (oldEntry.icon != null) {
1919 final StatusBarIcon ic = new StatusBarIcon(notification.getPackageName(),
1920 notification.getUser(),
1925 oldEntry.icon.setNotification(n);
1926 if (!oldEntry.icon.set(ic)) {
1927 handleNotificationError(notification, "Couldn't update icon: " + ic);
1933 if (shouldInterrupt) {
1934 updateHeadsUpViews(oldEntry, notification);
1936 resetHeadsUpDecayTimer();
1939 // we updated the notification above, so release to build a new shade entry
1940 mHeadsUpNotificationView.releaseAndClose();
1944 if (shouldInterrupt && alertAgain) {
1945 removeNotificationViews(key, ranking);
1946 addNotification(notification, ranking); //this will pop the headsup
1948 updateNotificationViews(oldEntry, notification);
1951 mNotificationData.updateRanking(ranking);
1952 updateNotifications();
1953 updateSuccessful = true;
1955 catch (RuntimeException e) {
1956 // It failed to add cleanly. Log, and remove the view from the panel.
1957 Log.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e);
1960 if (!updateSuccessful) {
1961 if (DEBUG) Log.d(TAG, "not reusing notification for key: " + key);
1963 if (shouldInterrupt) {
1964 if (DEBUG) Log.d(TAG, "rebuilding heads up for key: " + key);
1965 Entry newEntry = new Entry(notification, null);
1966 ViewGroup holder = mHeadsUpNotificationView.getHolder();
1967 if (inflateViewsForHeadsUp(newEntry, holder)) {
1968 mHeadsUpNotificationView.showNotification(newEntry);
1970 resetHeadsUpDecayTimer();
1973 Log.w(TAG, "Couldn't create new updated headsup for package "
1974 + contentView.getPackage());
1977 if (DEBUG) Log.d(TAG, "releasing heads up for key: " + key);
1978 oldEntry.notification = notification;
1979 mHeadsUpNotificationView.releaseAndClose();
1983 if (shouldInterrupt && alertAgain) {
1984 if (DEBUG) Log.d(TAG, "reposting to invoke heads up for key: " + key);
1985 removeNotificationViews(key, ranking);
1986 addNotification(notification, ranking); //this will pop the headsup
1988 if (DEBUG) Log.d(TAG, "rebuilding update in place for key: " + key);
1989 oldEntry.notification = notification;
1990 final StatusBarIcon ic = new StatusBarIcon(notification.getPackageName(),
1991 notification.getUser(),
1996 oldEntry.icon.setNotification(n);
1997 oldEntry.icon.set(ic);
1998 inflateViews(oldEntry, mStackScroller, wasHeadsUp);
1999 mNotificationData.updateRanking(ranking);
2000 updateNotifications();
2005 // Update the veto button accordingly (and as a result, whether this row is
2006 // swipe-dismissable)
2007 updateNotificationVetoButton(oldEntry.row, notification);
2010 boolean isForCurrentUser = isNotificationForCurrentProfiles(notification);
2011 if (DEBUG) Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");
2013 // Restart the ticker if it's still running
2014 if (updateTicker && isForCurrentUser) {
2016 tick(notification, false);
2019 // Recalculate the position of the sliding windows and the titles.
2020 setAreThereNotifications();
2021 updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
2024 private void updateNotificationViews(NotificationData.Entry entry,
2025 StatusBarNotification notification) {
2026 updateNotificationViews(entry, notification, false);
2029 private void updateHeadsUpViews(NotificationData.Entry entry,
2030 StatusBarNotification notification) {
2031 updateNotificationViews(entry, notification, true);
2034 private void updateNotificationViews(NotificationData.Entry entry,
2035 StatusBarNotification notification, boolean isHeadsUp) {
2036 final RemoteViews contentView = notification.getNotification().contentView;
2037 final RemoteViews bigContentView = isHeadsUp
2038 ? notification.getNotification().headsUpContentView
2039 : notification.getNotification().bigContentView;
2040 final Notification publicVersion = notification.getNotification().publicVersion;
2041 final RemoteViews publicContentView = publicVersion != null ? publicVersion.contentView
2044 // Reapply the RemoteViews
2045 contentView.reapply(mContext, entry.expanded, mOnClickHandler);
2046 if (bigContentView != null && entry.getBigContentView() != null) {
2047 bigContentView.reapply(mContext, entry.getBigContentView(),
2050 if (publicContentView != null && entry.getPublicContentView() != null) {
2051 publicContentView.reapply(mContext, entry.getPublicContentView(), mOnClickHandler);
2053 // update the contentIntent
2054 final PendingIntent contentIntent = notification.getNotification().contentIntent;
2055 if (contentIntent != null) {
2056 final View.OnClickListener listener = makeClicker(contentIntent, notification.getKey(),
2058 entry.row.setOnClickListener(listener);
2060 entry.row.setOnClickListener(null);
2062 entry.row.setStatusBarNotification(notification);
2063 entry.row.notifyContentUpdated();
2064 entry.row.resetHeight();
2067 protected void notifyHeadsUpScreenOn(boolean screenOn) {
2069 scheduleHeadsUpEscalation();
2073 private boolean alertAgain(Entry oldEntry, Notification newNotification) {
2074 return oldEntry == null || !oldEntry.hasInterrupted()
2075 || (newNotification.flags & Notification.FLAG_ONLY_ALERT_ONCE) == 0;
2078 protected boolean shouldInterrupt(StatusBarNotification sbn) {
2079 if (mNotificationData.shouldFilterOut(sbn)) {
2081 Log.d(TAG, "Skipping HUN check for " + sbn.getKey() + " since it's filtered out.");
2086 if (mHeadsUpNotificationView.isSnoozed(sbn.getPackageName())) {
2090 Notification notification = sbn.getNotification();
2091 // some predicates to make the boolean logic legible
2092 boolean isNoisy = (notification.defaults & Notification.DEFAULT_SOUND) != 0
2093 || (notification.defaults & Notification.DEFAULT_VIBRATE) != 0
2094 || notification.sound != null
2095 || notification.vibrate != null;
2096 boolean isHighPriority = sbn.getScore() >= INTERRUPTION_THRESHOLD;
2097 boolean isFullscreen = notification.fullScreenIntent != null;
2098 boolean hasTicker = mHeadsUpTicker && !TextUtils.isEmpty(notification.tickerText);
2099 boolean isAllowed = notification.extras.getInt(Notification.EXTRA_AS_HEADS_UP,
2100 Notification.HEADS_UP_ALLOWED) != Notification.HEADS_UP_NEVER;
2101 boolean accessibilityForcesLaunch = isFullscreen
2102 && mAccessibilityManager.isTouchExplorationEnabled();
2104 boolean interrupt = (isFullscreen || (isHighPriority && (isNoisy || hasTicker)))
2106 && !accessibilityForcesLaunch
2107 && mPowerManager.isScreenOn()
2108 && (!mStatusBarKeyguardViewManager.isShowing()
2109 || mStatusBarKeyguardViewManager.isOccluded())
2110 && !mStatusBarKeyguardViewManager.isInputRestricted();
2112 interrupt = interrupt && !mDreamManager.isDreaming();
2113 } catch (RemoteException e) {
2114 Log.d(TAG, "failed to query dream manager", e);
2116 if (DEBUG) Log.d(TAG, "interrupt: " + interrupt);
2120 public void setInteracting(int barWindow, boolean interacting) {
2121 // hook for subclasses
2124 public void setBouncerShowing(boolean bouncerShowing) {
2125 mBouncerShowing = bouncerShowing;
2129 * @return Whether the security bouncer from Keyguard is showing.
2131 public boolean isBouncerShowing() {
2132 return mBouncerShowing;
2135 public void destroy() {
2136 if (mSearchPanelView != null) {
2137 mWindowManager.removeViewImmediate(mSearchPanelView);
2139 mContext.unregisterReceiver(mBroadcastReceiver);
2141 mNotificationListener.unregisterAsSystemService();
2142 } catch (RemoteException e) {
2148 * @return a PackageManger for userId or if userId is < 0 (USER_ALL etc) then
2149 * return PackageManager for mContext
2151 protected PackageManager getPackageManagerForUser(int userId) {
2152 Context contextForUser = mContext;
2153 // UserHandle defines special userId as negative values, e.g. USER_ALL
2156 // Create a context for the correct user so if a package isn't installed
2157 // for user 0 we can still load information about the package.
2159 mContext.createPackageContextAsUser(mContext.getPackageName(),
2160 Context.CONTEXT_RESTRICTED,
2161 new UserHandle(userId));
2162 } catch (NameNotFoundException e) {
2163 // Shouldn't fail to find the package name for system ui.
2166 return contextForUser.getPackageManager();
2170 public void logNotificationExpansion(String key, boolean userAction, boolean expanded) {
2172 mBarService.onNotificationExpansionChanged(key, userAction, expanded);
2173 } catch (RemoteException e) {
2178 public boolean isKeyguardSecure() {
2179 if (mStatusBarKeyguardViewManager == null) {
2180 // startKeyguard() hasn't been called yet, so we don't know.
2181 // Make sure anything that needs to know isKeyguardSecure() checks and re-checks this
2182 // value onVisibilityChanged().
2183 Slog.w(TAG, "isKeyguardSecure() called before startKeyguard(), returning false",
2187 return mStatusBarKeyguardViewManager.isSecure();