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.app.ActivityManager;
22 import android.app.ActivityManager.StackId;
23 import android.app.ActivityManagerNative;
24 import android.app.ActivityOptions;
25 import android.app.KeyguardManager;
26 import android.app.Notification;
27 import android.app.NotificationManager;
28 import android.app.PendingIntent;
29 import android.app.RemoteInput;
30 import android.app.TaskStackBuilder;
31 import android.app.admin.DevicePolicyManager;
32 import android.content.BroadcastReceiver;
33 import android.content.ComponentName;
34 import android.content.Context;
35 import android.content.Intent;
36 import android.content.IntentFilter;
37 import android.content.IntentSender;
38 import android.content.pm.ApplicationInfo;
39 import android.content.pm.PackageManager;
40 import android.content.pm.PackageManager.NameNotFoundException;
41 import android.content.pm.UserInfo;
42 import android.content.res.Configuration;
43 import android.content.res.Resources;
44 import android.database.ContentObserver;
45 import android.graphics.Rect;
46 import android.graphics.drawable.Drawable;
47 import android.graphics.drawable.Icon;
48 import android.os.AsyncTask;
49 import android.os.Build;
50 import android.os.Bundle;
51 import android.os.Handler;
52 import android.os.IBinder;
53 import android.os.Message;
54 import android.os.PowerManager;
55 import android.os.RemoteException;
56 import android.os.ServiceManager;
57 import android.os.SystemProperties;
58 import android.os.UserHandle;
59 import android.os.UserManager;
60 import android.provider.Settings;
61 import android.service.dreams.DreamService;
62 import android.service.dreams.IDreamManager;
63 import android.service.notification.NotificationListenerService;
64 import android.service.notification.NotificationListenerService.RankingMap;
65 import android.service.notification.StatusBarNotification;
66 import android.service.vr.IVrManager;
67 import android.service.vr.IVrStateCallbacks;
68 import android.text.TextUtils;
69 import android.util.ArraySet;
70 import android.util.Log;
71 import android.util.Slog;
72 import android.util.SparseArray;
73 import android.util.SparseBooleanArray;
74 import android.view.Display;
75 import android.view.IWindowManager;
76 import android.view.LayoutInflater;
77 import android.view.MotionEvent;
78 import android.view.View;
79 import android.view.ViewAnimationUtils;
80 import android.view.ViewGroup;
81 import android.view.ViewParent;
82 import android.view.WindowManager;
83 import android.view.WindowManagerGlobal;
84 import android.view.accessibility.AccessibilityManager;
85 import android.widget.ImageView;
86 import android.widget.RemoteViews;
87 import android.widget.TextView;
88 import android.widget.Toast;
90 import com.android.internal.logging.MetricsLogger;
91 import com.android.internal.logging.MetricsProto.MetricsEvent;
92 import com.android.internal.statusbar.IStatusBarService;
93 import com.android.internal.statusbar.StatusBarIcon;
94 import com.android.internal.widget.LockPatternUtils;
95 import com.android.keyguard.KeyguardHostView.OnDismissAction;
96 import com.android.keyguard.KeyguardUpdateMonitor;
97 import com.android.systemui.DejankUtils;
98 import com.android.systemui.Interpolators;
99 import com.android.systemui.R;
100 import com.android.systemui.RecentsComponent;
101 import com.android.systemui.SwipeHelper;
102 import com.android.systemui.SystemUI;
103 import com.android.systemui.assist.AssistManager;
104 import com.android.systemui.recents.Recents;
105 import com.android.systemui.statusbar.NotificationData.Entry;
106 import com.android.systemui.statusbar.NotificationGuts.OnGutsClosedListener;
107 import com.android.systemui.statusbar.phone.NavigationBarView;
108 import com.android.systemui.statusbar.phone.NotificationGroupManager;
109 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
110 import com.android.systemui.statusbar.policy.HeadsUpManager;
111 import com.android.systemui.statusbar.policy.PreviewInflater;
112 import com.android.systemui.statusbar.policy.RemoteInputView;
113 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
114 import com.android.systemui.statusbar.stack.StackStateAnimator;
116 import java.util.ArrayList;
117 import java.util.List;
118 import java.util.Locale;
120 import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_HIGH;
122 public abstract class BaseStatusBar extends SystemUI implements
123 CommandQueue.Callbacks, ActivatableNotificationView.OnActivatedListener,
124 ExpandableNotificationRow.ExpansionLogger, NotificationData.Environment,
125 ExpandableNotificationRow.OnExpandClickListener,
126 OnGutsClosedListener {
127 public static final String TAG = "StatusBar";
128 public static final boolean DEBUG = false;
129 public static final boolean MULTIUSER_DEBUG = false;
131 public static final boolean ENABLE_REMOTE_INPUT =
132 SystemProperties.getBoolean("debug.enable_remote_input", true);
133 public static final boolean ENABLE_CHILD_NOTIFICATIONS
134 = SystemProperties.getBoolean("debug.child_notifs", true);
135 public static final boolean FORCE_REMOTE_INPUT_HISTORY =
136 SystemProperties.getBoolean("debug.force_remoteinput_history", false);
137 private static boolean ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT = false;
139 protected static final int MSG_SHOW_RECENT_APPS = 1019;
140 protected static final int MSG_HIDE_RECENT_APPS = 1020;
141 protected static final int MSG_TOGGLE_RECENTS_APPS = 1021;
142 protected static final int MSG_PRELOAD_RECENT_APPS = 1022;
143 protected static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 1023;
144 protected static final int MSG_SHOW_NEXT_AFFILIATED_TASK = 1024;
145 protected static final int MSG_SHOW_PREV_AFFILIATED_TASK = 1025;
146 protected static final int MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU = 1026;
147 protected static final int MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU = 1027;
149 protected static final boolean ENABLE_HEADS_UP = true;
150 protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
152 private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
154 // Should match the values in PhoneWindowManager
155 public static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
156 public static final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
158 private static final String BANNER_ACTION_CANCEL =
159 "com.android.systemui.statusbar.banner_action_cancel";
160 private static final String BANNER_ACTION_SETUP =
161 "com.android.systemui.statusbar.banner_action_setup";
162 private static final String WORK_CHALLENGE_UNLOCKED_NOTIFICATION_ACTION
163 = "com.android.systemui.statusbar.work_challenge_unlocked_notification_action";
165 protected CommandQueue mCommandQueue;
166 protected IStatusBarService mBarService;
167 protected H mHandler = createHandler();
170 protected NotificationData mNotificationData;
171 protected NotificationStackScrollLayout mStackScroller;
173 protected NotificationGroupManager mGroupManager = new NotificationGroupManager();
175 protected RemoteInputController mRemoteInputController;
177 // for heads up notifications
178 protected HeadsUpManager mHeadsUpManager;
180 protected int mCurrentUserId = 0;
181 final protected SparseArray<UserInfo> mCurrentProfiles = new SparseArray<UserInfo>();
183 protected int mLayoutDirection = -1; // invalid
184 protected AccessibilityManager mAccessibilityManager;
186 // on-screen navigation buttons
187 protected NavigationBarView mNavigationBarView = null;
189 protected boolean mDeviceInteractive;
191 protected boolean mVisible;
192 protected ArraySet<Entry> mHeadsUpEntriesToRemoveOnSwitch = new ArraySet<>();
193 protected ArraySet<Entry> mRemoteInputEntriesToRemoveOnCollapse = new ArraySet<>();
196 * Notifications with keys in this set are not actually around anymore. We kept them around
197 * when they were canceled in response to a remote input interaction. This allows us to show
198 * what you replied and allows you to continue typing into it.
200 protected ArraySet<String> mKeysKeptForRemoteInput = new ArraySet<>();
202 // mScreenOnFromKeyguard && mVisible.
203 private boolean mVisibleToUser;
205 private Locale mLocale;
206 private float mFontScale;
208 protected boolean mUseHeadsUp = false;
209 protected boolean mHeadsUpTicker = false;
210 protected boolean mDisableNotificationAlerts = false;
212 protected DevicePolicyManager mDevicePolicyManager;
213 protected IDreamManager mDreamManager;
214 protected PowerManager mPowerManager;
215 protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
217 // public mode, private notifications, etc
218 private boolean mLockscreenPublicMode = false;
219 private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray();
220 private final SparseBooleanArray mUsersAllowingNotifications = new SparseBooleanArray();
222 private UserManager mUserManager;
223 private int mDensity;
225 private KeyguardManager mKeyguardManager;
226 private LockPatternUtils mLockPatternUtils;
228 // UI-specific methods
231 * Create all windows necessary for the status bar (including navigation, overlay panels, etc)
232 * and add them to the window manager.
234 protected abstract void createAndAddWindows();
236 protected WindowManager mWindowManager;
237 protected IWindowManager mWindowManagerService;
239 protected abstract void refreshLayout(int layoutDirection);
241 protected Display mDisplay;
243 private boolean mDeviceProvisioned = false;
245 protected RecentsComponent mRecents;
247 protected int mZenMode;
249 // which notification is currently being longpress-examined by the user
250 private NotificationGuts mNotificationGutsExposed;
252 private KeyboardShortcuts mKeyboardShortcuts;
255 * The {@link StatusBarState} of the status bar.
257 protected int mState;
258 protected boolean mBouncerShowing;
259 protected boolean mShowLockscreenNotifications;
260 protected boolean mAllowLockscreenRemoteInput;
262 protected NotificationOverflowContainer mKeyguardIconOverflowContainer;
263 protected DismissView mDismissView;
264 protected EmptyShadeView mEmptyShadeView;
266 private NotificationClicker mNotificationClicker = new NotificationClicker();
268 protected AssistManager mAssistManager;
270 protected boolean mVrMode;
272 @Override // NotificationData.Environment
273 public boolean isDeviceProvisioned() {
274 return mDeviceProvisioned;
277 private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
279 public void onVrStateChanged(boolean enabled) {
284 public boolean isDeviceInVrMode() {
288 protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
290 public void onChange(boolean selfChange) {
291 final boolean provisioned = 0 != Settings.Global.getInt(
292 mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0);
293 if (provisioned != mDeviceProvisioned) {
294 mDeviceProvisioned = provisioned;
295 updateNotifications();
297 final int mode = Settings.Global.getInt(mContext.getContentResolver(),
298 Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
301 updateLockscreenNotificationSetting();
305 private final ContentObserver mLockscreenSettingsObserver = new ContentObserver(mHandler) {
307 public void onChange(boolean selfChange) {
308 // We don't know which user changed LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS or
309 // LOCK_SCREEN_SHOW_NOTIFICATIONS, so we just dump our cache ...
310 mUsersAllowingPrivateNotifications.clear();
311 mUsersAllowingNotifications.clear();
312 // ... and refresh all the notifications
313 updateNotifications();
317 private RemoteViews.OnClickHandler mOnClickHandler = new RemoteViews.OnClickHandler() {
319 public boolean onClickHandler(
320 final View view, final PendingIntent pendingIntent, final Intent fillInIntent) {
321 if (handleRemoteInput(view, pendingIntent, fillInIntent)) {
326 Log.v(TAG, "Notification click handler invoked for intent: " + pendingIntent);
328 logActionClick(view);
329 // The intent we are sending is for the application, which
330 // won't have permission to immediately start an activity after
331 // the user switches to home. We know it is safe to do at this
332 // point, so make sure new activity switches are now allowed.
334 ActivityManagerNative.getDefault().resumeAppSwitches();
335 } catch (RemoteException e) {
337 final boolean isActivity = pendingIntent.isActivity();
339 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
340 final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity(
341 mContext, pendingIntent.getIntent(), mCurrentUserId);
342 dismissKeyguardThenExecute(new OnDismissAction() {
344 public boolean onDismiss() {
345 if (keyguardShowing && !afterKeyguardGone) {
347 ActivityManagerNative.getDefault()
348 .keyguardWaitingForActivityDrawn();
349 ActivityManagerNative.getDefault().resumeAppSwitches();
350 } catch (RemoteException e) {
354 boolean handled = superOnClickHandler(view, pendingIntent, fillInIntent);
355 overrideActivityPendingAppTransition(keyguardShowing && !afterKeyguardGone);
357 // close the shade if it was open
359 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
361 visibilityChanged(false);
362 mAssistManager.hideAssist();
365 // Wait for activity start.
368 }, afterKeyguardGone);
371 return superOnClickHandler(view, pendingIntent, fillInIntent);
375 private void logActionClick(View view) {
376 ViewParent parent = view.getParent();
377 String key = getNotificationKeyForParent(parent);
379 Log.w(TAG, "Couldn't determine notification for click.");
383 // If this is a default template, determine the index of the button.
384 if (view.getId() == com.android.internal.R.id.action0 &&
385 parent != null && parent instanceof ViewGroup) {
386 ViewGroup actionGroup = (ViewGroup) parent;
387 index = actionGroup.indexOfChild(view);
390 mBarService.onNotificationActionClick(key, index);
391 } catch (RemoteException e) {
396 private String getNotificationKeyForParent(ViewParent parent) {
397 while (parent != null) {
398 if (parent instanceof ExpandableNotificationRow) {
399 return ((ExpandableNotificationRow) parent).getStatusBarNotification().getKey();
401 parent = parent.getParent();
406 private boolean superOnClickHandler(View view, PendingIntent pendingIntent,
407 Intent fillInIntent) {
408 return super.onClickHandler(view, pendingIntent, fillInIntent,
409 StackId.FULLSCREEN_WORKSPACE_STACK_ID);
412 private boolean handleRemoteInput(View view, PendingIntent pendingIntent, Intent fillInIntent) {
413 Object tag = view.getTag(com.android.internal.R.id.remote_input_tag);
414 RemoteInput[] inputs = null;
415 if (tag instanceof RemoteInput[]) {
416 inputs = (RemoteInput[]) tag;
419 if (inputs == null) {
423 RemoteInput input = null;
425 for (RemoteInput i : inputs) {
426 if (i.getAllowFreeFormInput()) {
435 ViewParent p = view.getParent();
436 RemoteInputView riv = null;
438 if (p instanceof View) {
440 if (pv.isRootNamespace()) {
441 riv = (RemoteInputView) pv.findViewWithTag(RemoteInputView.VIEW_TAG);
447 ExpandableNotificationRow row = null;
449 if (p instanceof ExpandableNotificationRow) {
450 row = (ExpandableNotificationRow) p;
456 if (riv == null || row == null) {
460 row.setUserExpanded(true);
462 if (!mAllowLockscreenRemoteInput) {
463 if (isLockscreenPublicMode()) {
464 onLockedRemoteInput(row, view);
467 final int userId = pendingIntent.getCreatorUserHandle().getIdentifier();
468 if (mUserManager.getUserInfo(userId).isManagedProfile()
469 && mKeyguardManager.isDeviceLocked(userId)) {
470 onLockedWorkRemoteInput(userId, row, view);
475 riv.setVisibility(View.VISIBLE);
476 int cx = view.getLeft() + view.getWidth() / 2;
477 int cy = view.getTop() + view.getHeight() / 2;
478 int w = riv.getWidth();
479 int h = riv.getHeight();
481 Math.max(cx + cy, cx + (h - cy)),
482 Math.max((w - cx) + cy, (w - cx) + (h - cy)));
483 ViewAnimationUtils.createCircularReveal(riv, cx, cy, 0, r)
486 riv.setPendingIntent(pendingIntent);
487 riv.setRemoteInput(inputs, input);
495 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
497 public void onReceive(Context context, Intent intent) {
498 String action = intent.getAction();
499 if (Intent.ACTION_USER_SWITCHED.equals(action)) {
500 mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
501 updateCurrentProfilesCache();
502 if (true) Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
504 updateLockscreenNotificationSetting();
506 userSwitched(mCurrentUserId);
507 } else if (Intent.ACTION_USER_ADDED.equals(action)) {
508 updateCurrentProfilesCache();
509 } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
510 List<ActivityManager.RecentTaskInfo> recentTask = null;
512 recentTask = ActivityManagerNative.getDefault().getRecentTasks(1,
513 ActivityManager.RECENT_WITH_EXCLUDED
514 | ActivityManager.RECENT_INCLUDE_PROFILES,
516 } catch (RemoteException e) {
517 // Abandon hope activity manager not running.
519 if (recentTask != null && recentTask.size() > 0) {
520 UserInfo user = mUserManager.getUserInfo(recentTask.get(0).userId);
521 if (user != null && user.isManagedProfile()) {
522 Toast toast = Toast.makeText(mContext,
523 R.string.managed_profile_foreground_toast,
525 TextView text = (TextView) toast.getView().findViewById(
526 android.R.id.message);
527 text.setCompoundDrawablesRelativeWithIntrinsicBounds(
528 R.drawable.stat_sys_managed_profile_status, 0, 0, 0);
529 int paddingPx = mContext.getResources().getDimensionPixelSize(
530 R.dimen.managed_profile_toast_padding);
531 text.setCompoundDrawablePadding(paddingPx);
535 } else if (BANNER_ACTION_CANCEL.equals(action) || BANNER_ACTION_SETUP.equals(action)) {
536 NotificationManager noMan = (NotificationManager)
537 mContext.getSystemService(Context.NOTIFICATION_SERVICE);
538 noMan.cancel(R.id.notification_hidden);
540 Settings.Secure.putInt(mContext.getContentResolver(),
541 Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
542 if (BANNER_ACTION_SETUP.equals(action)) {
543 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
545 mContext.startActivity(new Intent(Settings.ACTION_APP_NOTIFICATION_REDACTION)
546 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
550 } else if (WORK_CHALLENGE_UNLOCKED_NOTIFICATION_ACTION.equals(action)) {
551 final IntentSender intentSender = intent.getParcelableExtra(Intent.EXTRA_INTENT);
552 final String notificationKey = intent.getStringExtra(Intent.EXTRA_INDEX);
553 if (intentSender != null) {
555 mContext.startIntentSender(intentSender, null, 0, 0, 0);
556 } catch (IntentSender.SendIntentException e) {
560 if (notificationKey != null) {
562 mBarService.onNotificationClick(notificationKey);
563 } catch (RemoteException e) {
567 onWorkChallengeUnlocked();
572 private final BroadcastReceiver mAllUsersReceiver = new BroadcastReceiver() {
574 public void onReceive(Context context, Intent intent) {
575 String action = intent.getAction();
576 if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action) &&
577 isCurrentProfile(getSendingUserId())) {
578 mUsersAllowingPrivateNotifications.clear();
579 updateLockscreenNotificationSetting();
580 updateNotifications();
585 private final NotificationListenerService mNotificationListener =
586 new NotificationListenerService() {
588 public void onListenerConnected() {
589 if (DEBUG) Log.d(TAG, "onListenerConnected");
590 final StatusBarNotification[] notifications = getActiveNotifications();
591 final RankingMap currentRanking = getCurrentRanking();
592 mHandler.post(new Runnable() {
595 for (StatusBarNotification sbn : notifications) {
596 addNotification(sbn, currentRanking, null /* oldEntry */);
603 public void onNotificationPosted(final StatusBarNotification sbn,
604 final RankingMap rankingMap) {
605 if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
607 mHandler.post(new Runnable() {
610 processForRemoteInput(sbn.getNotification());
611 String key = sbn.getKey();
612 mKeysKeptForRemoteInput.remove(key);
613 boolean isUpdate = mNotificationData.get(key) != null;
614 // In case we don't allow child notifications, we ignore children of
615 // notifications that have a summary, since we're not going to show them
616 // anyway. This is true also when the summary is canceled,
617 // because children are automatically canceled by NoMan in that case.
618 if (!ENABLE_CHILD_NOTIFICATIONS
619 && mGroupManager.isChildInGroupWithSummary(sbn)) {
621 Log.d(TAG, "Ignoring group child due to existing summary: " + sbn);
624 // Remove existing notification to avoid stale data.
626 removeNotification(key, rankingMap);
628 mNotificationData.updateRanking(rankingMap);
633 updateNotification(sbn, rankingMap);
635 addNotification(sbn, rankingMap, null /* oldEntry */);
643 public void onNotificationRemoved(StatusBarNotification sbn,
644 final RankingMap rankingMap) {
645 if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn);
647 final String key = sbn.getKey();
648 mHandler.post(new Runnable() {
651 removeNotification(key, rankingMap);
658 public void onNotificationRankingUpdate(final RankingMap rankingMap) {
659 if (DEBUG) Log.d(TAG, "onRankingUpdate");
660 if (rankingMap != null) {
661 mHandler.post(new Runnable() {
664 updateNotificationRanking(rankingMap);
671 private void updateCurrentProfilesCache() {
672 synchronized (mCurrentProfiles) {
673 mCurrentProfiles.clear();
674 if (mUserManager != null) {
675 for (UserInfo user : mUserManager.getProfiles(mCurrentUserId)) {
676 mCurrentProfiles.put(user.id, user);
682 public void start() {
683 mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
684 mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
685 mDisplay = mWindowManager.getDefaultDisplay();
686 mDevicePolicyManager = (DevicePolicyManager)mContext.getSystemService(
687 Context.DEVICE_POLICY_SERVICE);
689 mNotificationData = new NotificationData(this);
691 mAccessibilityManager = (AccessibilityManager)
692 mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
694 mDreamManager = IDreamManager.Stub.asInterface(
695 ServiceManager.checkService(DreamService.DREAM_SERVICE));
696 mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
698 mContext.getContentResolver().registerContentObserver(
699 Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), true,
701 mContext.getContentResolver().registerContentObserver(
702 Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false,
704 mContext.getContentResolver().registerContentObserver(
705 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false,
707 UserHandle.USER_ALL);
708 if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) {
709 mContext.getContentResolver().registerContentObserver(
710 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT),
713 UserHandle.USER_ALL);
716 mContext.getContentResolver().registerContentObserver(
717 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
719 mLockscreenSettingsObserver,
720 UserHandle.USER_ALL);
722 mBarService = IStatusBarService.Stub.asInterface(
723 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
725 mRecents = getComponent(Recents.class);
727 final Configuration currentConfig = mContext.getResources().getConfiguration();
728 mLocale = currentConfig.locale;
729 mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale);
730 mFontScale = currentConfig.fontScale;
731 mDensity = currentConfig.densityDpi;
733 mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
734 mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
735 mLockPatternUtils = new LockPatternUtils(mContext);
737 // Connect in to the status bar manager service
738 mCommandQueue = new CommandQueue(this);
740 int[] switches = new int[9];
741 ArrayList<IBinder> binders = new ArrayList<IBinder>();
742 ArrayList<String> iconSlots = new ArrayList<>();
743 ArrayList<StatusBarIcon> icons = new ArrayList<>();
744 Rect fullscreenStackBounds = new Rect();
745 Rect dockedStackBounds = new Rect();
747 mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders,
748 fullscreenStackBounds, dockedStackBounds);
749 } catch (RemoteException ex) {
750 // If the system process isn't there we're doomed anyway.
753 createAndAddWindows();
755 mSettingsObserver.onChange(false); // set up
756 disable(switches[0], switches[6], false /* animate */);
757 setSystemUiVisibility(switches[1], switches[7], switches[8], 0xffffffff,
758 fullscreenStackBounds, dockedStackBounds);
759 topAppWindowChanged(switches[2] != 0);
760 // StatusBarManagerService has a back up of IME token and it's restored here.
761 setImeWindowStatus(binders.get(0), switches[3], switches[4], switches[5] != 0);
763 // Set up the initial icon state
764 int N = iconSlots.size();
766 for (int i=0; i < N; i++) {
767 setIcon(iconSlots.get(i), icons.get(i));
770 // Set up the initial notification state.
772 mNotificationListener.registerAsSystemService(mContext,
773 new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),
774 UserHandle.USER_ALL);
775 } catch (RemoteException e) {
776 Log.e(TAG, "Unable to register notification listener", e);
781 Log.d(TAG, String.format(
782 "init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x",
791 mCurrentUserId = ActivityManager.getCurrentUser();
792 setHeadsUpUser(mCurrentUserId);
794 IntentFilter filter = new IntentFilter();
795 filter.addAction(Intent.ACTION_USER_SWITCHED);
796 filter.addAction(Intent.ACTION_USER_ADDED);
797 filter.addAction(Intent.ACTION_USER_PRESENT);
798 mContext.registerReceiver(mBroadcastReceiver, filter);
800 IntentFilter internalFilter = new IntentFilter();
801 internalFilter.addAction(WORK_CHALLENGE_UNLOCKED_NOTIFICATION_ACTION);
802 internalFilter.addAction(BANNER_ACTION_CANCEL);
803 internalFilter.addAction(BANNER_ACTION_SETUP);
804 mContext.registerReceiver(mBroadcastReceiver, internalFilter, PERMISSION_SELF, null);
806 IntentFilter allUsersFilter = new IntentFilter();
807 allUsersFilter.addAction(
808 DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
809 mContext.registerReceiverAsUser(mAllUsersReceiver, UserHandle.ALL, allUsersFilter,
811 updateCurrentProfilesCache();
813 IVrManager vrManager = IVrManager.Stub.asInterface(ServiceManager.getService("vrmanager"));
815 vrManager.registerListener(mVrStateCallbacks);
816 } catch (RemoteException e) {
817 Slog.e(TAG, "Failed to register VR mode state listener: " + e);
822 protected void notifyUserAboutHiddenNotifications() {
823 if (0 != Settings.Secure.getInt(mContext.getContentResolver(),
824 Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 1)) {
825 Log.d(TAG, "user hasn't seen notification about hidden notifications");
826 if (!mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())) {
827 Log.d(TAG, "insecure lockscreen, skipping notification");
828 Settings.Secure.putInt(mContext.getContentResolver(),
829 Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
832 Log.d(TAG, "disabling lockecreen notifications and alerting the user");
833 // disable lockscreen notifications until user acts on the banner.
834 Settings.Secure.putInt(mContext.getContentResolver(),
835 Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
836 Settings.Secure.putInt(mContext.getContentResolver(),
837 Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0);
839 final String packageName = mContext.getPackageName();
840 PendingIntent cancelIntent = PendingIntent.getBroadcast(mContext, 0,
841 new Intent(BANNER_ACTION_CANCEL).setPackage(packageName),
842 PendingIntent.FLAG_CANCEL_CURRENT);
843 PendingIntent setupIntent = PendingIntent.getBroadcast(mContext, 0,
844 new Intent(BANNER_ACTION_SETUP).setPackage(packageName),
845 PendingIntent.FLAG_CANCEL_CURRENT);
847 final int colorRes = com.android.internal.R.color.system_notification_accent_color;
848 Notification.Builder note = new Notification.Builder(mContext)
849 .setSmallIcon(R.drawable.ic_android)
850 .setContentTitle(mContext.getString(R.string.hidden_notifications_title))
851 .setContentText(mContext.getString(R.string.hidden_notifications_text))
852 .setPriority(Notification.PRIORITY_HIGH)
854 .setColor(mContext.getColor(colorRes))
855 .setContentIntent(setupIntent)
856 .addAction(R.drawable.ic_close,
857 mContext.getString(R.string.hidden_notifications_cancel),
859 .addAction(R.drawable.ic_settings,
860 mContext.getString(R.string.hidden_notifications_setup),
862 overrideNotificationAppName(mContext, note);
864 NotificationManager noMan =
865 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
866 noMan.notify(R.id.notification_hidden, note.build());
870 public void userSwitched(int newUserId) {
871 setHeadsUpUser(newUserId);
874 protected abstract void setHeadsUpUser(int newUserId);
876 @Override // NotificationData.Environment
877 public boolean isNotificationForCurrentProfiles(StatusBarNotification n) {
878 final int thisUserId = mCurrentUserId;
879 final int notificationUserId = n.getUserId();
880 if (DEBUG && MULTIUSER_DEBUG) {
881 Log.v(TAG, String.format("%s: current userid: %d, notification userid: %d",
882 n, thisUserId, notificationUserId));
884 return isCurrentProfile(notificationUserId);
887 protected void setNotificationShown(StatusBarNotification n) {
888 setNotificationsShown(new String[]{n.getKey()});
891 protected void setNotificationsShown(String[] keys) {
893 mNotificationListener.setNotificationsShown(keys);
894 } catch (RuntimeException e) {
895 Log.d(TAG, "failed setNotificationsShown: ", e);
899 protected boolean isCurrentProfile(int userId) {
900 synchronized (mCurrentProfiles) {
901 return userId == UserHandle.USER_ALL || mCurrentProfiles.get(userId) != null;
906 public String getCurrentMediaNotificationKey() {
911 public NotificationGroupManager getGroupManager() {
912 return mGroupManager;
916 * Takes the necessary steps to prepare the status bar for starting an activity, then starts it.
917 * @param action A dismiss action that is called if it's safe to start the activity.
918 * @param afterKeyguardGone Whether the action should be executed after the Keyguard is gone.
920 protected void dismissKeyguardThenExecute(OnDismissAction action, boolean afterKeyguardGone) {
925 protected void onConfigurationChanged(Configuration newConfig) {
926 final Locale locale = mContext.getResources().getConfiguration().locale;
927 final int ld = TextUtils.getLayoutDirectionFromLocale(locale);
928 final float fontScale = newConfig.fontScale;
929 final int density = newConfig.densityDpi;
930 if (density != mDensity || mFontScale != fontScale) {
931 onDensityOrFontScaleChanged();
933 mFontScale = fontScale;
935 if (! locale.equals(mLocale) || ld != mLayoutDirection) {
937 Log.v(TAG, String.format(
938 "config changed locale/LD: %s (%d) -> %s (%d)", mLocale, mLayoutDirection,
942 mLayoutDirection = ld;
947 protected void onDensityOrFontScaleChanged() {
948 ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
949 for (int i = 0; i < activeNotifications.size(); i++) {
950 Entry entry = activeNotifications.get(i);
951 boolean exposedGuts = entry.row.getGuts() == mNotificationGutsExposed;
952 entry.row.reInflateViews();
954 mNotificationGutsExposed = entry.row.getGuts();
957 entry.cacheContentViews(mContext, null /* updatedNotification */);
958 inflateViews(entry, mStackScroller);
962 protected View bindVetoButtonClickListener(View row, final StatusBarNotification n) {
963 View vetoButton = row.findViewById(R.id.veto);
964 vetoButton.setOnClickListener(new View.OnClickListener() {
965 public void onClick(View v) {
966 // Accessibility feedback
967 v.announceForAccessibility(
968 mContext.getString(R.string.accessibility_notification_dismissed));
969 performRemoveNotification(n, false /* removeView */);
972 vetoButton.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
976 protected void performRemoveNotification(StatusBarNotification n, boolean removeView) {
977 final String pkg = n.getPackageName();
978 final String tag = n.getTag();
979 final int id = n.getId();
980 final int userId = n.getUserId();
982 mBarService.onNotificationClear(pkg, tag, id, userId);
983 if (FORCE_REMOTE_INPUT_HISTORY
984 && mKeysKeptForRemoteInput.contains(n.getKey())) {
985 mKeysKeptForRemoteInput.remove(n.getKey());
988 if (mRemoteInputEntriesToRemoveOnCollapse.remove(mNotificationData.get(n.getKey()))) {
992 removeNotification(n.getKey(), null);
995 } catch (RemoteException ex) {
996 // system process is dead if we're here.
1001 protected void applyColorsAndBackgrounds(StatusBarNotification sbn,
1002 NotificationData.Entry entry) {
1004 if (entry.getContentView().getId()
1005 != com.android.internal.R.id.status_bar_latest_event_content) {
1006 // Using custom RemoteViews
1007 if (entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD
1008 && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP) {
1009 entry.row.setShowingLegacyBackground(true);
1010 entry.legacy = true;
1014 if (entry.icon != null) {
1015 entry.icon.setTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
1019 public boolean isMediaNotification(NotificationData.Entry entry) {
1020 // TODO: confirm that there's a valid media key
1021 return entry.getExpandedContentView() != null &&
1022 entry.getExpandedContentView()
1023 .findViewById(com.android.internal.R.id.media_actions) != null;
1026 // The (i) button in the guts that links to the system notification settings for that app
1027 private void startAppNotificationSettingsActivity(String packageName, final int appUid) {
1028 final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
1029 intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
1030 intent.putExtra(Settings.EXTRA_APP_UID, appUid);
1031 startNotificationGutsIntent(intent, appUid);
1034 private void startNotificationGutsIntent(final Intent intent, final int appUid) {
1035 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
1036 dismissKeyguardThenExecute(new OnDismissAction() {
1038 public boolean onDismiss() {
1039 AsyncTask.execute(new Runnable() {
1042 if (keyguardShowing) {
1043 ActivityManagerNative.getDefault()
1044 .keyguardWaitingForActivityDrawn();
1046 TaskStackBuilder.create(mContext)
1047 .addNextIntentWithParentStack(intent)
1048 .startActivities(getActivityOptions(),
1049 new UserHandle(UserHandle.getUserId(appUid)));
1050 overrideActivityPendingAppTransition(keyguardShowing);
1051 } catch (RemoteException e) {
1055 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
1058 }, false /* afterKeyguardGone */);
1061 private void bindGuts(final ExpandableNotificationRow row) {
1063 final StatusBarNotification sbn = row.getStatusBarNotification();
1064 PackageManager pmUser = getPackageManagerForUser(mContext, sbn.getUser().getIdentifier());
1065 row.setTag(sbn.getPackageName());
1066 final NotificationGuts guts = row.getGuts();
1067 guts.setClosedListener(this);
1068 final String pkg = sbn.getPackageName();
1069 String appname = pkg;
1070 Drawable pkgicon = null;
1073 final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
1074 PackageManager.GET_UNINSTALLED_PACKAGES
1075 | PackageManager.GET_DISABLED_COMPONENTS);
1077 appname = String.valueOf(pmUser.getApplicationLabel(info));
1078 pkgicon = pmUser.getApplicationIcon(info);
1081 } catch (NameNotFoundException e) {
1082 // app is gone, just show package name and generic icon
1083 pkgicon = pmUser.getDefaultActivityIcon();
1086 ((ImageView) guts.findViewById(R.id.app_icon)).setImageDrawable(pkgicon);
1087 ((TextView) guts.findViewById(R.id.pkgname)).setText(appname);
1089 final View settingsButton = guts.findViewById(R.id.more_settings);
1091 final int appUidF = appUid;
1092 settingsButton.setOnClickListener(new View.OnClickListener() {
1093 public void onClick(View v) {
1094 MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTE_INFO);
1095 guts.resetFalsingCheck();
1096 startAppNotificationSettingsActivity(pkg, appUidF);
1100 settingsButton.setVisibility(View.GONE);
1103 guts.bindImportance(pmUser, sbn, mNotificationData.getImportance(sbn.getKey()));
1105 guts.findViewById(R.id.done).setOnClickListener(new View.OnClickListener() {
1107 public void onClick(View v) {
1108 // If the user has security enabled, show challenge if the setting is changed.
1109 if (guts.hasImportanceChanged() && isLockscreenPublicMode() &&
1110 (mState == StatusBarState.KEYGUARD
1111 || mState == StatusBarState.SHADE_LOCKED)) {
1112 OnDismissAction dismissAction = new OnDismissAction() {
1114 public boolean onDismiss() {
1115 saveImportanceCloseControls(sbn, row, guts, v);
1119 onLockedNotificationImportanceChange(dismissAction);
1121 saveImportanceCloseControls(sbn, row, guts, v);
1127 private void saveImportanceCloseControls(StatusBarNotification sbn,
1128 ExpandableNotificationRow row, NotificationGuts guts, View done) {
1129 guts.resetFalsingCheck();
1130 guts.saveImportance(sbn);
1132 int[] rowLocation = new int[2];
1133 int[] doneLocation = new int[2];
1134 row.getLocationOnScreen(rowLocation);
1135 done.getLocationOnScreen(doneLocation);
1137 final int centerX = done.getWidth() / 2;
1138 final int centerY = done.getHeight() / 2;
1139 final int x = doneLocation[0] - rowLocation[0] + centerX;
1140 final int y = doneLocation[1] - rowLocation[1] + centerY;
1141 dismissPopups(x, y);
1144 protected SwipeHelper.LongPressListener getNotificationLongClicker() {
1145 return new SwipeHelper.LongPressListener() {
1147 public boolean onLongPress(View v, final int x, final int y) {
1148 if (!(v instanceof ExpandableNotificationRow)) {
1151 if (v.getWindowToken() == null) {
1152 Log.e(TAG, "Trying to show notification guts, but not attached to window");
1156 final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
1159 // Assume we are a status_bar_notification_row
1160 final NotificationGuts guts = row.getGuts();
1162 // This view has no guts. Examples are the more card or the dismiss all view
1167 if (guts.getVisibility() == View.VISIBLE) {
1168 dismissPopups(x, y);
1172 MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTE_CONTROLS);
1174 // ensure that it's laid but not visible until actually laid out
1175 guts.setVisibility(View.INVISIBLE);
1176 // Post to ensure the the guts are properly laid out.
1177 guts.post(new Runnable() {
1179 dismissPopups(-1 /* x */, -1 /* y */, false /* resetGear */,
1180 false /* animate */);
1181 guts.setVisibility(View.VISIBLE);
1182 final double horz = Math.max(guts.getWidth() - x, x);
1183 final double vert = Math.max(guts.getHeight() - y, y);
1184 final float r = (float) Math.hypot(horz, vert);
1186 = ViewAnimationUtils.createCircularReveal(guts, x, y, 0, r);
1187 a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
1188 a.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
1189 a.addListener(new AnimatorListenerAdapter() {
1191 public void onAnimationEnd(Animator animation) {
1192 super.onAnimationEnd(animation);
1193 // Move the notification view back over the gear
1194 row.resetTranslation();
1198 guts.setExposed(true /* exposed */,
1199 mState == StatusBarState.KEYGUARD /* needsFalsingProtection */);
1200 row.closeRemoteInput();
1201 mStackScroller.onHeightChanged(null, true /* needsAnimation */);
1202 mNotificationGutsExposed = guts;
1211 * Returns the exposed NotificationGuts or null if none are exposed.
1213 public NotificationGuts getExposedGuts() {
1214 return mNotificationGutsExposed;
1217 public void dismissPopups() {
1218 dismissPopups(-1 /* x */, -1 /* y */, true /* resetGear */, false /* animate */);
1221 private void dismissPopups(int x, int y) {
1222 dismissPopups(x, y, true /* resetGear */, false /* animate */);
1225 public void dismissPopups(int x, int y, boolean resetGear, boolean animate) {
1226 if (mNotificationGutsExposed != null) {
1227 mNotificationGutsExposed.closeControls(x, y, true /* notify */);
1230 mStackScroller.resetExposedGearView(animate, true /* force */);
1235 public void onGutsClosed(NotificationGuts guts) {
1236 mStackScroller.onHeightChanged(null, true /* needsAnimation */);
1237 mNotificationGutsExposed = null;
1241 public void showRecentApps(boolean triggeredFromAltTab, boolean fromHome) {
1242 int msg = MSG_SHOW_RECENT_APPS;
1243 mHandler.removeMessages(msg);
1244 mHandler.obtainMessage(msg, triggeredFromAltTab ? 1 : 0, fromHome ? 1 : 0).sendToTarget();
1248 public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
1249 int msg = MSG_HIDE_RECENT_APPS;
1250 mHandler.removeMessages(msg);
1251 mHandler.obtainMessage(msg, triggeredFromAltTab ? 1 : 0,
1252 triggeredFromHomeKey ? 1 : 0).sendToTarget();
1256 public void toggleRecentApps() {
1261 public void toggleSplitScreen() {
1262 toggleSplitScreenMode(-1 /* metricsDockAction */, -1 /* metricsUndockAction */);
1266 public void preloadRecentApps() {
1267 int msg = MSG_PRELOAD_RECENT_APPS;
1268 mHandler.removeMessages(msg);
1269 mHandler.sendEmptyMessage(msg);
1273 public void cancelPreloadRecentApps() {
1274 int msg = MSG_CANCEL_PRELOAD_RECENT_APPS;
1275 mHandler.removeMessages(msg);
1276 mHandler.sendEmptyMessage(msg);
1280 public void dismissKeyboardShortcutsMenu() {
1281 int msg = MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU;
1282 mHandler.removeMessages(msg);
1283 mHandler.sendEmptyMessage(msg);
1287 public void toggleKeyboardShortcutsMenu(int deviceId) {
1288 int msg = MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU;
1289 mHandler.removeMessages(msg);
1290 mHandler.obtainMessage(msg, deviceId, 0).sendToTarget();
1293 /** Jumps to the next affiliated task in the group. */
1294 public void showNextAffiliatedTask() {
1295 int msg = MSG_SHOW_NEXT_AFFILIATED_TASK;
1296 mHandler.removeMessages(msg);
1297 mHandler.sendEmptyMessage(msg);
1300 /** Jumps to the previous affiliated task in the group. */
1301 public void showPreviousAffiliatedTask() {
1302 int msg = MSG_SHOW_PREV_AFFILIATED_TASK;
1303 mHandler.removeMessages(msg);
1304 mHandler.sendEmptyMessage(msg);
1307 protected H createHandler() {
1311 protected void sendCloseSystemWindows(String reason) {
1312 if (ActivityManagerNative.isSystemReady()) {
1314 ActivityManagerNative.getDefault().closeSystemDialogs(reason);
1315 } catch (RemoteException e) {
1320 protected abstract View getStatusBarView();
1322 protected View.OnTouchListener mRecentsPreloadOnTouchListener = new View.OnTouchListener() {
1323 // additional optimization when we have software system buttons - start loading the recent
1324 // tasks on touch down
1326 public boolean onTouch(View v, MotionEvent event) {
1327 int action = event.getAction() & MotionEvent.ACTION_MASK;
1328 if (action == MotionEvent.ACTION_DOWN) {
1330 } else if (action == MotionEvent.ACTION_CANCEL) {
1331 cancelPreloadingRecents();
1332 } else if (action == MotionEvent.ACTION_UP) {
1333 if (!v.isPressed()) {
1334 cancelPreloadingRecents();
1343 * Toggle docking the app window
1345 * @param metricsDockAction the action to log when docking is successful, or -1 to not log
1346 * anything on successful docking
1347 * @param metricsUndockAction the action to log when undocking, or -1 to not log anything when
1350 protected abstract void toggleSplitScreenMode(int metricsDockAction, int metricsUndockAction);
1352 /** Proxy for RecentsComponent */
1354 protected void showRecents(boolean triggeredFromAltTab, boolean fromHome) {
1355 if (mRecents != null) {
1356 sendCloseSystemWindows(SYSTEM_DIALOG_REASON_RECENT_APPS);
1357 mRecents.showRecents(triggeredFromAltTab, fromHome);
1361 protected void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
1362 if (mRecents != null) {
1363 mRecents.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
1367 protected void toggleRecents() {
1368 if (mRecents != null) {
1369 mRecents.toggleRecents(mDisplay);
1373 protected void preloadRecents() {
1374 if (mRecents != null) {
1375 mRecents.preloadRecents();
1379 protected void toggleKeyboardShortcuts(int deviceId) {
1380 KeyboardShortcuts.toggle(mContext, deviceId);
1383 protected void dismissKeyboardShortcuts() {
1384 KeyboardShortcuts.dismiss();
1387 protected void cancelPreloadingRecents() {
1388 if (mRecents != null) {
1389 mRecents.cancelPreloadingRecents();
1393 protected void showRecentsNextAffiliatedTask() {
1394 if (mRecents != null) {
1395 mRecents.showNextAffiliatedTask();
1399 protected void showRecentsPreviousAffiliatedTask() {
1400 if (mRecents != null) {
1401 mRecents.showPrevAffiliatedTask();
1406 * If there is an active heads-up notification and it has a fullscreen intent, fire it now.
1408 public abstract void maybeEscalateHeadsUp();
1411 * Save the current "public" (locked and secure) state of the lockscreen.
1413 public void setLockscreenPublicMode(boolean publicMode) {
1414 mLockscreenPublicMode = publicMode;
1417 public boolean isLockscreenPublicMode() {
1418 return mLockscreenPublicMode;
1421 protected void onWorkChallengeUnlocked() {}
1424 * Has the given user chosen to allow notifications to be shown even when the lockscreen is in
1425 * "public" (secure & locked) mode?
1427 public boolean userAllowsNotificationsInPublic(int userHandle) {
1428 if (userHandle == UserHandle.USER_ALL) {
1432 if (mUsersAllowingNotifications.indexOfKey(userHandle) < 0) {
1433 final boolean allowed = 0 != Settings.Secure.getIntForUser(
1434 mContext.getContentResolver(),
1435 Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, userHandle);
1436 mUsersAllowingNotifications.append(userHandle, allowed);
1440 return mUsersAllowingNotifications.get(userHandle);
1444 * Has the given user chosen to allow their private (full) notifications to be shown even
1445 * when the lockscreen is in "public" (secure & locked) mode?
1447 public boolean userAllowsPrivateNotificationsInPublic(int userHandle) {
1448 if (userHandle == UserHandle.USER_ALL) {
1452 if (mUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
1453 final boolean allowedByUser = 0 != Settings.Secure.getIntForUser(
1454 mContext.getContentResolver(),
1455 Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle);
1456 final boolean allowedByDpm = adminAllowsUnredactedNotifications(userHandle);
1457 final boolean allowed = allowedByUser && allowedByDpm;
1458 mUsersAllowingPrivateNotifications.append(userHandle, allowed);
1462 return mUsersAllowingPrivateNotifications.get(userHandle);
1465 private boolean adminAllowsUnredactedNotifications(int userHandle) {
1466 if (userHandle == UserHandle.USER_ALL) {
1469 final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(null /* admin */,
1471 return (dpmFlags & DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS) == 0;
1475 * Returns true if we're on a secure lockscreen and the user wants to hide notification data.
1476 * If so, notifications should be hidden.
1478 @Override // NotificationData.Environment
1479 public boolean shouldHideNotifications(int userid) {
1480 return isLockscreenPublicMode() && !userAllowsNotificationsInPublic(userid);
1484 * Returns true if we're on a secure lockscreen and the user wants to hide notifications via
1485 * package-specific override.
1487 @Override // NotificationDate.Environment
1488 public boolean shouldHideNotifications(String key) {
1489 return isLockscreenPublicMode()
1490 && mNotificationData.getVisibilityOverride(key) == Notification.VISIBILITY_SECRET;
1494 * Returns true if we're on a secure lockscreen.
1496 @Override // NotificationData.Environment
1497 public boolean onSecureLockScreen() {
1498 return isLockscreenPublicMode();
1501 public void onNotificationClear(StatusBarNotification notification) {
1503 mBarService.onNotificationClear(
1504 notification.getPackageName(),
1505 notification.getTag(),
1506 notification.getId(),
1507 notification.getUserId());
1508 } catch (android.os.RemoteException ex) {
1514 * Called when the notification panel layouts
1516 public void onPanelLaidOut() {
1517 if (mState == StatusBarState.KEYGUARD) {
1518 // Since the number of notifications is determined based on the height of the view, we
1519 // need to update them.
1520 int maxBefore = getMaxKeyguardNotifications(false /* recompute */);
1521 int maxNotifications = getMaxKeyguardNotifications(true /* recompute */);
1522 if (maxBefore != maxNotifications) {
1528 protected void onLockedNotificationImportanceChange(OnDismissAction dismissAction) {}
1530 protected void onLockedRemoteInput(ExpandableNotificationRow row, View clickedView) {}
1532 protected void onLockedWorkRemoteInput(int userId, ExpandableNotificationRow row,
1536 public void onExpandClicked(Entry clickedEntry, boolean nowExpanded) {
1539 protected class H extends Handler {
1540 public void handleMessage(Message m) {
1542 case MSG_SHOW_RECENT_APPS:
1543 showRecents(m.arg1 > 0, m.arg2 != 0);
1545 case MSG_HIDE_RECENT_APPS:
1546 hideRecents(m.arg1 > 0, m.arg2 > 0);
1548 case MSG_TOGGLE_RECENTS_APPS:
1551 case MSG_PRELOAD_RECENT_APPS:
1554 case MSG_CANCEL_PRELOAD_RECENT_APPS:
1555 cancelPreloadingRecents();
1557 case MSG_SHOW_NEXT_AFFILIATED_TASK:
1558 showRecentsNextAffiliatedTask();
1560 case MSG_SHOW_PREV_AFFILIATED_TASK:
1561 showRecentsPreviousAffiliatedTask();
1563 case MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU:
1564 toggleKeyboardShortcuts(m.arg1);
1566 case MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU:
1567 dismissKeyboardShortcuts();
1573 protected void workAroundBadLayerDrawableOpacity(View v) {
1576 protected boolean inflateViews(Entry entry, ViewGroup parent) {
1577 PackageManager pmUser = getPackageManagerForUser(mContext,
1578 entry.notification.getUser().getIdentifier());
1580 final StatusBarNotification sbn = entry.notification;
1581 entry.cacheContentViews(mContext, null);
1583 final RemoteViews contentView = entry.cachedContentView;
1584 final RemoteViews bigContentView = entry.cachedBigContentView;
1585 final RemoteViews headsUpContentView = entry.cachedHeadsUpContentView;
1586 final RemoteViews publicContentView = entry.cachedPublicContentView;
1588 if (contentView == null) {
1589 Log.v(TAG, "no contentView for: " + sbn.getNotification());
1594 Log.v(TAG, "publicContentView: " + publicContentView);
1597 ExpandableNotificationRow row;
1599 // Stash away previous user expansion state so we can restore it at
1601 boolean hasUserChangedExpansion = false;
1602 boolean userExpanded = false;
1603 boolean userLocked = false;
1605 if (entry.row != null) {
1607 hasUserChangedExpansion = row.hasUserChangedExpansion();
1608 userExpanded = row.isUserExpanded();
1609 userLocked = row.isUserLocked();
1611 if (hasUserChangedExpansion) {
1612 row.setUserExpanded(userExpanded);
1615 // create the row view
1616 LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
1617 Context.LAYOUT_INFLATER_SERVICE);
1618 row = (ExpandableNotificationRow) inflater.inflate(R.layout.status_bar_notification_row,
1620 row.setExpansionLogger(this, entry.notification.getKey());
1621 row.setGroupManager(mGroupManager);
1622 row.setHeadsUpManager(mHeadsUpManager);
1623 row.setRemoteInputController(mRemoteInputController);
1624 row.setOnExpandClickListener(this);
1626 // Get the app name.
1627 // Note that Notification.Builder#bindHeaderAppName has similar logic
1628 // but since this field is used in the guts, it must be accurate.
1629 // Therefore we will only show the application label, or, failing that, the
1630 // package name. No substitutions.
1631 final String pkg = sbn.getPackageName();
1632 String appname = pkg;
1634 final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
1635 PackageManager.GET_UNINSTALLED_PACKAGES
1636 | PackageManager.GET_DISABLED_COMPONENTS);
1638 appname = String.valueOf(pmUser.getApplicationLabel(info));
1640 } catch (NameNotFoundException e) {
1643 row.setAppName(appname);
1646 workAroundBadLayerDrawableOpacity(row);
1647 View vetoButton = bindVetoButtonClickListener(row, sbn);
1648 vetoButton.setContentDescription(mContext.getString(
1649 R.string.accessibility_remove_notification));
1651 // NB: the large icon is now handled entirely by the template
1653 // bind the click event to the content area
1654 NotificationContentView contentContainer = row.getPrivateLayout();
1655 NotificationContentView contentContainerPublic = row.getPublicLayout();
1657 row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
1658 if (ENABLE_REMOTE_INPUT) {
1659 row.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
1662 mNotificationClicker.register(row, sbn);
1664 // set up the adaptive layout
1665 View contentViewLocal = null;
1666 View bigContentViewLocal = null;
1667 View headsUpContentViewLocal = null;
1668 View publicViewLocal = null;
1670 contentViewLocal = contentView.apply(
1671 sbn.getPackageContext(mContext),
1674 if (bigContentView != null) {
1675 bigContentViewLocal = bigContentView.apply(
1676 sbn.getPackageContext(mContext),
1680 if (headsUpContentView != null) {
1681 headsUpContentViewLocal = headsUpContentView.apply(
1682 sbn.getPackageContext(mContext),
1686 if (publicContentView != null) {
1687 publicViewLocal = publicContentView.apply(
1688 sbn.getPackageContext(mContext),
1689 contentContainerPublic, mOnClickHandler);
1692 catch (RuntimeException e) {
1693 final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId());
1694 Log.e(TAG, "couldn't inflate view for notification " + ident, e);
1698 if (contentViewLocal != null) {
1699 contentViewLocal.setIsRootNamespace(true);
1700 contentContainer.setContractedChild(contentViewLocal);
1702 if (bigContentViewLocal != null) {
1703 bigContentViewLocal.setIsRootNamespace(true);
1704 contentContainer.setExpandedChild(bigContentViewLocal);
1706 if (headsUpContentViewLocal != null) {
1707 headsUpContentViewLocal.setIsRootNamespace(true);
1708 contentContainer.setHeadsUpChild(headsUpContentViewLocal);
1710 if (publicViewLocal != null) {
1711 publicViewLocal.setIsRootNamespace(true);
1712 contentContainerPublic.setContractedChild(publicViewLocal);
1715 // Extract target SDK version.
1717 ApplicationInfo info = pmUser.getApplicationInfo(sbn.getPackageName(), 0);
1718 entry.targetSdk = info.targetSdkVersion;
1719 } catch (NameNotFoundException ex) {
1720 Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex);
1722 entry.autoRedacted = entry.notification.getNotification().publicVersion == null;
1724 if (MULTIUSER_DEBUG) {
1725 TextView debug = (TextView) row.findViewById(R.id.debug_info);
1726 if (debug != null) {
1727 debug.setVisibility(View.VISIBLE);
1728 debug.setText("CU " + mCurrentUserId +" NU " + entry.notification.getUserId());
1732 entry.row.setOnActivatedListener(this);
1733 entry.row.setExpandable(bigContentViewLocal != null);
1735 applyColorsAndBackgrounds(sbn, entry);
1737 // Restore previous flags.
1738 if (hasUserChangedExpansion) {
1739 // Note: setUserExpanded() conveniently ignores calls with
1740 // userExpanded=true if !isExpandable().
1741 row.setUserExpanded(userExpanded);
1743 row.setUserLocked(userLocked);
1744 row.onNotificationUpdated(entry);
1749 * Adds RemoteInput actions from the WearableExtender; to be removed once more apps support this
1750 * via first-class API.
1752 * TODO: Remove once enough apps specify remote inputs on their own.
1754 private void processForRemoteInput(Notification n) {
1755 if (!ENABLE_REMOTE_INPUT) return;
1757 if (n.extras != null && n.extras.containsKey("android.wearable.EXTENSIONS") &&
1758 (n.actions == null || n.actions.length == 0)) {
1759 Notification.Action viableAction = null;
1760 Notification.WearableExtender we = new Notification.WearableExtender(n);
1762 List<Notification.Action> actions = we.getActions();
1763 final int numActions = actions.size();
1765 for (int i = 0; i < numActions; i++) {
1766 Notification.Action action = actions.get(i);
1767 if (action == null) {
1770 RemoteInput[] remoteInputs = action.getRemoteInputs();
1771 if (remoteInputs == null) {
1774 for (RemoteInput ri : remoteInputs) {
1775 if (ri.getAllowFreeFormInput()) {
1776 viableAction = action;
1780 if (viableAction != null) {
1785 if (viableAction != null) {
1786 Notification.Builder rebuilder = Notification.Builder.recoverBuilder(mContext, n);
1787 rebuilder.setActions(viableAction);
1788 rebuilder.build(); // will rewrite n
1793 public void startPendingIntentDismissingKeyguard(final PendingIntent intent) {
1794 if (!isDeviceProvisioned()) return;
1796 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
1797 final boolean afterKeyguardGone = intent.isActivity()
1798 && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
1800 dismissKeyguardThenExecute(new OnDismissAction() {
1801 public boolean onDismiss() {
1806 if (keyguardShowing && !afterKeyguardGone) {
1807 ActivityManagerNative.getDefault()
1808 .keyguardWaitingForActivityDrawn();
1811 // The intent we are sending is for the application, which
1812 // won't have permission to immediately start an activity after
1813 // the user switches to home. We know it is safe to do at this
1814 // point, so make sure new activity switches are now allowed.
1815 ActivityManagerNative.getDefault().resumeAppSwitches();
1816 } catch (RemoteException e) {
1819 intent.send(null, 0, null, null, null, null, getActivityOptions());
1820 } catch (PendingIntent.CanceledException e) {
1821 // the stack trace isn't very helpful here.
1822 // Just log the exception message.
1823 Log.w(TAG, "Sending intent failed: " + e);
1825 // TODO: Dismiss Keyguard.
1827 if (intent.isActivity()) {
1828 mAssistManager.hideAssist();
1829 overrideActivityPendingAppTransition(keyguardShowing
1830 && !afterKeyguardGone);
1835 // close the shade if it was open
1836 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
1837 true /* force */, true /* delayed */);
1838 visibilityChanged(false);
1842 }, afterKeyguardGone);
1845 public void addPostCollapseAction(Runnable r) {
1848 public boolean isCollapsing() {
1852 private final class NotificationClicker implements View.OnClickListener {
1853 public void onClick(final View v) {
1854 if (!(v instanceof ExpandableNotificationRow)) {
1855 Log.e(TAG, "NotificationClicker called on a view that is not a notification row.");
1859 final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
1860 final StatusBarNotification sbn = row.getStatusBarNotification();
1862 Log.e(TAG, "NotificationClicker called on an unclickable notification,");
1866 // Check if the notification is displaying the gear, if so slide notification back
1867 if (row.getSettingsRow() != null && row.getSettingsRow().isVisible()) {
1868 row.animateTranslateNotification(0);
1872 Notification notification = sbn.getNotification();
1873 final PendingIntent intent = notification.contentIntent != null
1874 ? notification.contentIntent
1875 : notification.fullScreenIntent;
1876 final String notificationKey = sbn.getKey();
1878 // Mark notification for one frame.
1879 row.setJustClicked(true);
1880 DejankUtils.postAfterTraversal(new Runnable() {
1883 row.setJustClicked(false);
1887 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
1888 final boolean afterKeyguardGone = intent.isActivity()
1889 && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
1891 dismissKeyguardThenExecute(new OnDismissAction() {
1892 public boolean onDismiss() {
1893 if (mHeadsUpManager != null && mHeadsUpManager.isHeadsUp(notificationKey)) {
1894 // Release the HUN notification to the shade.
1896 if (isPanelFullyCollapsed()) {
1897 HeadsUpManager.setIsClickedNotification(row, true);
1900 // In most cases, when FLAG_AUTO_CANCEL is set, the notification will
1901 // become canceled shortly by NoMan, but we can't assume that.
1902 mHeadsUpManager.releaseImmediately(notificationKey);
1904 StatusBarNotification parentToCancel = null;
1905 if (shouldAutoCancel(sbn) && mGroupManager.isOnlyChildInGroup(sbn)) {
1906 StatusBarNotification summarySbn = mGroupManager.getLogicalGroupSummary(sbn)
1907 .getStatusBarNotification();
1908 if (shouldAutoCancel(summarySbn)) {
1909 parentToCancel = summarySbn;
1912 final StatusBarNotification parentToCancelFinal = parentToCancel;
1917 if (keyguardShowing && !afterKeyguardGone) {
1918 ActivityManagerNative.getDefault()
1919 .keyguardWaitingForActivityDrawn();
1922 // The intent we are sending is for the application, which
1923 // won't have permission to immediately start an activity after
1924 // the user switches to home. We know it is safe to do at this
1925 // point, so make sure new activity switches are now allowed.
1926 ActivityManagerNative.getDefault().resumeAppSwitches();
1927 } catch (RemoteException e) {
1929 if (intent != null) {
1930 // If we are launching a work activity and require to launch
1931 // separate work challenge, we defer the activity action and cancel
1932 // notification until work challenge is unlocked.
1933 if (intent.isActivity()) {
1934 final int userId = intent.getCreatorUserHandle()
1936 if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)
1937 && mKeyguardManager.isDeviceLocked(userId)) {
1938 if (startWorkChallengeIfNecessary(userId,
1939 intent.getIntentSender(), notificationKey)) {
1940 // Show work challenge, do not run pendingintent and
1941 // remove notification
1947 intent.send(null, 0, null, null, null, null,
1948 getActivityOptions());
1949 } catch (PendingIntent.CanceledException e) {
1950 // the stack trace isn't very helpful here.
1951 // Just log the exception message.
1952 Log.w(TAG, "Sending contentIntent failed: " + e);
1954 // TODO: Dismiss Keyguard.
1956 if (intent.isActivity()) {
1957 mAssistManager.hideAssist();
1958 overrideActivityPendingAppTransition(keyguardShowing
1959 && !afterKeyguardGone);
1964 mBarService.onNotificationClick(notificationKey);
1965 } catch (RemoteException ex) {
1966 // system process is dead if we're here.
1968 if (parentToCancelFinal != null) {
1969 // We have to post it to the UI thread for synchronization
1970 mHandler.post(new Runnable() {
1973 Runnable removeRunnable = new Runnable() {
1976 performRemoveNotification(parentToCancelFinal,
1980 if (isCollapsing()) {
1981 // To avoid lags we're only performing the remove
1982 // after the shade was collapsed
1983 addPostCollapseAction(removeRunnable);
1985 removeRunnable.run();
1993 // close the shade if it was open
1994 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
1995 true /* force */, true /* delayed */);
1996 visibilityChanged(false);
2000 }, afterKeyguardGone);
2003 private boolean shouldAutoCancel(StatusBarNotification sbn) {
2004 int flags = sbn.getNotification().flags;
2005 if ((flags & Notification.FLAG_AUTO_CANCEL) != Notification.FLAG_AUTO_CANCEL) {
2008 if ((flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
2014 public void register(ExpandableNotificationRow row, StatusBarNotification sbn) {
2015 Notification notification = sbn.getNotification();
2016 if (notification.contentIntent != null || notification.fullScreenIntent != null) {
2017 row.setOnClickListener(this);
2019 row.setOnClickListener(null);
2024 public void animateCollapsePanels(int flags, boolean force) {
2027 public void animateCollapsePanels(int flags, boolean force, boolean delayed) {
2030 public void overrideActivityPendingAppTransition(boolean keyguardShowing) {
2031 if (keyguardShowing) {
2033 mWindowManagerService.overridePendingAppTransition(null, 0, 0, null);
2034 } catch (RemoteException e) {
2035 Log.w(TAG, "Error overriding app transition: " + e);
2040 protected boolean startWorkChallengeIfNecessary(int userId, IntentSender intendSender,
2041 String notificationKey) {
2042 final Intent newIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null,
2044 if (newIntent == null) {
2047 final Intent callBackIntent = new Intent(
2048 WORK_CHALLENGE_UNLOCKED_NOTIFICATION_ACTION);
2049 callBackIntent.putExtra(Intent.EXTRA_INTENT, intendSender);
2050 callBackIntent.putExtra(Intent.EXTRA_INDEX, notificationKey);
2051 callBackIntent.setPackage(mContext.getPackageName());
2053 PendingIntent callBackPendingIntent = PendingIntent.getBroadcast(
2057 PendingIntent.FLAG_CANCEL_CURRENT |
2058 PendingIntent.FLAG_ONE_SHOT |
2059 PendingIntent.FLAG_IMMUTABLE);
2061 Intent.EXTRA_INTENT,
2062 callBackPendingIntent.getIntentSender());
2064 ActivityManagerNative.getDefault().startConfirmDeviceCredentialIntent(newIntent);
2065 } catch (RemoteException ex) {
2071 protected Bundle getActivityOptions() {
2072 // Anything launched from the notification shade should always go into the
2073 // fullscreen stack.
2074 ActivityOptions options = ActivityOptions.makeBasic();
2075 options.setLaunchStackId(StackId.FULLSCREEN_WORKSPACE_STACK_ID);
2076 return options.toBundle();
2079 protected void visibilityChanged(boolean visible) {
2080 if (mVisible != visible) {
2086 updateVisibleToUser();
2089 protected void updateVisibleToUser() {
2090 boolean oldVisibleToUser = mVisibleToUser;
2091 mVisibleToUser = mVisible && mDeviceInteractive;
2093 if (oldVisibleToUser != mVisibleToUser) {
2094 handleVisibleToUserChanged(mVisibleToUser);
2099 * The LEDs are turned off when the notification panel is shown, even just a little bit.
2100 * See also PhoneStatusBar.setPanelExpanded for another place where we attempt to do this.
2102 protected void handleVisibleToUserChanged(boolean visibleToUser) {
2104 if (visibleToUser) {
2105 boolean pinnedHeadsUp = mHeadsUpManager.hasPinnedHeadsUp();
2106 boolean clearNotificationEffects =
2107 !isPanelFullyCollapsed() &&
2108 (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED);
2109 int notificationLoad = mNotificationData.getActiveNotifications().size();
2110 if (pinnedHeadsUp && isPanelFullyCollapsed()) {
2111 notificationLoad = 1;
2113 MetricsLogger.histogram(mContext, "note_load", notificationLoad);
2115 mBarService.onPanelRevealed(clearNotificationEffects, notificationLoad);
2117 mBarService.onPanelHidden();
2119 } catch (RemoteException ex) {
2120 // Won't fail unless the world has ended.
2125 * Clear Buzz/Beep/Blink.
2127 public void clearNotificationEffects() {
2129 mBarService.clearNotificationEffects();
2130 } catch (RemoteException e) {
2131 // Won't fail unless the world has ended.
2135 public abstract boolean isPanelFullyCollapsed();
2138 * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
2139 * about the failure.
2141 * WARNING: this will call back into us. Don't hold any locks.
2143 void handleNotificationError(StatusBarNotification n, String message) {
2144 removeNotification(n.getKey(), null);
2146 mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(),
2147 n.getInitialPid(), message, n.getUserId());
2148 } catch (RemoteException ex) {
2153 protected StatusBarNotification removeNotificationViews(String key, RankingMap ranking) {
2154 NotificationData.Entry entry = mNotificationData.remove(key, ranking);
2155 if (entry == null) {
2156 Log.w(TAG, "removeNotification for unknown key: " + key);
2159 updateNotifications();
2160 return entry.notification;
2163 protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn) {
2165 Log.d(TAG, "createNotificationViews(notification=" + sbn);
2167 final StatusBarIconView iconView = createIcon(sbn);
2168 if (iconView == null) {
2172 // Construct the expanded view.
2173 NotificationData.Entry entry = new NotificationData.Entry(sbn, iconView);
2174 if (!inflateViews(entry, mStackScroller)) {
2175 handleNotificationError(sbn, "Couldn't expand RemoteViews for: " + sbn);
2181 public StatusBarIconView createIcon(StatusBarNotification sbn) {
2182 // Construct the icon.
2183 Notification n = sbn.getNotification();
2184 final StatusBarIconView iconView = new StatusBarIconView(mContext,
2185 sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), n);
2186 iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
2188 final Icon smallIcon = n.getSmallIcon();
2189 if (smallIcon == null) {
2190 handleNotificationError(sbn,
2191 "No small icon in notification from " + sbn.getPackageName());
2194 final StatusBarIcon ic = new StatusBarIcon(
2196 sbn.getPackageName(),
2200 StatusBarIconView.contentDescForNotification(mContext, n));
2201 if (!iconView.set(ic)) {
2202 handleNotificationError(sbn, "Couldn't create icon: " + ic);
2208 protected void addNotificationViews(Entry entry, RankingMap ranking) {
2209 if (entry == null) {
2212 // Add the expanded view and icon.
2213 mNotificationData.add(entry, ranking);
2214 updateNotifications();
2218 * @param recompute wheter the number should be recomputed
2219 * @return The number of notifications we show on Keyguard.
2221 protected abstract int getMaxKeyguardNotifications(boolean recompute);
2224 * Updates expanded, dimmed and locked states of notification rows.
2226 protected void updateRowStates() {
2227 mKeyguardIconOverflowContainer.getIconsView().removeAllViews();
2229 ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
2230 final int N = activeNotifications.size();
2232 int visibleNotifications = 0;
2233 boolean onKeyguard = mState == StatusBarState.KEYGUARD;
2234 int maxNotifications = 0;
2236 maxNotifications = getMaxKeyguardNotifications(true /* recompute */);
2238 for (int i = 0; i < N; i++) {
2239 NotificationData.Entry entry = activeNotifications.get(i);
2240 boolean childNotification = mGroupManager.isChildInGroupWithSummary(entry.notification);
2242 entry.row.setOnKeyguard(true);
2244 entry.row.setOnKeyguard(false);
2245 entry.row.setSystemExpanded(visibleNotifications == 0 && !childNotification);
2247 boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(
2248 entry.notification) && !entry.row.isRemoved();
2249 boolean childWithVisibleSummary = childNotification
2250 && mGroupManager.getGroupSummary(entry.notification).getVisibility()
2252 boolean showOnKeyguard = shouldShowOnKeyguard(entry.notification);
2253 if (suppressedSummary || (isLockscreenPublicMode() && !mShowLockscreenNotifications) ||
2254 (onKeyguard && !childWithVisibleSummary
2255 && (visibleNotifications >= maxNotifications || !showOnKeyguard))) {
2256 entry.row.setVisibility(View.GONE);
2257 if (onKeyguard && showOnKeyguard && !childNotification && !suppressedSummary) {
2258 mKeyguardIconOverflowContainer.getIconsView().addNotification(entry);
2261 boolean wasGone = entry.row.getVisibility() == View.GONE;
2262 entry.row.setVisibility(View.VISIBLE);
2263 if (!childNotification && !entry.row.isRemoved()) {
2265 // notify the scroller of a child addition
2266 mStackScroller.generateAddAnimation(entry.row,
2267 !showOnKeyguard /* fromMoreCard */);
2269 visibleNotifications++;
2274 mStackScroller.updateOverflowContainerVisibility(onKeyguard
2275 && mKeyguardIconOverflowContainer.getIconsView().getChildCount() > 0);
2277 mStackScroller.changeViewPosition(mDismissView, mStackScroller.getChildCount() - 1);
2278 mStackScroller.changeViewPosition(mEmptyShadeView, mStackScroller.getChildCount() - 2);
2279 mStackScroller.changeViewPosition(mKeyguardIconOverflowContainer,
2280 mStackScroller.getChildCount() - 3);
2283 public boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
2284 return mShowLockscreenNotifications && !mNotificationData.isAmbient(sbn.getKey());
2287 protected void setZenMode(int mode) {
2288 if (!isDeviceProvisioned()) return;
2290 updateNotifications();
2293 // extended in PhoneStatusBar
2294 protected void setShowLockscreenNotifications(boolean show) {
2295 mShowLockscreenNotifications = show;
2298 protected void setLockScreenAllowRemoteInput(boolean allowLockscreenRemoteInput) {
2299 mAllowLockscreenRemoteInput = allowLockscreenRemoteInput;
2302 private void updateLockscreenNotificationSetting() {
2303 final boolean show = Settings.Secure.getIntForUser(mContext.getContentResolver(),
2304 Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
2306 mCurrentUserId) != 0;
2307 final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(
2308 null /* admin */, mCurrentUserId);
2309 final boolean allowedByDpm = (dpmFlags
2310 & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0;
2312 setShowLockscreenNotifications(show && allowedByDpm);
2314 if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) {
2315 final boolean remoteInput = Settings.Secure.getIntForUser(mContext.getContentResolver(),
2316 Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT,
2318 mCurrentUserId) != 0;
2319 final boolean remoteInputDpm =
2320 (dpmFlags & DevicePolicyManager.KEYGUARD_DISABLE_REMOTE_INPUT) == 0;
2322 setLockScreenAllowRemoteInput(remoteInput && remoteInputDpm);
2324 setLockScreenAllowRemoteInput(false);
2328 protected abstract void setAreThereNotifications();
2329 protected abstract void updateNotifications();
2330 public abstract boolean shouldDisableNavbarGestures();
2332 public abstract void addNotification(StatusBarNotification notification,
2333 RankingMap ranking, Entry oldEntry);
2334 protected abstract void updateNotificationRanking(RankingMap ranking);
2335 public abstract void removeNotification(String key, RankingMap ranking);
2337 public void updateNotification(StatusBarNotification notification, RankingMap ranking) {
2338 if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
2340 final String key = notification.getKey();
2341 Entry entry = mNotificationData.get(key);
2342 if (entry == null) {
2345 mHeadsUpEntriesToRemoveOnSwitch.remove(entry);
2346 mRemoteInputEntriesToRemoveOnCollapse.remove(entry);
2349 Notification n = notification.getNotification();
2350 mNotificationData.updateRanking(ranking);
2352 boolean applyInPlace = entry.cacheContentViews(mContext, notification.getNotification());
2353 boolean shouldPeek = shouldPeek(entry, notification);
2354 boolean alertAgain = alertAgain(entry, n);
2356 Log.d(TAG, "applyInPlace=" + applyInPlace
2357 + " shouldPeek=" + shouldPeek
2358 + " alertAgain=" + alertAgain);
2361 final StatusBarNotification oldNotification = entry.notification;
2362 entry.notification = notification;
2363 mGroupManager.onEntryUpdated(entry, oldNotification);
2365 boolean updateSuccessful = false;
2367 if (DEBUG) Log.d(TAG, "reusing notification for key: " + key);
2369 if (entry.icon != null) {
2371 final StatusBarIcon ic = new StatusBarIcon(
2372 notification.getUser(),
2373 notification.getPackageName(),
2377 StatusBarIconView.contentDescForNotification(mContext, n));
2378 entry.icon.setNotification(n);
2379 if (!entry.icon.set(ic)) {
2380 handleNotificationError(notification, "Couldn't update icon: " + ic);
2384 updateNotificationViews(entry, notification);
2385 updateSuccessful = true;
2387 catch (RuntimeException e) {
2388 // It failed to apply cleanly.
2389 Log.w(TAG, "Couldn't reapply views for package " +
2390 notification.getPackageName(), e);
2393 if (!updateSuccessful) {
2394 if (DEBUG) Log.d(TAG, "not reusing notification for key: " + key);
2395 final StatusBarIcon ic = new StatusBarIcon(
2396 notification.getUser(),
2397 notification.getPackageName(),
2401 StatusBarIconView.contentDescForNotification(mContext, n));
2402 entry.icon.setNotification(n);
2404 inflateViews(entry, mStackScroller);
2406 updateHeadsUp(key, entry, shouldPeek, alertAgain);
2407 updateNotifications();
2409 // Update the veto button accordingly (and as a result, whether this row is
2410 // swipe-dismissable)
2411 bindVetoButtonClickListener(entry.row, notification);
2413 if (!notification.isClearable()) {
2414 // The user may have performed a dismiss action on the notification, since it's
2415 // not clearable we should snap it back.
2416 mStackScroller.snapViewIfNeeded(entry.row);
2421 boolean isForCurrentUser = isNotificationForCurrentProfiles(notification);
2422 Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");
2425 setAreThereNotifications();
2428 protected abstract void updateHeadsUp(String key, Entry entry, boolean shouldPeek,
2429 boolean alertAgain);
2431 private void updateNotificationViews(Entry entry, StatusBarNotification sbn) {
2432 final RemoteViews contentView = entry.cachedContentView;
2433 final RemoteViews bigContentView = entry.cachedBigContentView;
2434 final RemoteViews headsUpContentView = entry.cachedHeadsUpContentView;
2435 final RemoteViews publicContentView = entry.cachedPublicContentView;
2437 // Reapply the RemoteViews
2438 contentView.reapply(mContext, entry.getContentView(), mOnClickHandler);
2439 if (bigContentView != null && entry.getExpandedContentView() != null) {
2440 bigContentView.reapply(sbn.getPackageContext(mContext),
2441 entry.getExpandedContentView(),
2444 View headsUpChild = entry.getHeadsUpContentView();
2445 if (headsUpContentView != null && headsUpChild != null) {
2446 headsUpContentView.reapply(sbn.getPackageContext(mContext),
2447 headsUpChild, mOnClickHandler);
2449 if (publicContentView != null && entry.getPublicContentView() != null) {
2450 publicContentView.reapply(sbn.getPackageContext(mContext),
2451 entry.getPublicContentView(), mOnClickHandler);
2453 // update the contentIntent
2454 mNotificationClicker.register(entry.row, sbn);
2456 entry.row.onNotificationUpdated(entry);
2457 entry.row.resetHeight();
2460 protected void updatePublicContentView(Entry entry,
2461 StatusBarNotification sbn) {
2462 final RemoteViews publicContentView = entry.cachedPublicContentView;
2463 View inflatedView = entry.getPublicContentView();
2464 if (entry.autoRedacted && publicContentView != null && inflatedView != null) {
2465 final boolean disabledByPolicy =
2466 !adminAllowsUnredactedNotifications(entry.notification.getUserId());
2467 String notificationHiddenText = mContext.getString(disabledByPolicy
2468 ? com.android.internal.R.string.notification_hidden_by_policy_text
2469 : com.android.internal.R.string.notification_hidden_text);
2470 TextView titleView = (TextView) inflatedView.findViewById(android.R.id.title);
2471 if (titleView != null
2472 && !titleView.getText().toString().equals(notificationHiddenText)) {
2473 publicContentView.setTextViewText(android.R.id.title, notificationHiddenText);
2474 publicContentView.reapply(sbn.getPackageContext(mContext),
2475 inflatedView, mOnClickHandler);
2476 entry.row.onNotificationUpdated(entry);
2481 protected void notifyHeadsUpScreenOff() {
2482 maybeEscalateHeadsUp();
2485 private boolean alertAgain(Entry oldEntry, Notification newNotification) {
2486 return oldEntry == null || !oldEntry.hasInterrupted()
2487 || (newNotification.flags & Notification.FLAG_ONLY_ALERT_ONCE) == 0;
2490 protected boolean shouldPeek(Entry entry) {
2491 return shouldPeek(entry, entry.notification);
2494 protected boolean shouldPeek(Entry entry, StatusBarNotification sbn) {
2495 if (!mUseHeadsUp || isDeviceInVrMode()) {
2499 if (mNotificationData.shouldFilterOut(sbn)) {
2500 if (DEBUG) Log.d(TAG, "No peeking: filtered notification: " + sbn.getKey());
2504 boolean inUse = mPowerManager.isScreenOn()
2505 && (!mStatusBarKeyguardViewManager.isShowing()
2506 || mStatusBarKeyguardViewManager.isOccluded())
2507 && !mStatusBarKeyguardViewManager.isInputRestricted();
2509 inUse = inUse && !mDreamManager.isDreaming();
2510 } catch (RemoteException e) {
2511 Log.d(TAG, "failed to query dream manager", e);
2516 Log.d(TAG, "No peeking: not in use: " + sbn.getKey());
2521 if (mNotificationData.shouldSuppressScreenOn(sbn.getKey())) {
2522 if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
2526 if (entry.hasJustLaunchedFullScreenIntent()) {
2527 if (DEBUG) Log.d(TAG, "No peeking: recent fullscreen: " + sbn.getKey());
2531 if (isSnoozedPackage(sbn)) {
2532 if (DEBUG) Log.d(TAG, "No peeking: snoozed package: " + sbn.getKey());
2536 if (mNotificationData.getImportance(sbn.getKey()) < IMPORTANCE_HIGH) {
2537 if (DEBUG) Log.d(TAG, "No peeking: unimportant notification: " + sbn.getKey());
2541 if (sbn.getNotification().fullScreenIntent != null) {
2542 if (mAccessibilityManager.isTouchExplorationEnabled()) {
2543 if (DEBUG) Log.d(TAG, "No peeking: accessible fullscreen: " + sbn.getKey());
2553 protected abstract boolean isSnoozedPackage(StatusBarNotification sbn);
2555 public void setInteracting(int barWindow, boolean interacting) {
2556 // hook for subclasses
2559 public void setBouncerShowing(boolean bouncerShowing) {
2560 mBouncerShowing = bouncerShowing;
2564 * @return Whether the security bouncer from Keyguard is showing.
2566 public boolean isBouncerShowing() {
2567 return mBouncerShowing;
2570 public void destroy() {
2571 mContext.unregisterReceiver(mBroadcastReceiver);
2573 mNotificationListener.unregisterAsSystemService();
2574 } catch (RemoteException e) {
2580 * @return a PackageManger for userId or if userId is < 0 (USER_ALL etc) then
2581 * return PackageManager for mContext
2583 public static PackageManager getPackageManagerForUser(Context context, int userId) {
2584 Context contextForUser = context;
2585 // UserHandle defines special userId as negative values, e.g. USER_ALL
2588 // Create a context for the correct user so if a package isn't installed
2589 // for user 0 we can still load information about the package.
2591 context.createPackageContextAsUser(context.getPackageName(),
2592 Context.CONTEXT_RESTRICTED,
2593 new UserHandle(userId));
2594 } catch (NameNotFoundException e) {
2595 // Shouldn't fail to find the package name for system ui.
2598 return contextForUser.getPackageManager();
2602 public void logNotificationExpansion(String key, boolean userAction, boolean expanded) {
2604 mBarService.onNotificationExpansionChanged(key, userAction, expanded);
2605 } catch (RemoteException e) {
2610 public boolean isKeyguardSecure() {
2611 if (mStatusBarKeyguardViewManager == null) {
2612 // startKeyguard() hasn't been called yet, so we don't know.
2613 // Make sure anything that needs to know isKeyguardSecure() checks and re-checks this
2614 // value onVisibilityChanged().
2615 Slog.w(TAG, "isKeyguardSecure() called before startKeyguard(), returning false",
2619 return mStatusBarKeyguardViewManager.isSecure();
2623 public void showAssistDisclosure() {
2624 if (mAssistManager != null) {
2625 mAssistManager.showDisclosure();
2630 public void startAssist(Bundle args) {
2631 if (mAssistManager != null) {
2632 mAssistManager.startAssist(args);