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