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.graphics.drawable.Icon;
45 import android.os.AsyncTask;
46 import android.os.Build;
47 import android.os.Bundle;
48 import android.os.Handler;
49 import android.os.IBinder;
50 import android.os.Message;
51 import android.os.PowerManager;
52 import android.os.RemoteException;
53 import android.os.ServiceManager;
54 import android.os.SystemProperties;
55 import android.os.UserHandle;
56 import android.os.UserManager;
57 import android.provider.Settings;
58 import android.service.dreams.DreamService;
59 import android.service.dreams.IDreamManager;
60 import android.service.notification.NotificationListenerService;
61 import android.service.notification.NotificationListenerService.RankingMap;
62 import android.service.notification.StatusBarNotification;
63 import android.text.TextUtils;
64 import android.util.Log;
65 import android.util.Slog;
66 import android.util.SparseArray;
67 import android.util.SparseBooleanArray;
68 import android.view.Display;
69 import android.view.IWindowManager;
70 import android.view.LayoutInflater;
71 import android.view.MotionEvent;
72 import android.view.View;
73 import android.view.ViewAnimationUtils;
74 import android.view.ViewGroup;
75 import android.view.ViewParent;
76 import android.view.WindowManager;
77 import android.view.WindowManagerGlobal;
78 import android.view.accessibility.AccessibilityManager;
79 import android.view.animation.AnimationUtils;
80 import android.widget.DateTimeView;
81 import android.widget.ImageView;
82 import android.widget.RemoteViews;
83 import android.widget.TextView;
84 import android.widget.Toast;
86 import com.android.internal.logging.MetricsLogger;
87 import com.android.internal.statusbar.IStatusBarService;
88 import com.android.internal.statusbar.StatusBarIcon;
89 import com.android.internal.statusbar.StatusBarIconList;
90 import com.android.internal.util.NotificationColorUtil;
91 import com.android.internal.widget.LockPatternUtils;
92 import com.android.keyguard.KeyguardUpdateMonitor;
93 import com.android.systemui.R;
94 import com.android.systemui.RecentsComponent;
95 import com.android.systemui.SwipeHelper;
96 import com.android.systemui.SystemUI;
97 import com.android.systemui.assist.AssistManager;
98 import com.android.systemui.recents.Recents;
99 import com.android.systemui.statusbar.NotificationData.Entry;
100 import com.android.systemui.statusbar.phone.NavigationBarView;
101 import com.android.systemui.statusbar.phone.NotificationGroupManager;
102 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
103 import com.android.systemui.statusbar.policy.HeadsUpManager;
104 import com.android.systemui.statusbar.policy.PreviewInflater;
105 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
107 import java.util.ArrayList;
108 import java.util.List;
109 import java.util.Locale;
111 import static com.android.keyguard.KeyguardHostView.OnDismissAction;
113 public abstract class BaseStatusBar extends SystemUI implements
114 CommandQueue.Callbacks, ActivatableNotificationView.OnActivatedListener,
115 RecentsComponent.Callbacks, ExpandableNotificationRow.ExpansionLogger,
116 NotificationData.Environment {
117 public static final String TAG = "StatusBar";
118 public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
119 public static final boolean MULTIUSER_DEBUG = false;
121 // STOPSHIP disable once we resolve b/18102199
122 private static final boolean NOTIFICATION_CLICK_DEBUG = true;
124 public static final boolean ENABLE_CHILD_NOTIFICATIONS = Build.IS_DEBUGGABLE
125 && SystemProperties.getBoolean("debug.child_notifs", false);
127 protected static final int MSG_SHOW_RECENT_APPS = 1019;
128 protected static final int MSG_HIDE_RECENT_APPS = 1020;
129 protected static final int MSG_TOGGLE_RECENTS_APPS = 1021;
130 protected static final int MSG_PRELOAD_RECENT_APPS = 1022;
131 protected static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 1023;
132 protected static final int MSG_SHOW_NEXT_AFFILIATED_TASK = 1024;
133 protected static final int MSG_SHOW_PREV_AFFILIATED_TASK = 1025;
135 protected static final boolean ENABLE_HEADS_UP = true;
136 // scores above this threshold should be displayed in heads up mode.
137 protected static final int INTERRUPTION_THRESHOLD = 10;
138 protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
140 // Should match the value in PhoneWindowManager
141 public static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
143 private static final String BANNER_ACTION_CANCEL =
144 "com.android.systemui.statusbar.banner_action_cancel";
145 private static final String BANNER_ACTION_SETUP =
146 "com.android.systemui.statusbar.banner_action_setup";
148 protected CommandQueue mCommandQueue;
149 protected IStatusBarService mBarService;
150 protected H mHandler = createHandler();
153 protected NotificationData mNotificationData;
154 protected NotificationStackScrollLayout mStackScroller;
156 protected NotificationGroupManager mGroupManager = new NotificationGroupManager();
158 // for heads up notifications
159 protected HeadsUpManager mHeadsUpManager;
161 protected int mCurrentUserId = 0;
162 final protected SparseArray<UserInfo> mCurrentProfiles = new SparseArray<UserInfo>();
164 protected int mLayoutDirection = -1; // invalid
165 protected AccessibilityManager mAccessibilityManager;
167 // on-screen navigation buttons
168 protected NavigationBarView mNavigationBarView = null;
170 protected Boolean mScreenOn;
172 // The second field is a bit different from the first one because it only listens to screen on/
173 // screen of events from Keyguard. We need this so we don't have a race condition with the
174 // broadcast. In the future, we should remove the first field altogether and rename the second
176 protected boolean mScreenOnFromKeyguard;
178 protected boolean mVisible;
180 // mScreenOnFromKeyguard && mVisible.
181 private boolean mVisibleToUser;
183 private Locale mLocale;
184 private float mFontScale;
186 protected boolean mUseHeadsUp = false;
187 protected boolean mHeadsUpTicker = false;
188 protected boolean mDisableNotificationAlerts = false;
190 protected DevicePolicyManager mDevicePolicyManager;
191 protected IDreamManager mDreamManager;
192 PowerManager mPowerManager;
193 protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
194 protected int mRowMinHeight;
195 protected int mRowMaxHeight;
197 // public mode, private notifications, etc
198 private boolean mLockscreenPublicMode = false;
199 private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray();
200 private NotificationColorUtil mNotificationColorUtil;
202 private UserManager mUserManager;
204 // UI-specific methods
207 * Create all windows necessary for the status bar (including navigation, overlay panels, etc)
208 * and add them to the window manager.
210 protected abstract void createAndAddWindows();
212 protected WindowManager mWindowManager;
213 protected IWindowManager mWindowManagerService;
215 protected abstract void refreshLayout(int layoutDirection);
217 protected Display mDisplay;
219 private boolean mDeviceProvisioned = false;
221 private RecentsComponent mRecents;
223 protected int mZenMode;
225 // which notification is currently being longpress-examined by the user
226 private NotificationGuts mNotificationGutsExposed;
228 private TimeInterpolator mLinearOutSlowIn, mFastOutLinearIn;
231 * The {@link StatusBarState} of the status bar.
233 protected int mState;
234 protected boolean mBouncerShowing;
235 protected boolean mShowLockscreenNotifications;
237 protected NotificationOverflowContainer mKeyguardIconOverflowContainer;
238 protected DismissView mDismissView;
239 protected EmptyShadeView mEmptyShadeView;
241 private NotificationClicker mNotificationClicker = new NotificationClicker();
243 protected AssistManager mAssistManager;
245 @Override // NotificationData.Environment
246 public boolean isDeviceProvisioned() {
247 return mDeviceProvisioned;
250 protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
252 public void onChange(boolean selfChange) {
253 final boolean provisioned = 0 != Settings.Global.getInt(
254 mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0);
255 if (provisioned != mDeviceProvisioned) {
256 mDeviceProvisioned = provisioned;
257 updateNotifications();
259 final int mode = Settings.Global.getInt(mContext.getContentResolver(),
260 Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
263 updateLockscreenNotificationSetting();
267 private final ContentObserver mLockscreenSettingsObserver = new ContentObserver(mHandler) {
269 public void onChange(boolean selfChange) {
270 // We don't know which user changed LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
271 // so we just dump our cache ...
272 mUsersAllowingPrivateNotifications.clear();
273 // ... and refresh all the notifications
274 updateNotifications();
278 private RemoteViews.OnClickHandler mOnClickHandler = new RemoteViews.OnClickHandler() {
280 public boolean onClickHandler(
281 final View view, final PendingIntent pendingIntent, final Intent fillInIntent) {
283 Log.v(TAG, "Notification click handler invoked for intent: " + pendingIntent);
285 logActionClick(view);
286 // The intent we are sending is for the application, which
287 // won't have permission to immediately start an activity after
288 // the user switches to home. We know it is safe to do at this
289 // point, so make sure new activity switches are now allowed.
291 ActivityManagerNative.getDefault().resumeAppSwitches();
292 } catch (RemoteException e) {
294 final boolean isActivity = pendingIntent.isActivity();
296 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
297 final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity(
298 mContext, pendingIntent.getIntent(), mCurrentUserId);
299 dismissKeyguardThenExecute(new OnDismissAction() {
301 public boolean onDismiss() {
302 if (keyguardShowing && !afterKeyguardGone) {
304 ActivityManagerNative.getDefault()
305 .keyguardWaitingForActivityDrawn();
306 ActivityManagerNative.getDefault().resumeAppSwitches();
307 } catch (RemoteException e) {
311 boolean handled = superOnClickHandler(view, pendingIntent, fillInIntent);
312 overrideActivityPendingAppTransition(keyguardShowing && !afterKeyguardGone);
314 // close the shade if it was open
316 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
318 visibilityChanged(false);
319 mAssistManager.hideAssist();
322 // Wait for activity start.
325 }, afterKeyguardGone);
328 return super.onClickHandler(view, pendingIntent, fillInIntent);
332 private void logActionClick(View view) {
333 ViewParent parent = view.getParent();
334 String key = getNotificationKeyForParent(parent);
336 Log.w(TAG, "Couldn't determine notification for click.");
340 // If this is a default template, determine the index of the button.
341 if (view.getId() == com.android.internal.R.id.action0 &&
342 parent != null && parent instanceof ViewGroup) {
343 ViewGroup actionGroup = (ViewGroup) parent;
344 index = actionGroup.indexOfChild(view);
346 if (NOTIFICATION_CLICK_DEBUG) {
347 Log.d(TAG, "Clicked on button " + index + " for " + key);
350 mBarService.onNotificationActionClick(key, index);
351 } catch (RemoteException e) {
356 private String getNotificationKeyForParent(ViewParent parent) {
357 while (parent != null) {
358 if (parent instanceof ExpandableNotificationRow) {
359 return ((ExpandableNotificationRow) parent).getStatusBarNotification().getKey();
361 parent = parent.getParent();
366 private boolean superOnClickHandler(View view, PendingIntent pendingIntent,
367 Intent fillInIntent) {
368 return super.onClickHandler(view, pendingIntent, fillInIntent);
372 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
374 public void onReceive(Context context, Intent intent) {
375 String action = intent.getAction();
376 if (Intent.ACTION_USER_SWITCHED.equals(action)) {
377 mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
378 updateCurrentProfilesCache();
379 if (true) Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
381 updateLockscreenNotificationSetting();
383 userSwitched(mCurrentUserId);
384 } else if (Intent.ACTION_USER_ADDED.equals(action)) {
385 updateCurrentProfilesCache();
386 } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
387 List<ActivityManager.RecentTaskInfo> recentTask = null;
389 recentTask = ActivityManagerNative.getDefault().getRecentTasks(1,
390 ActivityManager.RECENT_WITH_EXCLUDED
391 | ActivityManager.RECENT_INCLUDE_PROFILES,
393 } catch (RemoteException e) {
394 // Abandon hope activity manager not running.
396 if (recentTask != null && recentTask.size() > 0) {
397 UserInfo user = mUserManager.getUserInfo(recentTask.get(0).userId);
398 if (user != null && user.isManagedProfile()) {
399 Toast toast = Toast.makeText(mContext,
400 R.string.managed_profile_foreground_toast,
402 TextView text = (TextView) toast.getView().findViewById(
403 android.R.id.message);
404 text.setCompoundDrawablesRelativeWithIntrinsicBounds(
405 R.drawable.stat_sys_managed_profile_status, 0, 0, 0);
406 int paddingPx = mContext.getResources().getDimensionPixelSize(
407 R.dimen.managed_profile_toast_padding);
408 text.setCompoundDrawablePadding(paddingPx);
412 } else if (BANNER_ACTION_CANCEL.equals(action) || BANNER_ACTION_SETUP.equals(action)) {
413 NotificationManager noMan = (NotificationManager)
414 mContext.getSystemService(Context.NOTIFICATION_SERVICE);
415 noMan.cancel(R.id.notification_hidden);
417 Settings.Secure.putInt(mContext.getContentResolver(),
418 Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
419 if (BANNER_ACTION_SETUP.equals(action)) {
420 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
422 mContext.startActivity(new Intent(Settings.ACTION_APP_NOTIFICATION_REDACTION)
423 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
431 private final BroadcastReceiver mAllUsersReceiver = new BroadcastReceiver() {
433 public void onReceive(Context context, Intent intent) {
434 String action = intent.getAction();
435 if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action) &&
436 isCurrentProfile(getSendingUserId())) {
437 mUsersAllowingPrivateNotifications.clear();
438 updateLockscreenNotificationSetting();
439 updateNotifications();
444 private final NotificationListenerService mNotificationListener =
445 new NotificationListenerService() {
447 public void onListenerConnected() {
448 if (DEBUG) Log.d(TAG, "onListenerConnected");
449 final StatusBarNotification[] notifications = getActiveNotifications();
450 final RankingMap currentRanking = getCurrentRanking();
451 mHandler.post(new Runnable() {
454 for (StatusBarNotification sbn : notifications) {
455 addNotification(sbn, currentRanking, null /* oldEntry */);
462 public void onNotificationPosted(final StatusBarNotification sbn,
463 final RankingMap rankingMap) {
464 if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
466 mHandler.post(new Runnable() {
470 String key = sbn.getKey();
471 boolean isUpdate = mNotificationData.get(key) != null;
473 // In case we don't allow child notifications, we ignore children of
474 // notifications that have a summary, since we're not going to show them
475 // anyway. This is true also when the summary is canceled,
476 // because children are automatically canceled by NoMan in that case.
477 if (!ENABLE_CHILD_NOTIFICATIONS
478 && mGroupManager.isChildInGroupWithSummary(sbn)) {
480 Log.d(TAG, "Ignoring group child due to existing summary: " + sbn);
483 // Remove existing notification to avoid stale data.
485 removeNotification(key, rankingMap);
487 mNotificationData.updateRanking(rankingMap);
492 updateNotification(sbn, rankingMap);
494 addNotification(sbn, rankingMap, null /* oldEntry */);
502 public void onNotificationRemoved(StatusBarNotification sbn,
503 final RankingMap rankingMap) {
504 if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn);
506 final String key = sbn.getKey();
507 mHandler.post(new Runnable() {
510 removeNotification(key, rankingMap);
517 public void onNotificationRankingUpdate(final RankingMap rankingMap) {
518 if (DEBUG) Log.d(TAG, "onRankingUpdate");
519 if (rankingMap != null) {
520 mHandler.post(new Runnable() {
523 updateNotificationRanking(rankingMap);
530 private void updateCurrentProfilesCache() {
531 synchronized (mCurrentProfiles) {
532 mCurrentProfiles.clear();
533 if (mUserManager != null) {
534 for (UserInfo user : mUserManager.getProfiles(mCurrentUserId)) {
535 mCurrentProfiles.put(user.id, user);
541 public void start() {
542 mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
543 mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
544 mDisplay = mWindowManager.getDefaultDisplay();
545 mDevicePolicyManager = (DevicePolicyManager)mContext.getSystemService(
546 Context.DEVICE_POLICY_SERVICE);
548 mNotificationColorUtil = NotificationColorUtil.getInstance(mContext);
550 mNotificationData = new NotificationData(this);
552 mAccessibilityManager = (AccessibilityManager)
553 mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
555 mDreamManager = IDreamManager.Stub.asInterface(
556 ServiceManager.checkService(DreamService.DREAM_SERVICE));
557 mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
559 mContext.getContentResolver().registerContentObserver(
560 Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), true,
562 mContext.getContentResolver().registerContentObserver(
563 Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false,
565 mContext.getContentResolver().registerContentObserver(
566 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false,
568 UserHandle.USER_ALL);
570 mContext.getContentResolver().registerContentObserver(
571 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
573 mLockscreenSettingsObserver,
574 UserHandle.USER_ALL);
576 mBarService = IStatusBarService.Stub.asInterface(
577 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
579 mRecents = getComponent(Recents.class);
580 mRecents.setCallback(this);
582 final Configuration currentConfig = mContext.getResources().getConfiguration();
583 mLocale = currentConfig.locale;
584 mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale);
585 mFontScale = currentConfig.fontScale;
587 mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
589 mLinearOutSlowIn = AnimationUtils.loadInterpolator(mContext,
590 android.R.interpolator.linear_out_slow_in);
591 mFastOutLinearIn = AnimationUtils.loadInterpolator(mContext,
592 android.R.interpolator.fast_out_linear_in);
594 // Connect in to the status bar manager service
595 StatusBarIconList iconList = new StatusBarIconList();
596 mCommandQueue = new CommandQueue(this, iconList);
598 int[] switches = new int[8];
599 ArrayList<IBinder> binders = new ArrayList<IBinder>();
601 mBarService.registerStatusBar(mCommandQueue, iconList, switches, binders);
602 } catch (RemoteException ex) {
603 // If the system process isn't there we're doomed anyway.
606 createAndAddWindows();
608 mSettingsObserver.onChange(false); // set up
609 disable(switches[0], switches[6], false /* animate */);
610 setSystemUiVisibility(switches[1], 0xffffffff);
611 topAppWindowChanged(switches[2] != 0);
612 // StatusBarManagerService has a back up of IME token and it's restored here.
613 setImeWindowStatus(binders.get(0), switches[3], switches[4], switches[5] != 0);
615 // Set up the initial icon state
616 int N = iconList.size();
618 for (int i=0; i<N; i++) {
619 StatusBarIcon icon = iconList.getIcon(i);
621 addIcon(iconList.getSlot(i), i, viewIndex, icon);
626 // Set up the initial notification state.
628 mNotificationListener.registerAsSystemService(mContext,
629 new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),
630 UserHandle.USER_ALL);
631 } catch (RemoteException e) {
632 Log.e(TAG, "Unable to register notification listener", e);
637 Log.d(TAG, String.format(
638 "init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x",
647 mCurrentUserId = ActivityManager.getCurrentUser();
648 setHeadsUpUser(mCurrentUserId);
650 IntentFilter filter = new IntentFilter();
651 filter.addAction(Intent.ACTION_USER_SWITCHED);
652 filter.addAction(Intent.ACTION_USER_ADDED);
653 filter.addAction(Intent.ACTION_USER_PRESENT);
654 filter.addAction(BANNER_ACTION_CANCEL);
655 filter.addAction(BANNER_ACTION_SETUP);
656 mContext.registerReceiver(mBroadcastReceiver, filter);
658 IntentFilter allUsersFilter = new IntentFilter();
659 allUsersFilter.addAction(
660 DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
661 mContext.registerReceiverAsUser(mAllUsersReceiver, UserHandle.ALL, allUsersFilter,
663 updateCurrentProfilesCache();
666 protected void notifyUserAboutHiddenNotifications() {
667 if (0 != Settings.Secure.getInt(mContext.getContentResolver(),
668 Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 1)) {
669 Log.d(TAG, "user hasn't seen notification about hidden notifications");
670 final LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
671 if (!lockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())) {
672 Log.d(TAG, "insecure lockscreen, skipping notification");
673 Settings.Secure.putInt(mContext.getContentResolver(),
674 Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
677 Log.d(TAG, "disabling lockecreen notifications and alerting the user");
678 // disable lockscreen notifications until user acts on the banner.
679 Settings.Secure.putInt(mContext.getContentResolver(),
680 Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
681 Settings.Secure.putInt(mContext.getContentResolver(),
682 Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0);
684 final String packageName = mContext.getPackageName();
685 PendingIntent cancelIntent = PendingIntent.getBroadcast(mContext, 0,
686 new Intent(BANNER_ACTION_CANCEL).setPackage(packageName),
687 PendingIntent.FLAG_CANCEL_CURRENT);
688 PendingIntent setupIntent = PendingIntent.getBroadcast(mContext, 0,
689 new Intent(BANNER_ACTION_SETUP).setPackage(packageName),
690 PendingIntent.FLAG_CANCEL_CURRENT);
692 final Resources res = mContext.getResources();
693 final int colorRes = com.android.internal.R.color.system_notification_accent_color;
694 Notification.Builder note = new Notification.Builder(mContext)
695 .setSmallIcon(R.drawable.ic_android)
696 .setContentTitle(mContext.getString(R.string.hidden_notifications_title))
697 .setContentText(mContext.getString(R.string.hidden_notifications_text))
698 .setPriority(Notification.PRIORITY_HIGH)
700 .setColor(mContext.getColor(colorRes))
701 .setContentIntent(setupIntent)
702 .addAction(R.drawable.ic_close,
703 mContext.getString(R.string.hidden_notifications_cancel),
705 .addAction(R.drawable.ic_settings,
706 mContext.getString(R.string.hidden_notifications_setup),
709 NotificationManager noMan =
710 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
711 noMan.notify(R.id.notification_hidden, note.build());
715 public void userSwitched(int newUserId) {
716 setHeadsUpUser(newUserId);
719 protected abstract void setHeadsUpUser(int newUserId);
721 @Override // NotificationData.Environment
722 public boolean isNotificationForCurrentProfiles(StatusBarNotification n) {
723 final int thisUserId = mCurrentUserId;
724 final int notificationUserId = n.getUserId();
725 if (DEBUG && MULTIUSER_DEBUG) {
726 Log.v(TAG, String.format("%s: current userid: %d, notification userid: %d",
727 n, thisUserId, notificationUserId));
729 return isCurrentProfile(notificationUserId);
732 protected void setNotificationShown(StatusBarNotification n) {
733 setNotificationsShown(new String[]{n.getKey()});
736 protected void setNotificationsShown(String[] keys) {
738 mNotificationListener.setNotificationsShown(keys);
739 } catch (RuntimeException e) {
740 Log.d(TAG, "failed setNotificationsShown: ", e);
744 protected boolean isCurrentProfile(int userId) {
745 synchronized (mCurrentProfiles) {
746 return userId == UserHandle.USER_ALL || mCurrentProfiles.get(userId) != null;
751 public String getCurrentMediaNotificationKey() {
756 public NotificationGroupManager getGroupManager() {
757 return mGroupManager;
761 * Takes the necessary steps to prepare the status bar for starting an activity, then starts it.
762 * @param action A dismiss action that is called if it's safe to start the activity.
763 * @param afterKeyguardGone Whether the action should be executed after the Keyguard is gone.
765 protected void dismissKeyguardThenExecute(OnDismissAction action, boolean afterKeyguardGone) {
770 protected void onConfigurationChanged(Configuration newConfig) {
771 final Locale locale = mContext.getResources().getConfiguration().locale;
772 final int ld = TextUtils.getLayoutDirectionFromLocale(locale);
773 final float fontScale = newConfig.fontScale;
775 if (! locale.equals(mLocale) || ld != mLayoutDirection || fontScale != mFontScale) {
777 Log.v(TAG, String.format(
778 "config changed locale/LD: %s (%d) -> %s (%d)", mLocale, mLayoutDirection,
782 mLayoutDirection = ld;
787 protected View updateNotificationVetoButton(View row, StatusBarNotification n) {
788 View vetoButton = row.findViewById(R.id.veto);
789 if (n.isClearable()) {
790 final String _pkg = n.getPackageName();
791 final String _tag = n.getTag();
792 final int _id = n.getId();
793 final int _userId = n.getUserId();
794 vetoButton.setOnClickListener(new View.OnClickListener() {
795 public void onClick(View v) {
796 // Accessibility feedback
797 v.announceForAccessibility(
798 mContext.getString(R.string.accessibility_notification_dismissed));
800 mBarService.onNotificationClear(_pkg, _tag, _id, _userId);
802 } catch (RemoteException ex) {
803 // system process is dead if we're here.
807 vetoButton.setVisibility(View.VISIBLE);
809 vetoButton.setVisibility(View.GONE);
811 vetoButton.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
816 protected void applyColorsAndBackgrounds(StatusBarNotification sbn,
817 NotificationData.Entry entry) {
819 if (entry.getContentView().getId()
820 != com.android.internal.R.id.status_bar_latest_event_content) {
821 // Using custom RemoteViews
822 if (entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD
823 && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP) {
824 entry.row.setShowingLegacyBackground(true);
828 // Using platform templates
829 final int color = sbn.getNotification().color;
830 if (isMediaNotification(entry)) {
831 entry.row.setTintColor(color == Notification.COLOR_DEFAULT
833 R.color.notification_material_background_media_default_color)
838 if (entry.icon != null) {
839 entry.icon.setTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
843 public boolean isMediaNotification(NotificationData.Entry entry) {
844 // TODO: confirm that there's a valid media key
845 return entry.getExpandedContentView() != null &&
846 entry.getExpandedContentView()
847 .findViewById(com.android.internal.R.id.media_actions) != null;
850 // The gear button in the guts that links to the app's own notification settings
851 private void startAppOwnNotificationSettingsActivity(Intent intent,
852 final int notificationId, final String notificationTag, final int appUid) {
853 intent.putExtra("notification_id", notificationId);
854 intent.putExtra("notification_tag", notificationTag);
855 startNotificationGutsIntent(intent, appUid);
858 // The (i) button in the guts that links to the system notification settings for that app
859 private void startAppNotificationSettingsActivity(String packageName, final int appUid) {
860 final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
861 intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
862 intent.putExtra(Settings.EXTRA_APP_UID, appUid);
863 startNotificationGutsIntent(intent, appUid);
866 private void startNotificationGutsIntent(final Intent intent, final int appUid) {
867 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
868 dismissKeyguardThenExecute(new OnDismissAction() {
870 public boolean onDismiss() {
871 AsyncTask.execute(new Runnable() {
874 if (keyguardShowing) {
875 ActivityManagerNative.getDefault()
876 .keyguardWaitingForActivityDrawn();
878 TaskStackBuilder.create(mContext)
879 .addNextIntentWithParentStack(intent)
880 .startActivities(null,
881 new UserHandle(UserHandle.getUserId(appUid)));
882 overrideActivityPendingAppTransition(keyguardShowing);
883 } catch (RemoteException e) {
887 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
890 }, false /* afterKeyguardGone */);
893 private void bindGuts(ExpandableNotificationRow row) {
895 final StatusBarNotification sbn = row.getStatusBarNotification();
896 PackageManager pmUser = getPackageManagerForUser(
897 sbn.getUser().getIdentifier());
898 row.setTag(sbn.getPackageName());
899 final View guts = row.getGuts();
900 final String pkg = sbn.getPackageName();
901 String appname = pkg;
902 Drawable pkgicon = null;
905 final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
906 PackageManager.GET_UNINSTALLED_PACKAGES
907 | PackageManager.GET_DISABLED_COMPONENTS);
909 appname = String.valueOf(pmUser.getApplicationLabel(info));
910 pkgicon = pmUser.getApplicationIcon(info);
913 } catch (NameNotFoundException e) {
914 // app is gone, just show package name and generic icon
915 pkgicon = pmUser.getDefaultActivityIcon();
917 ((ImageView) row.findViewById(android.R.id.icon)).setImageDrawable(pkgicon);
918 ((DateTimeView) row.findViewById(R.id.timestamp)).setTime(sbn.getPostTime());
919 ((TextView) row.findViewById(R.id.pkgname)).setText(appname);
920 final View settingsButton = guts.findViewById(R.id.notification_inspect_item);
921 final View appSettingsButton
922 = guts.findViewById(R.id.notification_inspect_app_provided_settings);
924 final int appUidF = appUid;
925 settingsButton.setOnClickListener(new View.OnClickListener() {
926 public void onClick(View v) {
927 MetricsLogger.action(mContext, MetricsLogger.ACTION_NOTE_INFO);
928 startAppNotificationSettingsActivity(pkg, appUidF);
932 final Intent appSettingsQueryIntent
933 = new Intent(Intent.ACTION_MAIN)
934 .addCategory(Notification.INTENT_CATEGORY_NOTIFICATION_PREFERENCES)
936 List<ResolveInfo> infos = pmUser.queryIntentActivities(appSettingsQueryIntent, 0);
937 if (infos.size() > 0) {
938 appSettingsButton.setVisibility(View.VISIBLE);
939 appSettingsButton.setContentDescription(
940 mContext.getResources().getString(
941 R.string.status_bar_notification_app_settings_title,
944 final Intent appSettingsLaunchIntent = new Intent(appSettingsQueryIntent)
945 .setClassName(pkg, infos.get(0).activityInfo.name);
946 appSettingsButton.setOnClickListener(new View.OnClickListener() {
947 public void onClick(View v) {
948 MetricsLogger.action(mContext, MetricsLogger.ACTION_APP_NOTE_SETTINGS);
949 startAppOwnNotificationSettingsActivity(appSettingsLaunchIntent,
956 appSettingsButton.setVisibility(View.GONE);
959 settingsButton.setVisibility(View.GONE);
960 appSettingsButton.setVisibility(View.GONE);
965 protected SwipeHelper.LongPressListener getNotificationLongClicker() {
966 return new SwipeHelper.LongPressListener() {
968 public boolean onLongPress(View v, int x, int y) {
971 if (!(v instanceof ExpandableNotificationRow)) {
974 if (v.getWindowToken() == null) {
975 Log.e(TAG, "Trying to show notification guts, but not attached to window");
979 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
982 // Assume we are a status_bar_notification_row
983 final NotificationGuts guts = row.getGuts();
985 // This view has no guts. Examples are the more card or the dismiss all view
990 if (guts.getVisibility() == View.VISIBLE) {
991 Log.e(TAG, "Trying to show notification guts, but already visible");
995 MetricsLogger.action(mContext, MetricsLogger.ACTION_NOTE_CONTROLS);
996 guts.setVisibility(View.VISIBLE);
997 final double horz = Math.max(guts.getWidth() - x, x);
998 final double vert = Math.max(guts.getActualHeight() - y, y);
999 final float r = (float) Math.hypot(horz, vert);
1001 = ViewAnimationUtils.createCircularReveal(guts, x, y, 0, r);
1003 a.setInterpolator(mLinearOutSlowIn);
1006 mNotificationGutsExposed = guts;
1013 public void dismissPopups() {
1014 if (mNotificationGutsExposed != null) {
1015 final NotificationGuts v = mNotificationGutsExposed;
1016 mNotificationGutsExposed = null;
1018 if (v.getWindowToken() == null) return;
1020 final int x = (v.getLeft() + v.getRight()) / 2;
1021 final int y = (v.getTop() + v.getActualHeight() / 2);
1022 final Animator a = ViewAnimationUtils.createCircularReveal(v,
1025 a.setInterpolator(mFastOutLinearIn);
1026 a.addListener(new AnimatorListenerAdapter() {
1028 public void onAnimationEnd(Animator animation) {
1029 super.onAnimationEnd(animation);
1030 v.setVisibility(View.GONE);
1038 public void showRecentApps(boolean triggeredFromAltTab) {
1039 int msg = MSG_SHOW_RECENT_APPS;
1040 mHandler.removeMessages(msg);
1041 mHandler.obtainMessage(msg, triggeredFromAltTab ? 1 : 0, 0).sendToTarget();
1045 public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
1046 int msg = MSG_HIDE_RECENT_APPS;
1047 mHandler.removeMessages(msg);
1048 mHandler.obtainMessage(msg, triggeredFromAltTab ? 1 : 0,
1049 triggeredFromHomeKey ? 1 : 0).sendToTarget();
1053 public void toggleRecentApps() {
1058 public void preloadRecentApps() {
1059 int msg = MSG_PRELOAD_RECENT_APPS;
1060 mHandler.removeMessages(msg);
1061 mHandler.sendEmptyMessage(msg);
1065 public void cancelPreloadRecentApps() {
1066 int msg = MSG_CANCEL_PRELOAD_RECENT_APPS;
1067 mHandler.removeMessages(msg);
1068 mHandler.sendEmptyMessage(msg);
1071 /** Jumps to the next affiliated task in the group. */
1072 public void showNextAffiliatedTask() {
1073 int msg = MSG_SHOW_NEXT_AFFILIATED_TASK;
1074 mHandler.removeMessages(msg);
1075 mHandler.sendEmptyMessage(msg);
1078 /** Jumps to the previous affiliated task in the group. */
1079 public void showPreviousAffiliatedTask() {
1080 int msg = MSG_SHOW_PREV_AFFILIATED_TASK;
1081 mHandler.removeMessages(msg);
1082 mHandler.sendEmptyMessage(msg);
1085 protected H createHandler() {
1089 static void sendCloseSystemWindows(Context context, String reason) {
1090 if (ActivityManagerNative.isSystemReady()) {
1092 ActivityManagerNative.getDefault().closeSystemDialogs(reason);
1093 } catch (RemoteException e) {
1098 protected abstract View getStatusBarView();
1100 protected View.OnTouchListener mRecentsPreloadOnTouchListener = new View.OnTouchListener() {
1101 // additional optimization when we have software system buttons - start loading the recent
1102 // tasks on touch down
1104 public boolean onTouch(View v, MotionEvent event) {
1105 int action = event.getAction() & MotionEvent.ACTION_MASK;
1106 if (action == MotionEvent.ACTION_DOWN) {
1108 } else if (action == MotionEvent.ACTION_CANCEL) {
1109 cancelPreloadingRecents();
1110 } else if (action == MotionEvent.ACTION_UP) {
1111 if (!v.isPressed()) {
1112 cancelPreloadingRecents();
1120 /** Proxy for RecentsComponent */
1122 protected void showRecents(boolean triggeredFromAltTab) {
1123 if (mRecents != null) {
1124 sendCloseSystemWindows(mContext, SYSTEM_DIALOG_REASON_RECENT_APPS);
1125 mRecents.showRecents(triggeredFromAltTab, getStatusBarView());
1129 protected void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
1130 if (mRecents != null) {
1131 mRecents.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
1135 protected void toggleRecents() {
1136 if (mRecents != null) {
1137 sendCloseSystemWindows(mContext, SYSTEM_DIALOG_REASON_RECENT_APPS);
1138 mRecents.toggleRecents(mDisplay, mLayoutDirection, getStatusBarView());
1142 protected void preloadRecents() {
1143 if (mRecents != null) {
1144 mRecents.preloadRecents();
1148 protected void cancelPreloadingRecents() {
1149 if (mRecents != null) {
1150 mRecents.cancelPreloadingRecents();
1154 protected void showRecentsNextAffiliatedTask() {
1155 if (mRecents != null) {
1156 mRecents.showNextAffiliatedTask();
1160 protected void showRecentsPreviousAffiliatedTask() {
1161 if (mRecents != null) {
1162 mRecents.showPrevAffiliatedTask();
1167 public void onVisibilityChanged(boolean visible) {
1172 * If there is an active heads-up notification and it has a fullscreen intent, fire it now.
1174 public abstract void maybeEscalateHeadsUp();
1177 * Save the current "public" (locked and secure) state of the lockscreen.
1179 public void setLockscreenPublicMode(boolean publicMode) {
1180 mLockscreenPublicMode = publicMode;
1183 public boolean isLockscreenPublicMode() {
1184 return mLockscreenPublicMode;
1188 * Has the given user chosen to allow their private (full) notifications to be shown even
1189 * when the lockscreen is in "public" (secure & locked) mode?
1191 public boolean userAllowsPrivateNotificationsInPublic(int userHandle) {
1192 if (userHandle == UserHandle.USER_ALL) {
1196 if (mUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
1197 final boolean allowedByUser = 0 != Settings.Secure.getIntForUser(
1198 mContext.getContentResolver(),
1199 Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle);
1200 final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(null /* admin */,
1202 final boolean allowedByDpm = (dpmFlags
1203 & DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS) == 0;
1204 final boolean allowed = allowedByUser && allowedByDpm;
1205 mUsersAllowingPrivateNotifications.append(userHandle, allowed);
1209 return mUsersAllowingPrivateNotifications.get(userHandle);
1213 * Returns true if we're on a secure lockscreen and the user wants to hide "sensitive"
1214 * notification data. If so, private notifications should show their (possibly
1215 * auto-generated) publicVersion, and secret notifications should be totally invisible.
1217 @Override // NotificationData.Environment
1218 public boolean shouldHideSensitiveContents(int userid) {
1219 return isLockscreenPublicMode() && !userAllowsPrivateNotificationsInPublic(userid);
1222 public void onNotificationClear(StatusBarNotification notification) {
1224 mBarService.onNotificationClear(
1225 notification.getPackageName(),
1226 notification.getTag(),
1227 notification.getId(),
1228 notification.getUserId());
1229 } catch (android.os.RemoteException ex) {
1234 protected class H extends Handler {
1235 public void handleMessage(Message m) {
1237 case MSG_SHOW_RECENT_APPS:
1238 showRecents(m.arg1 > 0);
1240 case MSG_HIDE_RECENT_APPS:
1241 hideRecents(m.arg1 > 0, m.arg2 > 0);
1243 case MSG_TOGGLE_RECENTS_APPS:
1246 case MSG_PRELOAD_RECENT_APPS:
1249 case MSG_CANCEL_PRELOAD_RECENT_APPS:
1250 cancelPreloadingRecents();
1252 case MSG_SHOW_NEXT_AFFILIATED_TASK:
1253 showRecentsNextAffiliatedTask();
1255 case MSG_SHOW_PREV_AFFILIATED_TASK:
1256 showRecentsPreviousAffiliatedTask();
1262 protected void workAroundBadLayerDrawableOpacity(View v) {
1265 protected boolean inflateViews(Entry entry, ViewGroup parent) {
1266 PackageManager pmUser = getPackageManagerForUser(
1267 entry.notification.getUser().getIdentifier());
1269 int maxHeight = mRowMaxHeight;
1270 final StatusBarNotification sbn = entry.notification;
1271 RemoteViews contentView = sbn.getNotification().contentView;
1272 RemoteViews bigContentView = sbn.getNotification().bigContentView;
1273 RemoteViews headsUpContentView = sbn.getNotification().headsUpContentView;
1275 if (contentView == null) {
1280 Log.v(TAG, "publicNotification: " + sbn.getNotification().publicVersion);
1283 Notification publicNotification = sbn.getNotification().publicVersion;
1285 ExpandableNotificationRow row;
1287 // Stash away previous user expansion state so we can restore it at
1289 boolean hasUserChangedExpansion = false;
1290 boolean userExpanded = false;
1291 boolean userLocked = false;
1293 if (entry.row != null) {
1295 hasUserChangedExpansion = row.hasUserChangedExpansion();
1296 userExpanded = row.isUserExpanded();
1297 userLocked = row.isUserLocked();
1299 if (hasUserChangedExpansion) {
1300 row.setUserExpanded(userExpanded);
1303 // create the row view
1304 LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
1305 Context.LAYOUT_INFLATER_SERVICE);
1306 row = (ExpandableNotificationRow) inflater.inflate(R.layout.status_bar_notification_row,
1308 row.setExpansionLogger(this, entry.notification.getKey());
1309 row.setGroupManager(mGroupManager);
1312 workAroundBadLayerDrawableOpacity(row);
1313 View vetoButton = updateNotificationVetoButton(row, sbn);
1314 vetoButton.setContentDescription(mContext.getString(
1315 R.string.accessibility_remove_notification));
1317 // NB: the large icon is now handled entirely by the template
1319 // bind the click event to the content area
1320 NotificationContentView contentContainer = row.getPrivateLayout();
1321 NotificationContentView contentContainerPublic = row.getPublicLayout();
1323 row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
1325 mNotificationClicker.register(row, sbn);
1327 // set up the adaptive layout
1328 View contentViewLocal = null;
1329 View bigContentViewLocal = null;
1330 View headsUpContentViewLocal = null;
1332 contentViewLocal = contentView.apply(
1333 sbn.getPackageContext(mContext),
1336 if (bigContentView != null) {
1337 bigContentViewLocal = bigContentView.apply(
1338 sbn.getPackageContext(mContext),
1342 if (headsUpContentView != null) {
1343 headsUpContentViewLocal = headsUpContentView.apply(
1344 sbn.getPackageContext(mContext),
1349 catch (RuntimeException e) {
1350 final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId());
1351 Log.e(TAG, "couldn't inflate view for notification " + ident, e);
1355 if (contentViewLocal != null) {
1356 contentViewLocal.setIsRootNamespace(true);
1357 contentContainer.setContractedChild(contentViewLocal);
1359 if (bigContentViewLocal != null) {
1360 bigContentViewLocal.setIsRootNamespace(true);
1361 contentContainer.setExpandedChild(bigContentViewLocal);
1363 if (headsUpContentViewLocal != null) {
1364 headsUpContentViewLocal.setIsRootNamespace(true);
1365 contentContainer.setHeadsUpChild(headsUpContentViewLocal);
1368 // now the public version
1369 View publicViewLocal = null;
1370 if (publicNotification != null) {
1372 publicViewLocal = publicNotification.contentView.apply(
1373 sbn.getPackageContext(mContext),
1374 contentContainerPublic, mOnClickHandler);
1376 if (publicViewLocal != null) {
1377 publicViewLocal.setIsRootNamespace(true);
1378 contentContainerPublic.setContractedChild(publicViewLocal);
1381 catch (RuntimeException e) {
1382 final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId());
1383 Log.e(TAG, "couldn't inflate public view for notification " + ident, e);
1384 publicViewLocal = null;
1388 // Extract target SDK version.
1390 ApplicationInfo info = pmUser.getApplicationInfo(sbn.getPackageName(), 0);
1391 entry.targetSdk = info.targetSdkVersion;
1392 } catch (NameNotFoundException ex) {
1393 Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex);
1396 if (publicViewLocal == null) {
1397 // Add a basic notification template
1398 publicViewLocal = LayoutInflater.from(mContext).inflate(
1399 R.layout.notification_public_default,
1400 contentContainerPublic, false);
1401 publicViewLocal.setIsRootNamespace(true);
1403 final TextView title = (TextView) publicViewLocal.findViewById(R.id.title);
1405 title.setText(pmUser.getApplicationLabel(
1406 pmUser.getApplicationInfo(entry.notification.getPackageName(), 0)));
1407 } catch (NameNotFoundException e) {
1408 title.setText(entry.notification.getPackageName());
1411 final ImageView icon = (ImageView) publicViewLocal.findViewById(R.id.icon);
1412 final ImageView profileBadge = (ImageView) publicViewLocal.findViewById(
1413 R.id.profile_badge_line3);
1415 final StatusBarIcon ic = new StatusBarIcon(
1416 entry.notification.getUser(),
1417 entry.notification.getPackageName(),
1418 entry.notification.getNotification().getSmallIcon(),
1419 entry.notification.getNotification().iconLevel,
1420 entry.notification.getNotification().number,
1421 entry.notification.getNotification().tickerText);
1423 Drawable iconDrawable = StatusBarIconView.getIcon(mContext, ic);
1424 icon.setImageDrawable(iconDrawable);
1425 if (entry.targetSdk >= Build.VERSION_CODES.LOLLIPOP
1426 || mNotificationColorUtil.isGrayscaleIcon(iconDrawable)) {
1427 icon.setBackgroundResource(
1428 com.android.internal.R.drawable.notification_icon_legacy_bg);
1429 int padding = mContext.getResources().getDimensionPixelSize(
1430 com.android.internal.R.dimen.notification_large_icon_circle_padding);
1431 icon.setPadding(padding, padding, padding, padding);
1432 if (sbn.getNotification().color != Notification.COLOR_DEFAULT) {
1433 icon.getBackground().setColorFilter(
1434 sbn.getNotification().color, PorterDuff.Mode.SRC_ATOP);
1438 if (profileBadge != null) {
1439 Drawable profileDrawable = mContext.getPackageManager().getUserBadgeForDensity(
1440 entry.notification.getUser(), 0);
1441 if (profileDrawable != null) {
1442 profileBadge.setImageDrawable(profileDrawable);
1443 profileBadge.setVisibility(View.VISIBLE);
1445 profileBadge.setVisibility(View.GONE);
1449 final View privateTime = contentViewLocal.findViewById(com.android.internal.R.id.time);
1450 final DateTimeView time = (DateTimeView) publicViewLocal.findViewById(R.id.time);
1451 if (privateTime != null && privateTime.getVisibility() == View.VISIBLE) {
1452 time.setVisibility(View.VISIBLE);
1453 time.setTime(entry.notification.getNotification().when);
1456 final TextView text = (TextView) publicViewLocal.findViewById(R.id.text);
1458 text.setText(R.string.notification_hidden_text);
1459 text.setTextAppearance(mContext,
1460 R.style.TextAppearance_Material_Notification_Parenthetical);
1463 int topPadding = Notification.Builder.calculateTopPadding(mContext,
1464 false /* hasThreeLines */,
1465 mContext.getResources().getConfiguration().fontScale);
1466 title.setPadding(0, topPadding, 0, 0);
1468 contentContainerPublic.setContractedChild(publicViewLocal);
1469 entry.autoRedacted = true;
1472 if (MULTIUSER_DEBUG) {
1473 TextView debug = (TextView) row.findViewById(R.id.debug_info);
1474 if (debug != null) {
1475 debug.setVisibility(View.VISIBLE);
1476 debug.setText("CU " + mCurrentUserId +" NU " + entry.notification.getUserId());
1480 entry.row.setHeightRange(mRowMinHeight, maxHeight);
1481 entry.row.setOnActivatedListener(this);
1482 entry.row.setExpandable(bigContentViewLocal != null);
1484 applyColorsAndBackgrounds(sbn, entry);
1486 // Restore previous flags.
1487 if (hasUserChangedExpansion) {
1488 // Note: setUserExpanded() conveniently ignores calls with
1489 // userExpanded=true if !isExpandable().
1490 row.setUserExpanded(userExpanded);
1492 row.setUserLocked(userLocked);
1493 row.setStatusBarNotification(entry.notification);
1498 public void startPendingIntentDismissingKeyguard(final PendingIntent intent) {
1499 if (!isDeviceProvisioned()) return;
1501 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
1502 final boolean afterKeyguardGone = intent.isActivity()
1503 && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
1505 dismissKeyguardThenExecute(new OnDismissAction() {
1506 public boolean onDismiss() {
1511 if (keyguardShowing && !afterKeyguardGone) {
1512 ActivityManagerNative.getDefault()
1513 .keyguardWaitingForActivityDrawn();
1516 // The intent we are sending is for the application, which
1517 // won't have permission to immediately start an activity after
1518 // the user switches to home. We know it is safe to do at this
1519 // point, so make sure new activity switches are now allowed.
1520 ActivityManagerNative.getDefault().resumeAppSwitches();
1521 } catch (RemoteException e) {
1526 } catch (PendingIntent.CanceledException e) {
1527 // the stack trace isn't very helpful here.
1528 // Just log the exception message.
1529 Log.w(TAG, "Sending intent failed: " + e);
1531 // TODO: Dismiss Keyguard.
1533 if (intent.isActivity()) {
1534 mAssistManager.hideAssist();
1535 overrideActivityPendingAppTransition(keyguardShowing
1536 && !afterKeyguardGone);
1541 // close the shade if it was open
1542 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
1543 true /* force */, true /* delayed */);
1544 visibilityChanged(false);
1548 }, afterKeyguardGone);
1551 private final class NotificationClicker implements View.OnClickListener {
1552 public void onClick(final View v) {
1553 if (!(v instanceof ExpandableNotificationRow)) {
1554 Log.e(TAG, "NotificationClicker called on a view that is not a notification row.");
1558 final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
1559 final StatusBarNotification sbn = row.getStatusBarNotification();
1561 Log.e(TAG, "NotificationClicker called on an unclickable notification,");
1565 final PendingIntent intent = sbn.getNotification().contentIntent;
1566 final String notificationKey = sbn.getKey();
1568 if (NOTIFICATION_CLICK_DEBUG) {
1569 Log.d(TAG, "Clicked on content of " + notificationKey);
1571 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
1572 final boolean afterKeyguardGone = intent.isActivity()
1573 && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
1575 dismissKeyguardThenExecute(new OnDismissAction() {
1576 public boolean onDismiss() {
1577 if (mHeadsUpManager != null && mHeadsUpManager.isHeadsUp(notificationKey)) {
1578 // Release the HUN notification to the shade.
1580 // In most cases, when FLAG_AUTO_CANCEL is set, the notification will
1581 // become canceled shortly by NoMan, but we can't assume that.
1582 HeadsUpManager.setIsClickedNotification(row, true);
1583 mHeadsUpManager.releaseImmediately(notificationKey);
1589 if (keyguardShowing && !afterKeyguardGone) {
1590 ActivityManagerNative.getDefault()
1591 .keyguardWaitingForActivityDrawn();
1594 // The intent we are sending is for the application, which
1595 // won't have permission to immediately start an activity after
1596 // the user switches to home. We know it is safe to do at this
1597 // point, so make sure new activity switches are now allowed.
1598 ActivityManagerNative.getDefault().resumeAppSwitches();
1599 } catch (RemoteException e) {
1602 if (intent != null) {
1605 } catch (PendingIntent.CanceledException e) {
1606 // the stack trace isn't very helpful here.
1607 // Just log the exception message.
1608 Log.w(TAG, "Sending contentIntent failed: " + e);
1610 // TODO: Dismiss Keyguard.
1612 if (intent.isActivity()) {
1613 mAssistManager.hideAssist();
1614 overrideActivityPendingAppTransition(keyguardShowing
1615 && !afterKeyguardGone);
1620 mBarService.onNotificationClick(notificationKey);
1621 } catch (RemoteException ex) {
1622 // system process is dead if we're here.
1627 // close the shade if it was open
1628 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
1629 true /* force */, true /* delayed */);
1630 visibilityChanged(false);
1634 }, afterKeyguardGone);
1637 public void register(ExpandableNotificationRow row, StatusBarNotification sbn) {
1638 final PendingIntent contentIntent = sbn.getNotification().contentIntent;
1639 if (contentIntent != null) {
1640 row.setOnClickListener(this);
1642 row.setOnClickListener(null);
1647 public void animateCollapsePanels(int flags, boolean force) {
1650 public void animateCollapsePanels(int flags, boolean force, boolean delayed) {
1653 public void overrideActivityPendingAppTransition(boolean keyguardShowing) {
1654 if (keyguardShowing) {
1656 mWindowManagerService.overridePendingAppTransition(null, 0, 0, null);
1657 } catch (RemoteException e) {
1658 Log.w(TAG, "Error overriding app transition: " + e);
1663 protected void visibilityChanged(boolean visible) {
1664 if (mVisible != visible) {
1670 updateVisibleToUser();
1673 protected void updateVisibleToUser() {
1674 boolean oldVisibleToUser = mVisibleToUser;
1675 mVisibleToUser = mVisible && mScreenOnFromKeyguard;
1677 if (oldVisibleToUser != mVisibleToUser) {
1678 handleVisibleToUserChanged(mVisibleToUser);
1683 * The LEDs are turned off when the notification panel is shown, even just a little bit.
1685 protected void handleVisibleToUserChanged(boolean visibleToUser) {
1687 if (visibleToUser) {
1688 boolean pinnedHeadsUp = mHeadsUpManager.hasPinnedHeadsUp();
1689 boolean clearNotificationEffects =
1690 ((mShowLockscreenNotifications && mState == StatusBarState.KEYGUARD) ||
1691 (!pinnedHeadsUp && (mState == StatusBarState.SHADE
1692 || mState == StatusBarState.SHADE_LOCKED)));
1693 int notificationLoad = mNotificationData.getActiveNotifications().size();
1694 if (pinnedHeadsUp && isPanelFullyCollapsed()) {
1695 notificationLoad = 1;
1697 MetricsLogger.histogram(mContext, "note_load", notificationLoad);
1699 mBarService.onPanelRevealed(clearNotificationEffects, notificationLoad);
1701 mBarService.onPanelHidden();
1703 } catch (RemoteException ex) {
1704 // Won't fail unless the world has ended.
1709 * Clear Buzz/Beep/Blink.
1711 public void clearNotificationEffects() {
1713 mBarService.clearNotificationEffects();
1714 } catch (RemoteException e) {
1715 // Won't fail unless the world has ended.
1719 protected abstract boolean isPanelFullyCollapsed();
1722 * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
1723 * about the failure.
1725 * WARNING: this will call back into us. Don't hold any locks.
1727 void handleNotificationError(StatusBarNotification n, String message) {
1728 removeNotification(n.getKey(), null);
1730 mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(),
1731 n.getInitialPid(), message, n.getUserId());
1732 } catch (RemoteException ex) {
1737 protected StatusBarNotification removeNotificationViews(String key, RankingMap ranking) {
1738 NotificationData.Entry entry = mNotificationData.remove(key, ranking);
1739 if (entry == null) {
1740 Log.w(TAG, "removeNotification for unknown key: " + key);
1743 updateNotifications();
1744 return entry.notification;
1747 protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn) {
1749 Log.d(TAG, "createNotificationViews(notification=" + sbn);
1751 final StatusBarIconView iconView = createIcon(sbn);
1752 if (iconView == null) {
1756 // Construct the expanded view.
1757 NotificationData.Entry entry = new NotificationData.Entry(sbn, iconView);
1758 if (!inflateViews(entry, mStackScroller)) {
1759 handleNotificationError(sbn, "Couldn't expand RemoteViews for: " + sbn);
1765 protected StatusBarIconView createIcon(StatusBarNotification sbn) {
1766 // Construct the icon.
1767 Notification n = sbn.getNotification();
1768 final StatusBarIconView iconView = new StatusBarIconView(mContext,
1769 sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), n);
1770 iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
1772 final Icon smallIcon = n.getSmallIcon();
1773 if (smallIcon == null) {
1774 handleNotificationError(sbn,
1775 "No small icon in notification from " + sbn.getPackageName());
1778 final StatusBarIcon ic = new StatusBarIcon(
1780 sbn.getPackageName(),
1785 if (!iconView.set(ic)) {
1786 handleNotificationError(sbn, "Couldn't create icon: " + ic);
1792 protected void addNotificationViews(Entry entry, RankingMap ranking) {
1793 if (entry == null) {
1796 // Add the expanded view and icon.
1797 mNotificationData.add(entry, ranking);
1798 updateNotifications();
1802 * @return The number of notifications we show on Keyguard.
1804 protected abstract int getMaxKeyguardNotifications();
1807 * Updates expanded, dimmed and locked states of notification rows.
1809 protected void updateRowStates() {
1810 int maxKeyguardNotifications = getMaxKeyguardNotifications();
1811 mKeyguardIconOverflowContainer.getIconsView().removeAllViews();
1813 ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
1814 final int N = activeNotifications.size();
1816 int visibleNotifications = 0;
1817 boolean onKeyguard = mState == StatusBarState.KEYGUARD;
1818 for (int i = 0; i < N; i++) {
1819 NotificationData.Entry entry = activeNotifications.get(i);
1821 entry.row.setExpansionDisabled(true);
1823 entry.row.setExpansionDisabled(false);
1824 if (!entry.row.isUserLocked()) {
1825 boolean top = (i == 0);
1826 entry.row.setSystemExpanded(top);
1829 boolean isInvisibleChild = !mGroupManager.isVisible(entry.notification);
1830 boolean showOnKeyguard = shouldShowOnKeyguard(entry.notification);
1831 if ((isLockscreenPublicMode() && !mShowLockscreenNotifications) ||
1832 (onKeyguard && (visibleNotifications >= maxKeyguardNotifications
1833 || !showOnKeyguard || isInvisibleChild))) {
1834 entry.row.setVisibility(View.GONE);
1835 if (onKeyguard && showOnKeyguard && !isInvisibleChild) {
1836 mKeyguardIconOverflowContainer.getIconsView().addNotification(entry);
1839 boolean wasGone = entry.row.getVisibility() == View.GONE;
1840 entry.row.setVisibility(View.VISIBLE);
1841 if (!isInvisibleChild) {
1843 // notify the scroller of a child addition
1844 mStackScroller.generateAddAnimation(entry.row, true /* fromMoreCard */);
1846 visibleNotifications++;
1851 mStackScroller.updateOverflowContainerVisibility(onKeyguard
1852 && mKeyguardIconOverflowContainer.getIconsView().getChildCount() > 0);
1854 mStackScroller.changeViewPosition(mDismissView, mStackScroller.getChildCount() - 1);
1855 mStackScroller.changeViewPosition(mEmptyShadeView, mStackScroller.getChildCount() - 2);
1856 mStackScroller.changeViewPosition(mKeyguardIconOverflowContainer,
1857 mStackScroller.getChildCount() - 3);
1860 private boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
1861 return mShowLockscreenNotifications && !mNotificationData.isAmbient(sbn.getKey());
1864 protected void setZenMode(int mode) {
1865 if (!isDeviceProvisioned()) return;
1867 updateNotifications();
1870 // extended in PhoneStatusBar
1871 protected void setShowLockscreenNotifications(boolean show) {
1872 mShowLockscreenNotifications = show;
1875 private void updateLockscreenNotificationSetting() {
1876 final boolean show = Settings.Secure.getIntForUser(mContext.getContentResolver(),
1877 Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
1879 mCurrentUserId) != 0;
1880 final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(
1881 null /* admin */, mCurrentUserId);
1882 final boolean allowedByDpm = (dpmFlags
1883 & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0;
1884 setShowLockscreenNotifications(show && allowedByDpm);
1887 protected abstract void setAreThereNotifications();
1888 protected abstract void updateNotifications();
1889 public abstract boolean shouldDisableNavbarGestures();
1891 public abstract void addNotification(StatusBarNotification notification,
1892 RankingMap ranking, Entry oldEntry);
1893 protected abstract void updateNotificationRanking(RankingMap ranking);
1894 public abstract void removeNotification(String key, RankingMap ranking);
1896 public void updateNotification(StatusBarNotification notification, RankingMap ranking) {
1897 if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
1899 final String key = notification.getKey();
1900 Entry entry = mNotificationData.get(key);
1901 if (entry == null) {
1905 Notification n = notification.getNotification();
1907 logUpdate(entry, n);
1909 boolean applyInPlace = shouldApplyInPlace(entry, n);
1910 boolean shouldInterrupt = shouldInterrupt(entry, notification);
1911 boolean alertAgain = alertAgain(entry, n);
1913 entry.notification = notification;
1914 mGroupManager.onEntryUpdated(entry, entry.notification);
1916 boolean updateSuccessful = false;
1918 if (DEBUG) Log.d(TAG, "reusing notification for key: " + key);
1920 if (entry.icon != null) {
1922 final StatusBarIcon ic = new StatusBarIcon(
1923 notification.getUser(),
1924 notification.getPackageName(),
1929 entry.icon.setNotification(n);
1930 if (!entry.icon.set(ic)) {
1931 handleNotificationError(notification, "Couldn't update icon: " + ic);
1935 updateNotificationViews(entry, notification);
1936 updateSuccessful = true;
1938 catch (RuntimeException e) {
1939 // It failed to apply cleanly.
1940 Log.w(TAG, "Couldn't reapply views for package " + n.contentView.getPackage(), e);
1943 if (!updateSuccessful) {
1944 if (DEBUG) Log.d(TAG, "not reusing notification for key: " + key);
1945 final StatusBarIcon ic = new StatusBarIcon(
1946 notification.getUser(),
1947 notification.getPackageName(),
1952 entry.icon.setNotification(n);
1954 inflateViews(entry, mStackScroller);
1956 updateHeadsUp(key, entry, shouldInterrupt, alertAgain);
1957 mNotificationData.updateRanking(ranking);
1958 updateNotifications();
1960 // Update the veto button accordingly (and as a result, whether this row is
1961 // swipe-dismissable)
1962 updateNotificationVetoButton(entry.row, notification);
1966 boolean isForCurrentUser = isNotificationForCurrentProfiles(notification);
1967 Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");
1970 setAreThereNotifications();
1973 protected abstract void updateHeadsUp(String key, Entry entry, boolean shouldInterrupt,
1974 boolean alertAgain);
1976 private void logUpdate(Entry oldEntry, Notification n) {
1977 StatusBarNotification oldNotification = oldEntry.notification;
1978 Log.d(TAG, "old notification: when=" + oldNotification.getNotification().when
1979 + " ongoing=" + oldNotification.isOngoing()
1980 + " expanded=" + oldEntry.getContentView()
1981 + " contentView=" + oldNotification.getNotification().contentView
1982 + " bigContentView=" + oldNotification.getNotification().bigContentView
1983 + " publicView=" + oldNotification.getNotification().publicVersion
1984 + " rowParent=" + oldEntry.row.getParent());
1985 Log.d(TAG, "new notification: when=" + n.when
1986 + " ongoing=" + oldNotification.isOngoing()
1987 + " contentView=" + n.contentView
1988 + " bigContentView=" + n.bigContentView
1989 + " publicView=" + n.publicVersion);
1993 * @return whether we can just reapply the RemoteViews from a notification in-place when it is
1996 private boolean shouldApplyInPlace(Entry entry, Notification n) {
1997 StatusBarNotification oldNotification = entry.notification;
1998 // XXX: modify when we do something more intelligent with the two content views
1999 final RemoteViews oldContentView = oldNotification.getNotification().contentView;
2000 final RemoteViews contentView = n.contentView;
2001 final RemoteViews oldBigContentView = oldNotification.getNotification().bigContentView;
2002 final RemoteViews bigContentView = n.bigContentView;
2003 final RemoteViews oldHeadsUpContentView
2004 = oldNotification.getNotification().headsUpContentView;
2005 final RemoteViews headsUpContentView = n.headsUpContentView;
2006 final Notification oldPublicNotification = oldNotification.getNotification().publicVersion;
2007 final RemoteViews oldPublicContentView = oldPublicNotification != null
2008 ? oldPublicNotification.contentView : null;
2009 final Notification publicNotification = n.publicVersion;
2010 final RemoteViews publicContentView = publicNotification != null
2011 ? publicNotification.contentView : null;
2012 boolean contentsUnchanged = entry.getContentView() != null
2013 && contentView.getPackage() != null
2014 && oldContentView.getPackage() != null
2015 && oldContentView.getPackage().equals(contentView.getPackage())
2016 && oldContentView.getLayoutId() == contentView.getLayoutId();
2017 // large view may be null
2018 boolean bigContentsUnchanged =
2019 (entry.getExpandedContentView() == null && bigContentView == null)
2020 || ((entry.getExpandedContentView() != null && bigContentView != null)
2021 && bigContentView.getPackage() != null
2022 && oldBigContentView.getPackage() != null
2023 && oldBigContentView.getPackage().equals(bigContentView.getPackage())
2024 && oldBigContentView.getLayoutId() == bigContentView.getLayoutId());
2025 boolean headsUpContentsUnchanged =
2026 (oldHeadsUpContentView == null && headsUpContentView == null)
2027 || ((oldHeadsUpContentView != null && headsUpContentView != null)
2028 && headsUpContentView.getPackage() != null
2029 && oldHeadsUpContentView.getPackage() != null
2030 && oldHeadsUpContentView.getPackage().equals(headsUpContentView.getPackage())
2031 && oldHeadsUpContentView.getLayoutId() == headsUpContentView.getLayoutId());
2032 boolean publicUnchanged =
2033 (oldPublicContentView == null && publicContentView == null)
2034 || ((oldPublicContentView != null && publicContentView != null)
2035 && publicContentView.getPackage() != null
2036 && oldPublicContentView.getPackage() != null
2037 && oldPublicContentView.getPackage().equals(publicContentView.getPackage())
2038 && oldPublicContentView.getLayoutId() == publicContentView.getLayoutId());
2039 return contentsUnchanged && bigContentsUnchanged && headsUpContentsUnchanged
2043 private void updateNotificationViews(Entry entry, StatusBarNotification notification) {
2044 final RemoteViews contentView = notification.getNotification().contentView;
2045 final RemoteViews bigContentView = notification.getNotification().bigContentView;
2046 final RemoteViews headsUpContentView = notification.getNotification().headsUpContentView;
2047 final Notification publicVersion = notification.getNotification().publicVersion;
2048 final RemoteViews publicContentView = publicVersion != null ? publicVersion.contentView
2051 // Reapply the RemoteViews
2052 contentView.reapply(mContext, entry.getContentView(), mOnClickHandler);
2053 if (bigContentView != null && entry.getExpandedContentView() != null) {
2054 bigContentView.reapply(notification.getPackageContext(mContext),
2055 entry.getExpandedContentView(),
2058 View headsUpChild = entry.getHeadsUpContentView();
2059 if (headsUpContentView != null && headsUpChild != null) {
2060 headsUpContentView.reapply(notification.getPackageContext(mContext),
2061 headsUpChild, mOnClickHandler);
2063 if (publicContentView != null && entry.getPublicContentView() != null) {
2064 publicContentView.reapply(notification.getPackageContext(mContext),
2065 entry.getPublicContentView(), mOnClickHandler);
2067 // update the contentIntent
2068 mNotificationClicker.register(entry.row, notification);
2070 entry.row.setStatusBarNotification(notification);
2071 entry.row.notifyContentUpdated();
2072 entry.row.resetHeight();
2075 protected void notifyHeadsUpScreenOff() {
2076 maybeEscalateHeadsUp();
2079 private boolean alertAgain(Entry oldEntry, Notification newNotification) {
2080 return oldEntry == null || !oldEntry.hasInterrupted()
2081 || (newNotification.flags & Notification.FLAG_ONLY_ALERT_ONCE) == 0;
2084 protected boolean shouldInterrupt(Entry entry) {
2085 return shouldInterrupt(entry, entry.notification);
2088 protected boolean shouldInterrupt(Entry entry, StatusBarNotification sbn) {
2089 if (mNotificationData.shouldFilterOut(sbn)) {
2091 Log.d(TAG, "Skipping HUN check for " + sbn.getKey() + " since it's filtered out.");
2096 if (isSnoozedPackage(sbn)) {
2100 Notification notification = sbn.getNotification();
2101 // some predicates to make the boolean logic legible
2102 boolean isNoisy = (notification.defaults & Notification.DEFAULT_SOUND) != 0
2103 || (notification.defaults & Notification.DEFAULT_VIBRATE) != 0
2104 || notification.sound != null
2105 || notification.vibrate != null;
2106 boolean isHighPriority = sbn.getScore() >= INTERRUPTION_THRESHOLD;
2107 boolean isFullscreen = notification.fullScreenIntent != null;
2108 boolean hasTicker = mHeadsUpTicker && !TextUtils.isEmpty(notification.tickerText);
2109 boolean isAllowed = notification.extras.getInt(Notification.EXTRA_AS_HEADS_UP,
2110 Notification.HEADS_UP_ALLOWED) != Notification.HEADS_UP_NEVER;
2111 boolean accessibilityForcesLaunch = isFullscreen
2112 && mAccessibilityManager.isTouchExplorationEnabled();
2113 boolean justLaunchedFullScreenIntent = entry.hasJustLaunchedFullScreenIntent();
2115 boolean interrupt = (isFullscreen || (isHighPriority && (isNoisy || hasTicker)))
2117 && !accessibilityForcesLaunch
2118 && !justLaunchedFullScreenIntent
2119 && mPowerManager.isScreenOn()
2120 && (!mStatusBarKeyguardViewManager.isShowing()
2121 || mStatusBarKeyguardViewManager.isOccluded())
2122 && !mStatusBarKeyguardViewManager.isInputRestricted();
2124 interrupt = interrupt && !mDreamManager.isDreaming();
2125 } catch (RemoteException e) {
2126 Log.d(TAG, "failed to query dream manager", e);
2128 if (DEBUG) Log.d(TAG, "interrupt: " + interrupt);
2132 protected abstract boolean isSnoozedPackage(StatusBarNotification sbn);
2134 public void setInteracting(int barWindow, boolean interacting) {
2135 // hook for subclasses
2138 public void setBouncerShowing(boolean bouncerShowing) {
2139 mBouncerShowing = bouncerShowing;
2143 * @return Whether the security bouncer from Keyguard is showing.
2145 public boolean isBouncerShowing() {
2146 return mBouncerShowing;
2149 public void destroy() {
2150 mContext.unregisterReceiver(mBroadcastReceiver);
2152 mNotificationListener.unregisterAsSystemService();
2153 } catch (RemoteException e) {
2159 * @return a PackageManger for userId or if userId is < 0 (USER_ALL etc) then
2160 * return PackageManager for mContext
2162 protected PackageManager getPackageManagerForUser(int userId) {
2163 Context contextForUser = mContext;
2164 // UserHandle defines special userId as negative values, e.g. USER_ALL
2167 // Create a context for the correct user so if a package isn't installed
2168 // for user 0 we can still load information about the package.
2170 mContext.createPackageContextAsUser(mContext.getPackageName(),
2171 Context.CONTEXT_RESTRICTED,
2172 new UserHandle(userId));
2173 } catch (NameNotFoundException e) {
2174 // Shouldn't fail to find the package name for system ui.
2177 return contextForUser.getPackageManager();
2181 public void logNotificationExpansion(String key, boolean userAction, boolean expanded) {
2183 mBarService.onNotificationExpansionChanged(key, userAction, expanded);
2184 } catch (RemoteException e) {
2189 public boolean isKeyguardSecure() {
2190 if (mStatusBarKeyguardViewManager == null) {
2191 // startKeyguard() hasn't been called yet, so we don't know.
2192 // Make sure anything that needs to know isKeyguardSecure() checks and re-checks this
2193 // value onVisibilityChanged().
2194 Slog.w(TAG, "isKeyguardSecure() called before startKeyguard(), returning false",
2198 return mStatusBarKeyguardViewManager.isSecure();
2202 public void showAssistDisclosure() {
2203 if (mAssistManager != null) {
2204 mAssistManager.showDisclosure();
2209 public void startAssist(Bundle args) {
2210 if (mAssistManager != null) {
2211 mAssistManager.startAssist(args);