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.app.ActivityManager;
20 import android.app.ActivityManagerNative;
21 import android.app.Notification;
22 import android.app.PendingIntent;
23 import android.app.TaskStackBuilder;
24 import android.content.BroadcastReceiver;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.IntentFilter;
28 import android.content.pm.ApplicationInfo;
29 import android.content.pm.PackageManager;
30 import android.content.pm.PackageManager.NameNotFoundException;
31 import android.content.pm.UserInfo;
32 import android.content.res.Configuration;
33 import android.database.ContentObserver;
34 import android.graphics.Rect;
35 import android.graphics.drawable.Drawable;
36 import android.net.Uri;
37 import android.os.Build;
38 import android.os.Handler;
39 import android.os.IBinder;
40 import android.os.Message;
41 import android.os.PowerManager;
42 import android.os.RemoteException;
43 import android.os.ServiceManager;
44 import android.os.UserHandle;
45 import android.os.UserManager;
46 import android.provider.Settings;
47 import android.service.dreams.DreamService;
48 import android.service.dreams.IDreamManager;
49 import android.service.notification.StatusBarNotification;
50 import android.text.TextUtils;
51 import android.util.Log;
52 import android.util.SparseArray;
53 import android.util.SparseBooleanArray;
54 import android.view.Display;
55 import android.view.IWindowManager;
56 import android.view.LayoutInflater;
57 import android.view.MenuItem;
58 import android.view.MotionEvent;
59 import android.view.View;
60 import android.view.ViewGroup;
61 import android.view.ViewGroup.LayoutParams;
62 import android.view.WindowManager;
63 import android.view.WindowManagerGlobal;
64 import android.widget.ImageView;
65 import android.widget.LinearLayout;
66 import android.widget.PopupMenu;
67 import android.widget.RemoteViews;
68 import android.widget.TextView;
70 import com.android.internal.statusbar.IStatusBarService;
71 import com.android.internal.statusbar.StatusBarIcon;
72 import com.android.internal.statusbar.StatusBarIconList;
73 import com.android.internal.util.LegacyNotificationUtil;
74 import com.android.internal.widget.SizeAdaptiveLayout;
75 import com.android.systemui.R;
76 import com.android.systemui.RecentsComponent;
77 import com.android.systemui.SearchPanelView;
78 import com.android.systemui.SystemUI;
79 import com.android.systemui.statusbar.phone.KeyguardTouchDelegate;
80 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
82 import java.util.ArrayList;
83 import java.util.Locale;
85 public abstract class BaseStatusBar extends SystemUI implements
86 CommandQueue.Callbacks {
87 public static final String TAG = "StatusBar";
88 public static final boolean DEBUG = false;
89 public static final boolean MULTIUSER_DEBUG = false;
91 protected static final int MSG_TOGGLE_RECENTS_PANEL = 1020;
92 protected static final int MSG_CLOSE_RECENTS_PANEL = 1021;
93 protected static final int MSG_PRELOAD_RECENT_APPS = 1022;
94 protected static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 1023;
95 protected static final int MSG_OPEN_SEARCH_PANEL = 1024;
96 protected static final int MSG_CLOSE_SEARCH_PANEL = 1025;
97 protected static final int MSG_SHOW_HEADS_UP = 1026;
98 protected static final int MSG_HIDE_HEADS_UP = 1027;
99 protected static final int MSG_ESCALATE_HEADS_UP = 1028;
101 protected static final boolean ENABLE_HEADS_UP = true;
102 // scores above this threshold should be displayed in heads up mode.
103 protected static final int INTERRUPTION_THRESHOLD = 10;
104 protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
106 // Should match the value in PhoneWindowManager
107 public static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
109 public static final int EXPANDED_LEAVE_ALONE = -10000;
110 public static final int EXPANDED_FULL_OPEN = -10001;
112 protected CommandQueue mCommandQueue;
113 protected IStatusBarService mBarService;
114 protected H mHandler = createHandler();
117 protected NotificationData mNotificationData = new NotificationData();
118 protected NotificationStackScrollLayout mStackScroller;
120 protected NotificationData.Entry mInterruptingNotificationEntry;
121 protected long mInterruptingNotificationTime;
123 // used to notify status bar for suppressing notification LED
124 protected boolean mPanelSlightlyVisible;
127 protected SearchPanelView mSearchPanelView;
129 protected PopupMenu mNotificationBlamePopup;
131 protected int mCurrentUserId = 0;
132 final protected SparseArray<UserInfo> mCurrentProfiles = new SparseArray<UserInfo>();
134 protected int mLayoutDirection = -1; // invalid
135 private Locale mLocale;
136 protected boolean mUseHeadsUp = false;
137 protected boolean mHeadsUpTicker = false;
139 protected IDreamManager mDreamManager;
140 PowerManager mPowerManager;
141 protected int mRowMinHeight;
142 protected int mRowMaxHeight;
144 // public mode, private notifications, etc
145 private boolean mLockscreenPublicMode = false;
146 private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray();
147 private LegacyNotificationUtil mLegacyNotificationUtil = LegacyNotificationUtil.getInstance();
149 private UserManager mUserManager;
151 // UI-specific methods
154 * Create all windows necessary for the status bar (including navigation, overlay panels, etc)
155 * and add them to the window manager.
157 protected abstract void createAndAddWindows();
159 protected WindowManager mWindowManager;
160 protected IWindowManager mWindowManagerService;
161 protected abstract void refreshLayout(int layoutDirection);
163 protected Display mDisplay;
165 private boolean mDeviceProvisioned = false;
167 private RecentsComponent mRecents;
169 protected int mZenMode;
171 protected boolean mOnKeyguard;
172 protected View mKeyguardIconOverflowContainer;
173 protected NotificationOverflowIconsView mOverflowIconsView;
175 public boolean isDeviceProvisioned() {
176 return mDeviceProvisioned;
179 protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
181 public void onChange(boolean selfChange) {
182 final boolean provisioned = 0 != Settings.Global.getInt(
183 mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0);
184 if (provisioned != mDeviceProvisioned) {
185 mDeviceProvisioned = provisioned;
186 updateNotificationIcons();
188 final int mode = Settings.Global.getInt(mContext.getContentResolver(),
189 Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
194 private final ContentObserver mLockscreenSettingsObserver = new ContentObserver(mHandler) {
196 public void onChange(boolean selfChange) {
197 // We don't know which user changed LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
198 // so we just dump our cache ...
199 mUsersAllowingPrivateNotifications.clear();
200 // ... and refresh all the notifications
201 updateNotificationIcons();
205 private RemoteViews.OnClickHandler mOnClickHandler = new RemoteViews.OnClickHandler() {
207 public boolean onClickHandler(View view, PendingIntent pendingIntent, Intent fillInIntent) {
209 Log.v(TAG, "Notification click handler invoked for intent: " + pendingIntent);
211 final boolean isActivity = pendingIntent.isActivity();
214 // The intent we are sending is for the application, which
215 // won't have permission to immediately start an activity after
216 // the user switches to home. We know it is safe to do at this
217 // point, so make sure new activity switches are now allowed.
218 ActivityManagerNative.getDefault().resumeAppSwitches();
219 // Also, notifications can be launched from the lock screen,
220 // so dismiss the lock screen when the activity starts.
221 ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
222 } catch (RemoteException e) {
226 boolean handled = super.onClickHandler(view, pendingIntent, fillInIntent);
228 if (isActivity && handled) {
229 // close the shade if it was open
230 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
231 visibilityChanged(false);
237 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
239 public void onReceive(Context context, Intent intent) {
240 String action = intent.getAction();
241 if (Intent.ACTION_USER_SWITCHED.equals(action)) {
242 mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
243 updateCurrentProfilesCache();
244 if (true) Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
245 userSwitched(mCurrentUserId);
246 } else if (Intent.ACTION_USER_ADDED.equals(action)) {
247 updateCurrentProfilesCache();
252 private void updateCurrentProfilesCache() {
253 synchronized (mCurrentProfiles) {
254 mCurrentProfiles.clear();
255 if (mUserManager != null) {
256 for (UserInfo user : mUserManager.getProfiles(mCurrentUserId)) {
257 mCurrentProfiles.put(user.id, user);
263 public void start() {
264 mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
265 mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
266 mDisplay = mWindowManager.getDefaultDisplay();
268 mDreamManager = IDreamManager.Stub.asInterface(
269 ServiceManager.checkService(DreamService.DREAM_SERVICE));
270 mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
272 mSettingsObserver.onChange(false); // set up
273 mContext.getContentResolver().registerContentObserver(
274 Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), true,
276 mContext.getContentResolver().registerContentObserver(
277 Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false,
280 mContext.getContentResolver().registerContentObserver(
281 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
283 mLockscreenSettingsObserver,
284 UserHandle.USER_ALL);
286 mBarService = IStatusBarService.Stub.asInterface(
287 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
289 mRecents = getComponent(RecentsComponent.class);
291 mLocale = mContext.getResources().getConfiguration().locale;
292 mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale);
294 mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
296 // Connect in to the status bar manager service
297 StatusBarIconList iconList = new StatusBarIconList();
298 ArrayList<IBinder> notificationKeys = new ArrayList<IBinder>();
299 ArrayList<StatusBarNotification> notifications = new ArrayList<StatusBarNotification>();
300 mCommandQueue = new CommandQueue(this, iconList);
302 int[] switches = new int[7];
303 ArrayList<IBinder> binders = new ArrayList<IBinder>();
305 mBarService.registerStatusBar(mCommandQueue, iconList, notificationKeys, notifications,
307 } catch (RemoteException ex) {
308 // If the system process isn't there we're doomed anyway.
311 createAndAddWindows();
313 disable(switches[0]);
314 setSystemUiVisibility(switches[1], 0xffffffff);
315 topAppWindowChanged(switches[2] != 0);
316 // StatusBarManagerService has a back up of IME token and it's restored here.
317 setImeWindowStatus(binders.get(0), switches[3], switches[4]);
318 setHardKeyboardStatus(switches[5] != 0, switches[6] != 0);
320 // Set up the initial icon state
321 int N = iconList.size();
323 for (int i=0; i<N; i++) {
324 StatusBarIcon icon = iconList.getIcon(i);
326 addIcon(iconList.getSlot(i), i, viewIndex, icon);
331 // Set up the initial notification state
332 N = notificationKeys.size();
333 if (N == notifications.size()) {
334 for (int i=0; i<N; i++) {
335 addNotification(notificationKeys.get(i), notifications.get(i));
338 Log.wtf(TAG, "Notification list length mismatch: keys=" + N
339 + " notifications=" + notifications.size());
343 Log.d(TAG, String.format(
344 "init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x",
353 mCurrentUserId = ActivityManager.getCurrentUser();
355 IntentFilter filter = new IntentFilter();
356 filter.addAction(Intent.ACTION_USER_SWITCHED);
357 filter.addAction(Intent.ACTION_USER_ADDED);
358 mContext.registerReceiver(mBroadcastReceiver, filter);
360 updateCurrentProfilesCache();
363 public void userSwitched(int newUserId) {
364 // should be overridden
367 public boolean notificationIsForCurrentProfiles(StatusBarNotification n) {
368 final int thisUserId = mCurrentUserId;
369 final int notificationUserId = n.getUserId();
370 if (DEBUG && MULTIUSER_DEBUG) {
371 Log.v(TAG, String.format("%s: current userid: %d, notification userid: %d",
372 n, thisUserId, notificationUserId));
374 synchronized (mCurrentProfiles) {
375 return notificationUserId == UserHandle.USER_ALL
376 || mCurrentProfiles.get(notificationUserId) != null;
381 protected void onConfigurationChanged(Configuration newConfig) {
382 final Locale locale = mContext.getResources().getConfiguration().locale;
383 final int ld = TextUtils.getLayoutDirectionFromLocale(locale);
384 if (! locale.equals(mLocale) || ld != mLayoutDirection) {
386 Log.v(TAG, String.format(
387 "config changed locale/LD: %s (%d) -> %s (%d)", mLocale, mLayoutDirection,
391 mLayoutDirection = ld;
396 protected View updateNotificationVetoButton(View row, StatusBarNotification n) {
397 View vetoButton = row.findViewById(R.id.veto);
398 if (n.isClearable() || (mInterruptingNotificationEntry != null
399 && mInterruptingNotificationEntry.row == row)) {
400 final String _pkg = n.getPackageName();
401 final String _tag = n.getTag();
402 final int _id = n.getId();
403 final int _userId = n.getUserId();
404 vetoButton.setOnClickListener(new View.OnClickListener() {
405 public void onClick(View v) {
406 // Accessibility feedback
407 v.announceForAccessibility(
408 mContext.getString(R.string.accessibility_notification_dismissed));
410 mBarService.onNotificationClear(_pkg, _tag, _id, _userId);
412 } catch (RemoteException ex) {
413 // system process is dead if we're here.
417 vetoButton.setVisibility(View.VISIBLE);
419 vetoButton.setVisibility(View.GONE);
421 vetoButton.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
426 protected void applyLegacyRowBackground(StatusBarNotification sbn,
427 NotificationData.Entry entry) {
428 if (entry.expanded.getId() != com.android.internal.R.id.status_bar_latest_event_content) {
431 ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(sbn.getPackageName(), 0);
432 version = info.targetSdkVersion;
433 } catch (NameNotFoundException ex) {
434 Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex);
436 if (version > 0 && version < Build.VERSION_CODES.GINGERBREAD) {
437 entry.row.setBackgroundResource(R.drawable.notification_row_legacy_bg);
438 } else if (version < Build.VERSION_CODES.L) {
439 entry.row.setBackgroundResourceIds(
440 com.android.internal.R.drawable.notification_bg,
441 com.android.internal.R.drawable.notification_bg_dim);
446 private void startApplicationDetailsActivity(String packageName) {
447 Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
448 Uri.fromParts("package", packageName, null));
449 intent.setComponent(intent.resolveActivity(mContext.getPackageManager()));
450 TaskStackBuilder.create(mContext).addNextIntentWithParentStack(intent).startActivities(
451 null, UserHandle.CURRENT);
454 protected View.OnLongClickListener getNotificationLongClicker() {
455 return new View.OnLongClickListener() {
457 public boolean onLongClick(View v) {
458 final String packageNameF = (String) v.getTag();
459 if (packageNameF == null) return false;
460 if (v.getWindowToken() == null) return false;
461 mNotificationBlamePopup = new PopupMenu(mContext, v);
462 mNotificationBlamePopup.getMenuInflater().inflate(
463 R.menu.notification_popup_menu,
464 mNotificationBlamePopup.getMenu());
465 mNotificationBlamePopup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
466 public boolean onMenuItemClick(MenuItem item) {
467 if (item.getItemId() == R.id.notification_inspect_item) {
468 startApplicationDetailsActivity(packageNameF);
469 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
476 mNotificationBlamePopup.show();
483 public void dismissPopups() {
484 if (mNotificationBlamePopup != null) {
485 mNotificationBlamePopup.dismiss();
486 mNotificationBlamePopup = null;
490 public void onHeadsUpDismissed() {
494 public void toggleRecentApps() {
495 int msg = MSG_TOGGLE_RECENTS_PANEL;
496 mHandler.removeMessages(msg);
497 mHandler.sendEmptyMessage(msg);
501 public void preloadRecentApps() {
502 int msg = MSG_PRELOAD_RECENT_APPS;
503 mHandler.removeMessages(msg);
504 mHandler.sendEmptyMessage(msg);
508 public void cancelPreloadRecentApps() {
509 int msg = MSG_CANCEL_PRELOAD_RECENT_APPS;
510 mHandler.removeMessages(msg);
511 mHandler.sendEmptyMessage(msg);
515 public void showSearchPanel() {
516 int msg = MSG_OPEN_SEARCH_PANEL;
517 mHandler.removeMessages(msg);
518 mHandler.sendEmptyMessage(msg);
522 public void hideSearchPanel() {
523 int msg = MSG_CLOSE_SEARCH_PANEL;
524 mHandler.removeMessages(msg);
525 mHandler.sendEmptyMessage(msg);
528 protected abstract WindowManager.LayoutParams getSearchLayoutParams(
529 LayoutParams layoutParams);
531 protected void updateSearchPanel() {
533 boolean visible = false;
534 if (mSearchPanelView != null) {
535 visible = mSearchPanelView.isShowing();
536 mWindowManager.removeView(mSearchPanelView);
539 // Provide SearchPanel with a temporary parent to allow layout params to work.
540 LinearLayout tmpRoot = new LinearLayout(mContext);
541 mSearchPanelView = (SearchPanelView) LayoutInflater.from(mContext).inflate(
542 R.layout.status_bar_search_panel, tmpRoot, false);
543 mSearchPanelView.setOnTouchListener(
544 new TouchOutsideListener(MSG_CLOSE_SEARCH_PANEL, mSearchPanelView));
545 mSearchPanelView.setVisibility(View.GONE);
547 WindowManager.LayoutParams lp = getSearchLayoutParams(mSearchPanelView.getLayoutParams());
549 mWindowManager.addView(mSearchPanelView, lp);
550 mSearchPanelView.setBar(this);
552 mSearchPanelView.show(true, false);
556 protected H createHandler() {
560 static void sendCloseSystemWindows(Context context, String reason) {
561 if (ActivityManagerNative.isSystemReady()) {
563 ActivityManagerNative.getDefault().closeSystemDialogs(reason);
564 } catch (RemoteException e) {
569 protected abstract View getStatusBarView();
571 protected View.OnTouchListener mRecentsPreloadOnTouchListener = new View.OnTouchListener() {
572 // additional optimization when we have software system buttons - start loading the recent
573 // tasks on touch down
575 public boolean onTouch(View v, MotionEvent event) {
576 int action = event.getAction() & MotionEvent.ACTION_MASK;
577 if (action == MotionEvent.ACTION_DOWN) {
578 preloadRecentTasksList();
579 } else if (action == MotionEvent.ACTION_CANCEL) {
580 cancelPreloadingRecentTasksList();
581 } else if (action == MotionEvent.ACTION_UP) {
582 if (!v.isPressed()) {
583 cancelPreloadingRecentTasksList();
591 protected void toggleRecentsActivity() {
592 if (mRecents != null) {
593 sendCloseSystemWindows(mContext, SYSTEM_DIALOG_REASON_RECENT_APPS);
594 mRecents.toggleRecents(mDisplay, mLayoutDirection, getStatusBarView());
598 protected void preloadRecentTasksList() {
599 if (mRecents != null) {
600 mRecents.preloadRecentTasksList();
604 protected void cancelPreloadingRecentTasksList() {
605 if (mRecents != null) {
606 mRecents.cancelPreloadingRecentTasksList();
610 protected void closeRecents() {
611 if (mRecents != null) {
612 mRecents.closeRecents();
616 public abstract void resetHeadsUpDecayTimer();
619 * Save the current "public" (locked and secure) state of the lockscreen.
621 public void setLockscreenPublicMode(boolean publicMode) {
622 mLockscreenPublicMode = publicMode;
625 public boolean isLockscreenPublicMode() {
626 return mLockscreenPublicMode;
630 * Has the given user chosen to allow their private (full) notifications to be shown even
631 * when the lockscreen is in "public" (secure & locked) mode?
633 public boolean userAllowsPrivateNotificationsInPublic(int userHandle) {
634 if (userHandle == UserHandle.USER_ALL) {
638 if (mUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
639 final boolean allowed = 0 != Settings.Secure.getIntForUser(
640 mContext.getContentResolver(),
641 Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle);
642 mUsersAllowingPrivateNotifications.append(userHandle, allowed);
646 return mUsersAllowingPrivateNotifications.get(userHandle);
649 protected class H extends Handler {
650 public void handleMessage(Message m) {
653 case MSG_TOGGLE_RECENTS_PANEL:
654 toggleRecentsActivity();
656 case MSG_CLOSE_RECENTS_PANEL:
659 case MSG_PRELOAD_RECENT_APPS:
660 preloadRecentTasksList();
662 case MSG_CANCEL_PRELOAD_RECENT_APPS:
663 cancelPreloadingRecentTasksList();
665 case MSG_OPEN_SEARCH_PANEL:
666 if (DEBUG) Log.d(TAG, "opening search panel");
667 if (mSearchPanelView != null && mSearchPanelView.isAssistantAvailable()) {
668 mSearchPanelView.show(true, true);
672 case MSG_CLOSE_SEARCH_PANEL:
673 if (DEBUG) Log.d(TAG, "closing search panel");
674 if (mSearchPanelView != null && mSearchPanelView.isShowing()) {
675 mSearchPanelView.show(false, true);
683 public class TouchOutsideListener implements View.OnTouchListener {
685 private StatusBarPanel mPanel;
687 public TouchOutsideListener(int msg, StatusBarPanel panel) {
692 public boolean onTouch(View v, MotionEvent ev) {
693 final int action = ev.getAction();
694 if (action == MotionEvent.ACTION_OUTSIDE
695 || (action == MotionEvent.ACTION_DOWN
696 && !mPanel.isInContentArea((int)ev.getX(), (int)ev.getY()))) {
697 mHandler.removeMessages(mMsg);
698 mHandler.sendEmptyMessage(mMsg);
705 protected void workAroundBadLayerDrawableOpacity(View v) {
708 protected void onHideSearchPanel() {
711 protected void onShowSearchPanel() {
714 public boolean inflateViews(NotificationData.Entry entry, ViewGroup parent) {
715 return inflateViews(entry, parent, false);
718 public boolean inflateViewsForHeadsUp(NotificationData.Entry entry, ViewGroup parent) {
719 return inflateViews(entry, parent, true);
722 public boolean inflateViews(NotificationData.Entry entry, ViewGroup parent, boolean isHeadsUp) {
724 mContext.getResources().getDimensionPixelSize(R.dimen.notification_min_height);
726 mContext.getResources().getDimensionPixelSize(R.dimen.notification_max_height);
727 StatusBarNotification sbn = entry.notification;
728 RemoteViews contentView = sbn.getNotification().contentView;
729 RemoteViews bigContentView = sbn.getNotification().bigContentView;
733 mContext.getResources().getDimensionPixelSize(R.dimen.notification_mid_height);
734 bigContentView = sbn.getNotification().headsUpContentView;
737 if (contentView == null) {
741 Log.v(TAG, "publicNotification: "
742 + sbn.getNotification().publicVersion);
744 Notification publicNotification = sbn.getNotification().publicVersion;
746 // create the row view
747 LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(
748 Context.LAYOUT_INFLATER_SERVICE);
749 ExpandableNotificationRow row = (ExpandableNotificationRow) inflater.inflate(
750 R.layout.status_bar_notification_row, parent, false);
752 // for blaming (see SwipeHelper.setLongPressListener)
753 row.setTag(sbn.getPackageName());
755 workAroundBadLayerDrawableOpacity(row);
756 View vetoButton = updateNotificationVetoButton(row, sbn);
757 vetoButton.setContentDescription(mContext.getString(
758 R.string.accessibility_remove_notification));
760 // NB: the large icon is now handled entirely by the template
762 // bind the click event to the content area
763 ViewGroup content = (ViewGroup)row.findViewById(R.id.container);
764 SizeAdaptiveLayout expanded = (SizeAdaptiveLayout)row.findViewById(R.id.expanded);
765 SizeAdaptiveLayout expandedPublic
766 = (SizeAdaptiveLayout)row.findViewById(R.id.expandedPublic);
768 content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
770 PendingIntent contentIntent = sbn.getNotification().contentIntent;
771 if (contentIntent != null) {
772 final View.OnClickListener listener = makeClicker(contentIntent,
773 sbn.getPackageName(), sbn.getTag(), sbn.getId(), isHeadsUp, sbn.getUserId());
774 content.setOnClickListener(listener);
776 content.setOnClickListener(null);
779 // set up the adaptive layout
780 View contentViewLocal = null;
781 View bigContentViewLocal = null;
783 contentViewLocal = contentView.apply(mContext, expanded,
785 if (bigContentView != null) {
786 bigContentViewLocal = bigContentView.apply(mContext, expanded,
790 catch (RuntimeException e) {
791 final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId());
792 Log.e(TAG, "couldn't inflate view for notification " + ident, e);
796 if (contentViewLocal != null) {
797 contentViewLocal.setIsRootNamespace(true);
798 SizeAdaptiveLayout.LayoutParams params =
799 new SizeAdaptiveLayout.LayoutParams(contentViewLocal.getLayoutParams());
800 params.minHeight = minHeight;
801 params.maxHeight = minHeight;
802 expanded.addView(contentViewLocal, params);
804 if (bigContentViewLocal != null) {
805 bigContentViewLocal.setIsRootNamespace(true);
806 SizeAdaptiveLayout.LayoutParams params =
807 new SizeAdaptiveLayout.LayoutParams(bigContentViewLocal.getLayoutParams());
808 params.minHeight = minHeight+1;
809 params.maxHeight = maxHeight;
810 expanded.addView(bigContentViewLocal, params);
813 PackageManager pm = mContext.getPackageManager();
815 // now the public version
816 View publicViewLocal = null;
817 if (publicNotification != null) {
819 publicViewLocal = publicNotification.contentView.apply(mContext, expandedPublic,
822 if (publicViewLocal != null) {
823 publicViewLocal.setIsRootNamespace(true);
824 SizeAdaptiveLayout.LayoutParams params =
825 new SizeAdaptiveLayout.LayoutParams(publicViewLocal.getLayoutParams());
826 params.minHeight = minHeight;
827 params.maxHeight = minHeight;
828 expandedPublic.addView(publicViewLocal, params);
831 catch (RuntimeException e) {
832 final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId());
833 Log.e(TAG, "couldn't inflate public view for notification " + ident, e);
834 publicViewLocal = null;
838 if (publicViewLocal == null) {
839 // Add a basic notification template
840 publicViewLocal = LayoutInflater.from(mContext).inflate(
841 com.android.internal.R.layout.notification_template_quantum_base,
842 expandedPublic, true);
844 final TextView title = (TextView) publicViewLocal.findViewById(com.android.internal.R.id.title);
846 title.setText(pm.getApplicationLabel(
847 pm.getApplicationInfo(entry.notification.getPackageName(), 0)));
848 } catch (NameNotFoundException e) {
849 title.setText(entry.notification.getPackageName());
852 final ImageView icon = (ImageView) publicViewLocal.findViewById(com.android.internal.R.id.icon);
854 final StatusBarIcon ic = new StatusBarIcon(entry.notification.getPackageName(),
855 entry.notification.getUser(),
856 entry.notification.getNotification().icon,
857 entry.notification.getNotification().iconLevel,
858 entry.notification.getNotification().number,
859 entry.notification.getNotification().tickerText);
861 Drawable iconDrawable = StatusBarIconView.getIcon(mContext, ic);
862 icon.setImageDrawable(iconDrawable);
863 if (mLegacyNotificationUtil.isGrayscale(iconDrawable)) {
864 icon.setBackgroundResource(
865 com.android.internal.R.drawable.notification_icon_legacy_bg_inset);
868 final TextView text = (TextView) publicViewLocal.findViewById(com.android.internal.R.id.text);
869 text.setText("Unlock your device to see this notification.");
871 // TODO: fill out "time" as well
874 row.setDrawingCacheEnabled(true);
876 if (MULTIUSER_DEBUG) {
877 TextView debug = (TextView) row.findViewById(R.id.debug_info);
879 debug.setVisibility(View.VISIBLE);
880 debug.setText("CU " + mCurrentUserId +" NU " + entry.notification.getUserId());
884 entry.row.setHeightRange(mRowMinHeight, mRowMaxHeight);
885 entry.content = content;
886 entry.expanded = contentViewLocal;
887 entry.expandedPublic = publicViewLocal;
888 entry.setBigContentView(bigContentViewLocal);
890 applyLegacyRowBackground(sbn, entry);
895 public NotificationClicker makeClicker(PendingIntent intent, String pkg, String tag,
896 int id, boolean forHun, int userId) {
897 return new NotificationClicker(intent, pkg, tag, id, forHun, userId);
900 protected class NotificationClicker implements View.OnClickListener {
901 private PendingIntent mIntent;
905 private boolean mIsHeadsUp;
908 public NotificationClicker(PendingIntent intent, String pkg, String tag, int id,
909 boolean forHun, int userId) {
918 public void onClick(View v) {
920 // The intent we are sending is for the application, which
921 // won't have permission to immediately start an activity after
922 // the user switches to home. We know it is safe to do at this
923 // point, so make sure new activity switches are now allowed.
924 ActivityManagerNative.getDefault().resumeAppSwitches();
925 // Also, notifications can be launched from the lock screen,
926 // so dismiss the lock screen when the activity starts.
927 ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
928 } catch (RemoteException e) {
931 if (mIntent != null) {
932 int[] pos = new int[2];
933 v.getLocationOnScreen(pos);
934 Intent overlay = new Intent();
935 overlay.setSourceBounds(
936 new Rect(pos[0], pos[1], pos[0]+v.getWidth(), pos[1]+v.getHeight()));
938 mIntent.send(mContext, 0, overlay);
939 } catch (PendingIntent.CanceledException e) {
940 // the stack trace isn't very helpful here. Just log the exception message.
941 Log.w(TAG, "Sending contentIntent failed: " + e);
944 KeyguardTouchDelegate.getInstance(mContext).dismiss();
949 mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
951 mBarService.onNotificationClick(mPkg, mTag, mId, mUserId);
952 } catch (RemoteException ex) {
953 // system process is dead if we're here.
956 // close the shade if it was open
957 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
958 visibilityChanged(false);
963 * The LEDs are turned o)ff when the notification panel is shown, even just a little bit.
964 * This was added last-minute and is inconsistent with the way the rest of the notifications
965 * are handled, because the notification isn't really cancelled. The lights are just
966 * turned off. If any other notifications happen, the lights will turn back on. Steve says
967 * this is what he wants. (see bug 1131461)
969 protected void visibilityChanged(boolean visible) {
970 if (mPanelSlightlyVisible != visible) {
971 mPanelSlightlyVisible = visible;
974 mBarService.onPanelRevealed();
976 mBarService.onPanelHidden();
978 } catch (RemoteException ex) {
979 // Won't fail unless the world has ended.
985 * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
988 * WARNING: this will call back into us. Don't hold any locks.
990 void handleNotificationError(IBinder key, StatusBarNotification n, String message) {
991 removeNotification(key);
993 mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(),
994 n.getInitialPid(), message, n.getUserId());
995 } catch (RemoteException ex) {
1000 protected StatusBarNotification removeNotificationViews(IBinder key) {
1001 NotificationData.Entry entry = mNotificationData.remove(key);
1002 if (entry == null) {
1003 Log.w(TAG, "removeNotification for unknown key: " + key);
1006 // Remove the expanded view.
1007 ViewGroup rowParent = (ViewGroup)entry.row.getParent();
1008 if (rowParent != null) rowParent.removeView(entry.row);
1010 updateNotificationIcons();
1012 return entry.notification;
1015 protected NotificationData.Entry createNotificationViews(IBinder key,
1016 StatusBarNotification notification) {
1018 Log.d(TAG, "createNotificationViews(key=" + key + ", notification=" + notification);
1020 // Construct the icon.
1021 final StatusBarIconView iconView = new StatusBarIconView(mContext,
1022 notification.getPackageName() + "/0x" + Integer.toHexString(notification.getId()),
1023 notification.getNotification());
1024 iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
1026 final StatusBarIcon ic = new StatusBarIcon(notification.getPackageName(),
1027 notification.getUser(),
1028 notification.getNotification().icon,
1029 notification.getNotification().iconLevel,
1030 notification.getNotification().number,
1031 notification.getNotification().tickerText);
1032 if (!iconView.set(ic)) {
1033 handleNotificationError(key, notification, "Couldn't create icon: " + ic);
1036 // Construct the expanded view.
1037 NotificationData.Entry entry = new NotificationData.Entry(key, notification, iconView);
1038 if (!inflateViews(entry, mStackScroller)) {
1039 handleNotificationError(key, notification, "Couldn't expand RemoteViews for: "
1046 protected void addNotificationViews(NotificationData.Entry entry) {
1047 // Add the expanded view and icon.
1048 int pos = mNotificationData.add(entry);
1050 Log.d(TAG, "addNotificationViews: added at " + pos);
1053 updateNotificationIcons();
1056 private void addNotificationViews(IBinder key, StatusBarNotification notification) {
1057 addNotificationViews(createNotificationViews(key, notification));
1061 * @return The number of notifications we show on Keyguard.
1063 protected abstract int getMaxKeyguardNotifications();
1066 * Updates expanded, dimmed and locked states of notification rows.
1068 protected void updateRowStates() {
1069 int maxKeyguardNotifications = getMaxKeyguardNotifications();
1070 mOverflowIconsView.removeAllViews();
1071 int n = mNotificationData.size();
1072 int visibleNotifications = 0;
1073 for (int i = n-1; i >= 0; i--) {
1074 NotificationData.Entry entry = mNotificationData.get(i);
1076 entry.row.setSystemExpanded(false);
1078 if (!entry.row.isUserLocked()) {
1079 boolean top = (i == n-1);
1080 entry.row.setSystemExpanded(top || entry.row.isUserExpanded());
1083 entry.row.setDimmed(mOnKeyguard);
1084 entry.row.setLocked(mOnKeyguard);
1085 boolean showOnKeyguard = shouldShowOnKeyguard(entry.notification);
1086 if (mOnKeyguard && (visibleNotifications >= maxKeyguardNotifications
1087 || !showOnKeyguard)) {
1088 entry.row.setVisibility(View.GONE);
1089 if (showOnKeyguard) {
1090 mOverflowIconsView.addNotification(entry);
1093 entry.row.setVisibility(View.VISIBLE);
1094 visibleNotifications++;
1098 if (mOnKeyguard && mOverflowIconsView.getChildCount() > 0) {
1099 mKeyguardIconOverflowContainer.setVisibility(View.VISIBLE);
1101 mKeyguardIconOverflowContainer.setVisibility(View.GONE);
1105 private boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
1106 return sbn.getNotification().priority >= Notification.PRIORITY_LOW;
1109 protected void setZenMode(int mode) {
1110 if (!isDeviceProvisioned()) return;
1112 updateNotificationIcons();
1115 protected abstract void haltTicker();
1116 protected abstract void setAreThereNotifications();
1117 protected abstract void updateNotificationIcons();
1118 protected abstract void tick(IBinder key, StatusBarNotification n, boolean firstTime);
1119 protected abstract void updateExpandedViewPos(int expandedPosition);
1120 protected abstract int getExpandedViewMaxHeight();
1121 protected abstract boolean shouldDisableNavbarGestures();
1123 protected boolean isTopNotification(ViewGroup parent, NotificationData.Entry entry) {
1124 return parent != null && parent.indexOfChild(entry.row) == 0;
1127 public void updateNotification(IBinder key, StatusBarNotification notification) {
1128 if (DEBUG) Log.d(TAG, "updateNotification(" + key + " -> " + notification + ")");
1130 final NotificationData.Entry oldEntry = mNotificationData.findByKey(key);
1131 if (oldEntry == null) {
1132 Log.w(TAG, "updateNotification for unknown key: " + key);
1136 final StatusBarNotification oldNotification = oldEntry.notification;
1138 // XXX: modify when we do something more intelligent with the two content views
1139 final RemoteViews oldContentView = oldNotification.getNotification().contentView;
1140 final RemoteViews contentView = notification.getNotification().contentView;
1141 final RemoteViews oldBigContentView = oldNotification.getNotification().bigContentView;
1142 final RemoteViews bigContentView = notification.getNotification().bigContentView;
1143 final RemoteViews oldHeadsUpContentView = oldNotification.getNotification().headsUpContentView;
1144 final RemoteViews headsUpContentView = notification.getNotification().headsUpContentView;
1145 final Notification oldPublicNotification = oldNotification.getNotification().publicVersion;
1146 final RemoteViews oldPublicContentView = oldPublicNotification != null
1147 ? oldPublicNotification.contentView : null;
1148 final Notification publicNotification = notification.getNotification().publicVersion;
1149 final RemoteViews publicContentView = publicNotification != null
1150 ? publicNotification.contentView : null;
1153 Log.d(TAG, "old notification: when=" + oldNotification.getNotification().when
1154 + " ongoing=" + oldNotification.isOngoing()
1155 + " expanded=" + oldEntry.expanded
1156 + " contentView=" + oldContentView
1157 + " bigContentView=" + oldBigContentView
1158 + " publicView=" + oldPublicContentView
1159 + " rowParent=" + oldEntry.row.getParent());
1160 Log.d(TAG, "new notification: when=" + notification.getNotification().when
1161 + " ongoing=" + oldNotification.isOngoing()
1162 + " contentView=" + contentView
1163 + " bigContentView=" + bigContentView
1164 + " publicView=" + publicContentView);
1167 // Can we just reapply the RemoteViews in place? If when didn't change, the order
1171 boolean contentsUnchanged = oldEntry.expanded != null
1172 && contentView.getPackage() != null
1173 && oldContentView.getPackage() != null
1174 && oldContentView.getPackage().equals(contentView.getPackage())
1175 && oldContentView.getLayoutId() == contentView.getLayoutId();
1176 // large view may be null
1177 boolean bigContentsUnchanged =
1178 (oldEntry.getBigContentView() == null && bigContentView == null)
1179 || ((oldEntry.getBigContentView() != null && bigContentView != null)
1180 && bigContentView.getPackage() != null
1181 && oldBigContentView.getPackage() != null
1182 && oldBigContentView.getPackage().equals(bigContentView.getPackage())
1183 && oldBigContentView.getLayoutId() == bigContentView.getLayoutId());
1184 boolean headsUpContentsUnchanged =
1185 (oldHeadsUpContentView == null && headsUpContentView == null)
1186 || ((oldHeadsUpContentView != null && headsUpContentView != null)
1187 && headsUpContentView.getPackage() != null
1188 && oldHeadsUpContentView.getPackage() != null
1189 && oldHeadsUpContentView.getPackage().equals(headsUpContentView.getPackage())
1190 && oldHeadsUpContentView.getLayoutId() == headsUpContentView.getLayoutId());
1191 boolean publicUnchanged =
1192 (oldPublicContentView == null && publicContentView == null)
1193 || ((oldPublicContentView != null && publicContentView != null)
1194 && publicContentView.getPackage() != null
1195 && oldPublicContentView.getPackage() != null
1196 && oldPublicContentView.getPackage().equals(publicContentView.getPackage())
1197 && oldPublicContentView.getLayoutId() == publicContentView.getLayoutId());
1199 ViewGroup rowParent = (ViewGroup) oldEntry.row.getParent();
1200 boolean orderUnchanged =
1201 notification.getNotification().when == oldNotification.getNotification().when
1202 && notification.getScore() == oldNotification.getScore();
1203 // score now encompasses/supersedes isOngoing()
1205 boolean updateTicker = notification.getNotification().tickerText != null
1206 && !TextUtils.equals(notification.getNotification().tickerText,
1207 oldEntry.notification.getNotification().tickerText);
1208 boolean isTopAnyway = isTopNotification(rowParent, oldEntry);
1209 if (contentsUnchanged && bigContentsUnchanged && headsUpContentsUnchanged && publicUnchanged
1210 && (orderUnchanged || isTopAnyway)) {
1211 if (DEBUG) Log.d(TAG, "reusing notification for key: " + key);
1212 oldEntry.notification = notification;
1214 updateNotificationViews(oldEntry, notification);
1216 if (ENABLE_HEADS_UP && mInterruptingNotificationEntry != null
1217 && oldNotification == mInterruptingNotificationEntry.notification) {
1218 if (!shouldInterrupt(notification)) {
1219 if (DEBUG) Log.d(TAG, "no longer interrupts!");
1220 mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
1222 if (DEBUG) Log.d(TAG, "updating the current heads up:" + notification);
1223 mInterruptingNotificationEntry.notification = notification;
1224 updateHeadsUpViews(mInterruptingNotificationEntry, notification);
1229 final StatusBarIcon ic = new StatusBarIcon(notification.getPackageName(),
1230 notification.getUser(),
1231 notification.getNotification().icon, notification.getNotification().iconLevel,
1232 notification.getNotification().number,
1233 notification.getNotification().tickerText);
1234 if (!oldEntry.icon.set(ic)) {
1235 handleNotificationError(key, notification, "Couldn't update icon: " + ic);
1240 catch (RuntimeException e) {
1241 // It failed to add cleanly. Log, and remove the view from the panel.
1242 Log.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e);
1243 removeNotificationViews(key);
1244 addNotificationViews(key, notification);
1247 if (DEBUG) Log.d(TAG, "not reusing notification for key: " + key);
1248 if (DEBUG) Log.d(TAG, "contents was " + (contentsUnchanged ? "unchanged" : "changed"));
1249 if (DEBUG) Log.d(TAG, "order was " + (orderUnchanged ? "unchanged" : "changed"));
1250 if (DEBUG) Log.d(TAG, "notification is " + (isTopAnyway ? "top" : "not top"));
1251 removeNotificationViews(key);
1252 addNotificationViews(key, notification); // will also replace the heads up
1253 final NotificationData.Entry newEntry = mNotificationData.findByKey(key);
1254 final boolean userChangedExpansion = oldEntry.row.hasUserChangedExpansion();
1255 if (userChangedExpansion) {
1256 boolean userExpanded = oldEntry.row.isUserExpanded();
1257 newEntry.row.applyExpansionToLayout(userExpanded);
1258 newEntry.row.setUserExpanded(userExpanded);
1262 // Update the veto button accordingly (and as a result, whether this row is
1263 // swipe-dismissable)
1264 updateNotificationVetoButton(oldEntry.row, notification);
1267 boolean isForCurrentUser = notificationIsForCurrentProfiles(notification);
1268 if (DEBUG) Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");
1270 // Restart the ticker if it's still running
1271 if (updateTicker && isForCurrentUser) {
1273 tick(key, notification, false);
1276 // Recalculate the position of the sliding windows and the titles.
1277 setAreThereNotifications();
1278 updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
1281 private void updateNotificationViews(NotificationData.Entry entry,
1282 StatusBarNotification notification) {
1283 updateNotificationViews(entry, notification, false);
1286 private void updateHeadsUpViews(NotificationData.Entry entry,
1287 StatusBarNotification notification) {
1288 updateNotificationViews(entry, notification, true);
1291 private void updateNotificationViews(NotificationData.Entry entry,
1292 StatusBarNotification notification, boolean isHeadsUp) {
1293 final RemoteViews contentView = notification.getNotification().contentView;
1294 final RemoteViews bigContentView = isHeadsUp
1295 ? notification.getNotification().headsUpContentView
1296 : notification.getNotification().bigContentView;
1297 final Notification publicVersion = notification.getNotification().publicVersion;
1298 final RemoteViews publicContentView = publicVersion != null ? publicVersion.contentView
1301 // Reapply the RemoteViews
1302 contentView.reapply(mContext, entry.expanded, mOnClickHandler);
1303 if (bigContentView != null && entry.getBigContentView() != null) {
1304 bigContentView.reapply(mContext, entry.getBigContentView(),
1307 if (publicContentView != null && entry.getPublicContentView() != null) {
1308 publicContentView.reapply(mContext, entry.getPublicContentView(), mOnClickHandler);
1310 // update the contentIntent
1311 final PendingIntent contentIntent = notification.getNotification().contentIntent;
1312 if (contentIntent != null) {
1313 final View.OnClickListener listener = makeClicker(contentIntent,
1314 notification.getPackageName(), notification.getTag(), notification.getId(),
1315 isHeadsUp, notification.getUserId());
1316 entry.content.setOnClickListener(listener);
1318 entry.content.setOnClickListener(null);
1322 protected void notifyHeadsUpScreenOn(boolean screenOn) {
1323 if (!screenOn && mInterruptingNotificationEntry != null) {
1324 mHandler.sendEmptyMessage(MSG_ESCALATE_HEADS_UP);
1328 protected boolean shouldInterrupt(StatusBarNotification sbn) {
1329 Notification notification = sbn.getNotification();
1330 // some predicates to make the boolean logic legible
1331 boolean isNoisy = (notification.defaults & Notification.DEFAULT_SOUND) != 0
1332 || (notification.defaults & Notification.DEFAULT_VIBRATE) != 0
1333 || notification.sound != null
1334 || notification.vibrate != null;
1335 boolean isHighPriority = sbn.getScore() >= INTERRUPTION_THRESHOLD;
1336 boolean isFullscreen = notification.fullScreenIntent != null;
1337 boolean hasTicker = mHeadsUpTicker && !TextUtils.isEmpty(notification.tickerText);
1338 boolean isAllowed = notification.extras.getInt(Notification.EXTRA_AS_HEADS_UP,
1339 Notification.HEADS_UP_ALLOWED) != Notification.HEADS_UP_NEVER;
1341 final KeyguardTouchDelegate keyguard = KeyguardTouchDelegate.getInstance(mContext);
1342 boolean interrupt = (isFullscreen || (isHighPriority && (isNoisy || hasTicker)))
1344 && mPowerManager.isScreenOn()
1345 && !keyguard.isShowingAndNotOccluded()
1346 && !keyguard.isInputRestricted();
1348 interrupt = interrupt && !mDreamManager.isDreaming();
1349 } catch (RemoteException e) {
1350 Log.d(TAG, "failed to query dream manager", e);
1352 if (DEBUG) Log.d(TAG, "interrupt: " + interrupt);
1356 // Q: What kinds of notifications should show during setup?
1357 // A: Almost none! Only things coming from the system (package is "android") that also
1358 // have special "kind" tags marking them as relevant for setup (see below).
1359 protected boolean showNotificationEvenIfUnprovisioned(StatusBarNotification sbn) {
1360 return "android".equals(sbn.getPackageName())
1361 && sbn.getNotification().extras.getBoolean(Notification.EXTRA_ALLOW_DURING_SETUP);
1364 public boolean inKeyguardRestrictedInputMode() {
1365 return KeyguardTouchDelegate.getInstance(mContext).isInputRestricted();
1368 public void setInteracting(int barWindow, boolean interacting) {
1369 // hook for subclasses
1372 public void destroy() {
1373 if (mSearchPanelView != null) {
1374 mWindowManager.removeViewImmediate(mSearchPanelView);
1376 mContext.unregisterReceiver(mBroadcastReceiver);