OSDN Git Service

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