PhoneStatusBar.java revision 14f717a506a0d22facbec07386b06634e0c6a8ee
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.phone;
18
19
20import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
21import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
22import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
23import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
24import static android.app.StatusBarManager.windowStateToString;
25import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
26import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
27import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
28import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCENT;
29import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
30import static com.android.systemui.statusbar.phone.BarTransitions.MODE_WARNING;
31
32import android.animation.Animator;
33import android.animation.AnimatorListenerAdapter;
34import android.animation.TimeInterpolator;
35import android.app.ActivityManager;
36import android.app.ActivityManagerNative;
37import android.app.IActivityManager;
38import android.app.Notification;
39import android.app.PendingIntent;
40import android.app.StatusBarManager;
41import android.content.BroadcastReceiver;
42import android.content.Context;
43import android.content.Intent;
44import android.content.IntentFilter;
45import android.content.res.Configuration;
46import android.content.res.Resources;
47import android.database.ContentObserver;
48import android.graphics.Bitmap;
49import android.graphics.Canvas;
50import android.graphics.ColorFilter;
51import android.graphics.PixelFormat;
52import android.graphics.Point;
53import android.graphics.PorterDuff;
54import android.graphics.Rect;
55import android.graphics.drawable.ColorDrawable;
56import android.graphics.drawable.Drawable;
57import android.inputmethodservice.InputMethodService;
58import android.media.AudioAttributes;
59import android.media.MediaMetadata;
60import android.media.session.MediaController;
61import android.media.session.MediaSession;
62import android.media.session.MediaSessionManager;
63import android.media.session.PlaybackState;
64import android.os.AsyncTask;
65import android.os.Bundle;
66import android.os.Handler;
67import android.os.IBinder;
68import android.os.Message;
69import android.os.PowerManager;
70import android.os.RemoteException;
71import android.os.SystemClock;
72import android.os.UserHandle;
73import android.provider.Settings;
74import android.service.notification.NotificationListenerService;
75import android.service.notification.NotificationListenerService.RankingMap;
76import android.service.notification.StatusBarNotification;
77import android.util.ArraySet;
78import android.util.DisplayMetrics;
79import android.util.EventLog;
80import android.util.Log;
81import android.view.Display;
82import android.view.Gravity;
83import android.view.HardwareCanvas;
84import android.view.KeyEvent;
85import android.view.LayoutInflater;
86import android.view.MotionEvent;
87import android.view.VelocityTracker;
88import android.view.View;
89import android.view.ViewGroup;
90import android.view.ViewGroup.LayoutParams;
91import android.view.ViewPropertyAnimator;
92import android.view.ViewStub;
93import android.view.ViewTreeObserver;
94import android.view.WindowManager;
95import android.view.accessibility.AccessibilityEvent;
96import android.view.accessibility.AccessibilityManager;
97import android.view.animation.AccelerateDecelerateInterpolator;
98import android.view.animation.AccelerateInterpolator;
99import android.view.animation.Animation;
100import android.view.animation.AnimationUtils;
101import android.view.animation.DecelerateInterpolator;
102import android.view.animation.Interpolator;
103import android.view.animation.LinearInterpolator;
104import android.view.animation.PathInterpolator;
105import android.widget.FrameLayout;
106import android.widget.ImageView;
107import android.widget.LinearLayout;
108import android.widget.TextView;
109
110import com.android.internal.statusbar.StatusBarIcon;
111import com.android.keyguard.KeyguardHostView.OnDismissAction;
112import com.android.keyguard.ViewMediatorCallback;
113import com.android.systemui.BatteryMeterView;
114import com.android.systemui.DemoMode;
115import com.android.systemui.EventLogTags;
116import com.android.systemui.FontSizeUtils;
117import com.android.systemui.R;
118import com.android.systemui.doze.DozeService;
119import com.android.systemui.keyguard.KeyguardViewMediator;
120import com.android.systemui.qs.QSPanel;
121import com.android.systemui.statusbar.ActivatableNotificationView;
122import com.android.systemui.statusbar.BaseStatusBar;
123import com.android.systemui.statusbar.CommandQueue;
124import com.android.systemui.statusbar.DismissView;
125import com.android.systemui.statusbar.DragDownHelper;
126import com.android.systemui.statusbar.EmptyShadeView;
127import com.android.systemui.statusbar.ExpandableNotificationRow;
128import com.android.systemui.statusbar.GestureRecorder;
129import com.android.systemui.statusbar.KeyguardIndicationController;
130import com.android.systemui.statusbar.NotificationData;
131import com.android.systemui.statusbar.NotificationData.Entry;
132import com.android.systemui.statusbar.NotificationOverflowContainer;
133import com.android.systemui.statusbar.SignalClusterView;
134import com.android.systemui.statusbar.SpeedBumpView;
135import com.android.systemui.statusbar.StatusBarIconView;
136import com.android.systemui.statusbar.StatusBarState;
137import com.android.systemui.statusbar.policy.AccessibilityController;
138import com.android.systemui.statusbar.policy.BatteryController;
139import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
140import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
141import com.android.systemui.statusbar.policy.BrightnessMirrorController;
142import com.android.systemui.statusbar.policy.CastControllerImpl;
143import com.android.systemui.statusbar.policy.FlashlightController;
144import com.android.systemui.statusbar.policy.HeadsUpNotificationView;
145import com.android.systemui.statusbar.policy.HotspotControllerImpl;
146import com.android.systemui.statusbar.policy.KeyButtonView;
147import com.android.systemui.statusbar.policy.KeyguardMonitor;
148import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
149import com.android.systemui.statusbar.policy.LocationControllerImpl;
150import com.android.systemui.statusbar.policy.NetworkControllerImpl;
151import com.android.systemui.statusbar.policy.NextAlarmController;
152import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
153import com.android.systemui.statusbar.policy.SecurityControllerImpl;
154import com.android.systemui.statusbar.policy.UserInfoController;
155import com.android.systemui.statusbar.policy.UserSwitcherController;
156import com.android.systemui.statusbar.policy.ZenModeController;
157import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
158import com.android.systemui.statusbar.stack.NotificationStackScrollLayout.OnChildLocationsChangedListener;
159import com.android.systemui.statusbar.stack.StackScrollAlgorithm;
160import com.android.systemui.statusbar.stack.StackScrollState.ViewState;
161import com.android.systemui.volume.VolumeComponent;
162
163import java.io.FileDescriptor;
164import java.io.PrintWriter;
165import java.util.ArrayList;
166import java.util.Collection;
167import java.util.Collections;
168import java.util.List;
169
170public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
171        DragDownHelper.DragDownCallback, ActivityStarter {
172    static final String TAG = "PhoneStatusBar";
173    public static final boolean DEBUG = BaseStatusBar.DEBUG;
174    public static final boolean SPEW = false;
175    public static final boolean DUMPTRUCK = true; // extra dumpsys info
176    public static final boolean DEBUG_GESTURES = false;
177    public static final boolean DEBUG_MEDIA = false;
178    public static final boolean DEBUG_MEDIA_FAKE_ARTWORK = false;
179
180    public static final boolean DEBUG_WINDOW_STATE = false;
181
182    // additional instrumentation for testing purposes; intended to be left on during development
183    public static final boolean CHATTY = DEBUG;
184
185    public static final String ACTION_STATUSBAR_START
186            = "com.android.internal.policy.statusbar.START";
187
188    public static final boolean SHOW_LOCKSCREEN_MEDIA_ARTWORK = true;
189
190    private static final int MSG_OPEN_NOTIFICATION_PANEL = 1000;
191    private static final int MSG_CLOSE_PANELS = 1001;
192    private static final int MSG_OPEN_SETTINGS_PANEL = 1002;
193    // 1020-1040 reserved for BaseStatusBar
194
195    private static final boolean CLOSE_PANEL_WHEN_EMPTIED = true;
196
197    private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10; // see NotificationManagerService
198    private static final int HIDE_ICONS_BELOW_SCORE = Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER;
199
200    private static final int STATUS_OR_NAV_TRANSIENT =
201            View.STATUS_BAR_TRANSIENT | View.NAVIGATION_BAR_TRANSIENT;
202    private static final long AUTOHIDE_TIMEOUT_MS = 3000;
203
204    /** The minimum delay in ms between reports of notification visibility. */
205    private static final int VISIBILITY_REPORT_MIN_DELAY_MS = 500;
206
207    /**
208     * The delay to reset the hint text when the hint animation is finished running.
209     */
210    private static final int HINT_RESET_DELAY_MS = 1200;
211
212    private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
213            .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
214            .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
215            .build();
216
217    public static final int FADE_KEYGUARD_START_DELAY = 100;
218    public static final int FADE_KEYGUARD_DURATION = 300;
219
220    /** Allow some time inbetween the long press for back and recents. */
221    private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200;
222
223    PhoneStatusBarPolicy mIconPolicy;
224
225    // These are no longer handled by the policy, because we need custom strategies for them
226    BluetoothControllerImpl mBluetoothController;
227    SecurityControllerImpl mSecurityController;
228    BatteryController mBatteryController;
229    LocationControllerImpl mLocationController;
230    NetworkControllerImpl mNetworkController;
231    HotspotControllerImpl mHotspotController;
232    RotationLockControllerImpl mRotationLockController;
233    UserInfoController mUserInfoController;
234    ZenModeController mZenModeController;
235    CastControllerImpl mCastController;
236    VolumeComponent mVolumeComponent;
237    KeyguardUserSwitcher mKeyguardUserSwitcher;
238    FlashlightController mFlashlightController;
239    UserSwitcherController mUserSwitcherController;
240    NextAlarmController mNextAlarmController;
241    KeyguardMonitor mKeyguardMonitor;
242    BrightnessMirrorController mBrightnessMirrorController;
243    AccessibilityController mAccessibilityController;
244
245    int mNaturalBarHeight = -1;
246    int mIconSize = -1;
247    int mIconHPadding = -1;
248    Display mDisplay;
249    Point mCurrentDisplaySize = new Point();
250
251    StatusBarWindowView mStatusBarWindow;
252    PhoneStatusBarView mStatusBarView;
253    private int mStatusBarWindowState = WINDOW_STATE_SHOWING;
254    private StatusBarWindowManager mStatusBarWindowManager;
255    private UnlockMethodCache mUnlockMethodCache;
256    private DozeServiceHost mDozeServiceHost;
257
258    int mPixelFormat;
259    Object mQueueLock = new Object();
260
261    // viewgroup containing the normal contents of the statusbar
262    LinearLayout mStatusBarContents;
263
264    // right-hand icons
265    LinearLayout mSystemIconArea;
266    LinearLayout mSystemIcons;
267
268    // left-hand icons
269    LinearLayout mStatusIcons;
270    LinearLayout mStatusIconsKeyguard;
271
272    // the icons themselves
273    IconMerger mNotificationIcons;
274    View mNotificationIconArea;
275
276    // [+>
277    View mMoreIcon;
278
279    // expanded notifications
280    NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window
281    View mExpandedContents;
282    int mNotificationPanelGravity;
283    int mNotificationPanelMarginBottomPx;
284    float mNotificationPanelMinHeightFrac;
285    TextView mNotificationPanelDebugText;
286
287    // settings
288    View mFlipSettingsView;
289    private QSPanel mQSPanel;
290
291    // top bar
292    StatusBarHeaderView mHeader;
293    KeyguardStatusBarView mKeyguardStatusBar;
294    View mKeyguardStatusView;
295    KeyguardBottomAreaView mKeyguardBottomArea;
296    boolean mLeaveOpenOnKeyguardHide;
297    KeyguardIndicationController mKeyguardIndicationController;
298
299    private boolean mKeyguardFadingAway;
300    private long mKeyguardFadingAwayDelay;
301    private long mKeyguardFadingAwayDuration;
302
303    int mKeyguardMaxNotificationCount;
304
305    // carrier/wifi label
306    private TextView mCarrierLabel;
307    private boolean mCarrierLabelVisible = false;
308    private int mCarrierLabelHeight;
309    private int mStatusBarHeaderHeight;
310
311    private boolean mShowCarrierInPanel = false;
312
313    // position
314    int[] mPositionTmp = new int[2];
315    boolean mExpandedVisible;
316
317    // on-screen navigation buttons
318    private NavigationBarView mNavigationBarView = null;
319    private int mNavigationBarWindowState = WINDOW_STATE_SHOWING;
320
321    // the tracker view
322    int mTrackingPosition; // the position of the top of the tracking view.
323
324    // ticker
325    private boolean mTickerEnabled;
326    private Ticker mTicker;
327    private View mTickerView;
328    private boolean mTicking;
329
330    // Tracking finger for opening/closing.
331    int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore
332    boolean mTracking;
333    VelocityTracker mVelocityTracker;
334
335    int[] mAbsPos = new int[2];
336    ArrayList<Runnable> mPostCollapseRunnables = new ArrayList<>();
337
338    // for disabling the status bar
339    int mDisabled = 0;
340
341    // tracking calls to View.setSystemUiVisibility()
342    int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE;
343
344    DisplayMetrics mDisplayMetrics = new DisplayMetrics();
345
346    // XXX: gesture research
347    private final GestureRecorder mGestureRec = DEBUG_GESTURES
348        ? new GestureRecorder("/sdcard/statusbar_gestures.dat")
349        : null;
350
351    private int mNavigationIconHints = 0;
352
353    // ensure quick settings is disabled until the current user makes it through the setup wizard
354    private boolean mUserSetup = false;
355    private ContentObserver mUserSetupObserver = new ContentObserver(new Handler()) {
356        @Override
357        public void onChange(boolean selfChange) {
358            final boolean userSetup = 0 != Settings.Secure.getIntForUser(
359                    mContext.getContentResolver(),
360                    Settings.Secure.USER_SETUP_COMPLETE,
361                    0 /*default */,
362                    mCurrentUserId);
363            if (MULTIUSER_DEBUG) Log.d(TAG, String.format("User setup changed: " +
364                    "selfChange=%s userSetup=%s mUserSetup=%s",
365                    selfChange, userSetup, mUserSetup));
366
367            if (userSetup != mUserSetup) {
368                mUserSetup = userSetup;
369                if (!mUserSetup && mStatusBarView != null)
370                    animateCollapseQuickSettings();
371            }
372        }
373    };
374
375    final private ContentObserver mHeadsUpObserver = new ContentObserver(mHandler) {
376        @Override
377        public void onChange(boolean selfChange) {
378            boolean wasUsing = mUseHeadsUp;
379            mUseHeadsUp = ENABLE_HEADS_UP && !mDisableNotificationAlerts
380                    && Settings.Global.HEADS_UP_OFF != Settings.Global.getInt(
381                    mContext.getContentResolver(), Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
382                    Settings.Global.HEADS_UP_OFF);
383            mHeadsUpTicker = mUseHeadsUp && 0 != Settings.Global.getInt(
384                    mContext.getContentResolver(), SETTING_HEADS_UP_TICKER, 0);
385            Log.d(TAG, "heads up is " + (mUseHeadsUp ? "enabled" : "disabled"));
386            if (wasUsing != mUseHeadsUp) {
387                if (!mUseHeadsUp) {
388                    Log.d(TAG, "dismissing any existing heads up notification on disable event");
389                    setHeadsUpVisibility(false);
390                    mHeadsUpNotificationView.release();
391                    removeHeadsUpView();
392                } else {
393                    addHeadsUpView();
394                }
395            }
396        }
397    };
398
399    private int mInteractingWindows;
400    private boolean mAutohideSuspended;
401    private int mStatusBarMode;
402    private int mNavigationBarMode;
403    private Boolean mScreenOn;
404
405    private ViewMediatorCallback mKeyguardViewMediatorCallback;
406    private ScrimController mScrimController;
407
408    private final Runnable mAutohide = new Runnable() {
409        @Override
410        public void run() {
411            int requested = mSystemUiVisibility & ~STATUS_OR_NAV_TRANSIENT;
412            if (mSystemUiVisibility != requested) {
413                notifyUiVisibilityChanged(requested);
414            }
415        }};
416
417    private boolean mVisible;
418    private boolean mWaitingForKeyguardExit;
419    private boolean mDozing;
420
421    private Interpolator mLinearOutSlowIn;
422    private Interpolator mLinearInterpolator = new LinearInterpolator();
423    private Interpolator mBackdropInterpolator = new AccelerateDecelerateInterpolator();
424    public static final Interpolator ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
425    public static final Interpolator ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f);
426
427    private FrameLayout mBackdrop;
428    private ImageView mBackdropFront, mBackdropBack;
429
430    private MediaSessionManager mMediaSessionManager;
431    private MediaController mMediaController;
432    private String mMediaNotificationKey;
433    private MediaMetadata mMediaMetadata;
434    private MediaController.Callback mMediaListener
435            = new MediaController.Callback() {
436        @Override
437        public void onPlaybackStateChanged(PlaybackState state) {
438            super.onPlaybackStateChanged(state);
439            if (DEBUG_MEDIA) Log.v(TAG, "DEBUG_MEDIA: onPlaybackStateChanged: " + state);
440        }
441
442        @Override
443        public void onMetadataChanged(MediaMetadata metadata) {
444            super.onMetadataChanged(metadata);
445            if (DEBUG_MEDIA) Log.v(TAG, "DEBUG_MEDIA: onMetadataChanged: " + metadata);
446            mMediaMetadata = metadata;
447            updateMediaMetaData(true);
448        }
449    };
450
451    private final OnChildLocationsChangedListener mOnChildLocationsChangedListener =
452            new OnChildLocationsChangedListener() {
453        @Override
454        public void onChildLocationsChanged(NotificationStackScrollLayout stackScrollLayout) {
455            userActivity();
456        }
457    };
458
459    private int mDisabledUnmodified;
460
461    /** Keys of notifications currently visible to the user. */
462    private final ArraySet<String> mCurrentlyVisibleNotifications = new ArraySet<String>();
463    private long mLastVisibilityReportUptimeMs;
464
465    private final ShadeUpdates mShadeUpdates = new ShadeUpdates();
466
467    private int mDrawCount;
468    private Runnable mLaunchTransitionEndRunnable;
469    private boolean mLaunchTransitionFadingAway;
470    private ExpandableNotificationRow mDraggedDownRow;
471
472    private static final int VISIBLE_LOCATIONS = ViewState.LOCATION_FIRST_CARD
473            | ViewState.LOCATION_TOP_STACK_PEEKING
474            | ViewState.LOCATION_MAIN_AREA
475            | ViewState.LOCATION_BOTTOM_STACK_PEEKING;
476
477    private final OnChildLocationsChangedListener mNotificationLocationsChangedListener =
478            new OnChildLocationsChangedListener() {
479                @Override
480                public void onChildLocationsChanged(
481                        NotificationStackScrollLayout stackScrollLayout) {
482                    if (mHandler.hasCallbacks(mVisibilityReporter)) {
483                        // Visibilities will be reported when the existing
484                        // callback is executed.
485                        return;
486                    }
487                    // Calculate when we're allowed to run the visibility
488                    // reporter. Note that this timestamp might already have
489                    // passed. That's OK, the callback will just be executed
490                    // ASAP.
491                    long nextReportUptimeMs =
492                            mLastVisibilityReportUptimeMs + VISIBILITY_REPORT_MIN_DELAY_MS;
493                    mHandler.postAtTime(mVisibilityReporter, nextReportUptimeMs);
494                }
495            };
496
497    // Tracks notifications currently visible in mNotificationStackScroller and
498    // emits visibility events via NoMan on changes.
499    private final Runnable mVisibilityReporter = new Runnable() {
500        private final ArrayList<String> mTmpNewlyVisibleNotifications = new ArrayList<String>();
501        private final ArrayList<String> mTmpCurrentlyVisibleNotifications = new ArrayList<String>();
502
503        @Override
504        public void run() {
505            mLastVisibilityReportUptimeMs = SystemClock.uptimeMillis();
506
507            // 1. Loop over mNotificationData entries:
508            //   A. Keep list of visible notifications.
509            //   B. Keep list of previously hidden, now visible notifications.
510            // 2. Compute no-longer visible notifications by removing currently
511            //    visible notifications from the set of previously visible
512            //    notifications.
513            // 3. Report newly visible and no-longer visible notifications.
514            // 4. Keep currently visible notifications for next report.
515            ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
516            int N = activeNotifications.size();
517            for (int i = 0; i < N; i++) {
518                Entry entry = activeNotifications.get(i);
519                String key = entry.notification.getKey();
520                boolean previouslyVisible = mCurrentlyVisibleNotifications.contains(key);
521                boolean currentlyVisible =
522                        (mStackScroller.getChildLocation(entry.row) & VISIBLE_LOCATIONS) != 0;
523                if (currentlyVisible) {
524                    // Build new set of visible notifications.
525                    mTmpCurrentlyVisibleNotifications.add(key);
526                }
527                if (!previouslyVisible && currentlyVisible) {
528                    mTmpNewlyVisibleNotifications.add(key);
529                }
530            }
531            ArraySet<String> noLongerVisibleNotifications = mCurrentlyVisibleNotifications;
532            noLongerVisibleNotifications.removeAll(mTmpCurrentlyVisibleNotifications);
533
534            logNotificationVisibilityChanges(
535                    mTmpNewlyVisibleNotifications, noLongerVisibleNotifications);
536
537            mCurrentlyVisibleNotifications.clear();
538            mCurrentlyVisibleNotifications.addAll(mTmpCurrentlyVisibleNotifications);
539
540            mTmpNewlyVisibleNotifications.clear();
541            mTmpCurrentlyVisibleNotifications.clear();
542        }
543    };
544
545    private final View.OnClickListener mOverflowClickListener = new View.OnClickListener() {
546        @Override
547        public void onClick(View v) {
548            goToLockedShade(null);
549        }
550    };
551
552    @Override
553    public void start() {
554        mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
555                .getDefaultDisplay();
556        updateDisplaySize();
557        super.start(); // calls createAndAddWindows()
558
559        mMediaSessionManager
560                = (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
561        // TODO: use MediaSessionManager.SessionListener to hook us up to future updates
562        // in session state
563
564        addNavigationBar();
565
566        // Lastly, call to the icon policy to install/update all the icons.
567        mIconPolicy = new PhoneStatusBarPolicy(mContext, mCastController);
568        mSettingsObserver.onChange(false); // set up
569
570        mHeadsUpObserver.onChange(true); // set up
571        if (ENABLE_HEADS_UP) {
572            mContext.getContentResolver().registerContentObserver(
573                    Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED), true,
574                    mHeadsUpObserver);
575            mContext.getContentResolver().registerContentObserver(
576                    Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true,
577                    mHeadsUpObserver);
578        }
579        mUnlockMethodCache = UnlockMethodCache.getInstance(mContext);
580        startKeyguard();
581
582        mDozeServiceHost = new DozeServiceHost();
583        putComponent(DozeService.Host.class, mDozeServiceHost);
584        putComponent(PhoneStatusBar.class, this);
585
586        setControllerUsers();
587
588        notifyUserAboutHiddenNotifications();
589    }
590
591    // ================================================================================
592    // Constructing the view
593    // ================================================================================
594    protected PhoneStatusBarView makeStatusBarView() {
595        final Context context = mContext;
596
597        Resources res = context.getResources();
598
599        updateDisplaySize(); // populates mDisplayMetrics
600        updateResources();
601
602        mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
603
604        mStatusBarWindow = (StatusBarWindowView) View.inflate(context,
605                R.layout.super_status_bar, null);
606        mStatusBarWindow.mService = this;
607        mStatusBarWindow.setOnTouchListener(new View.OnTouchListener() {
608            @Override
609            public boolean onTouch(View v, MotionEvent event) {
610                checkUserAutohide(v, event);
611                if (event.getAction() == MotionEvent.ACTION_DOWN) {
612                    if (mExpandedVisible) {
613                        animateCollapsePanels();
614                    }
615                }
616                return mStatusBarWindow.onTouchEvent(event);
617            }});
618
619        mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar);
620        mStatusBarView.setBar(this);
621
622        PanelHolder holder = (PanelHolder) mStatusBarWindow.findViewById(R.id.panel_holder);
623        mStatusBarView.setPanelHolder(holder);
624
625        mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById(
626                R.id.notification_panel);
627        mNotificationPanel.setStatusBar(this);
628
629        if (!ActivityManager.isHighEndGfx()) {
630            mStatusBarWindow.setBackground(null);
631            mNotificationPanel.setBackground(new FastColorDrawable(context.getResources().getColor(
632                    R.color.notification_panel_solid_background)));
633        }
634        if (ENABLE_HEADS_UP) {
635            mHeadsUpNotificationView =
636                    (HeadsUpNotificationView) View.inflate(context, R.layout.heads_up, null);
637            mHeadsUpNotificationView.setVisibility(View.GONE);
638            mHeadsUpNotificationView.setBar(this);
639        }
640        if (MULTIUSER_DEBUG) {
641            mNotificationPanelDebugText = (TextView) mNotificationPanel.findViewById(
642                    R.id.header_debug_info);
643            mNotificationPanelDebugText.setVisibility(View.VISIBLE);
644        }
645
646        updateShowSearchHoldoff();
647
648        try {
649            boolean showNav = mWindowManagerService.hasNavigationBar();
650            if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav);
651            if (showNav) {
652                mNavigationBarView =
653                    (NavigationBarView) View.inflate(context, R.layout.navigation_bar, null);
654
655                mNavigationBarView.setDisabledFlags(mDisabled);
656                mNavigationBarView.setBar(this);
657                mNavigationBarView.setOnVerticalChangedListener(
658                        new NavigationBarView.OnVerticalChangedListener() {
659                    @Override
660                    public void onVerticalChanged(boolean isVertical) {
661                        if (mSearchPanelView != null) {
662                            mSearchPanelView.setHorizontal(isVertical);
663                        }
664                        mNotificationPanel.setQsScrimEnabled(!isVertical);
665                    }
666                });
667                mNavigationBarView.setOnTouchListener(new View.OnTouchListener() {
668                    @Override
669                    public boolean onTouch(View v, MotionEvent event) {
670                        checkUserAutohide(v, event);
671                        return false;
672                    }});
673            }
674        } catch (RemoteException ex) {
675            // no window manager? good luck with that
676        }
677
678        // figure out which pixel-format to use for the status bar.
679        mPixelFormat = PixelFormat.OPAQUE;
680
681        mSystemIconArea = (LinearLayout) mStatusBarView.findViewById(R.id.system_icon_area);
682        mSystemIcons = (LinearLayout) mStatusBarView.findViewById(R.id.system_icons);
683        mStatusIcons = (LinearLayout)mStatusBarView.findViewById(R.id.statusIcons);
684        mNotificationIconArea = mStatusBarView.findViewById(R.id.notification_icon_area_inner);
685        mNotificationIcons = (IconMerger)mStatusBarView.findViewById(R.id.notificationIcons);
686        mMoreIcon = mStatusBarView.findViewById(R.id.moreIcon);
687        mNotificationIcons.setOverflowIndicator(mMoreIcon);
688        mStatusBarContents = (LinearLayout)mStatusBarView.findViewById(R.id.status_bar_contents);
689
690        mStackScroller = (NotificationStackScrollLayout) mStatusBarWindow.findViewById(
691                R.id.notification_stack_scroller);
692        mStackScroller.setLongPressListener(getNotificationLongClicker());
693        mStackScroller.setPhoneStatusBar(this);
694
695        mKeyguardIconOverflowContainer =
696                (NotificationOverflowContainer) LayoutInflater.from(mContext).inflate(
697                        R.layout.status_bar_notification_keyguard_overflow, mStackScroller, false);
698        mKeyguardIconOverflowContainer.setOnActivatedListener(this);
699        mKeyguardIconOverflowContainer.setOnClickListener(mOverflowClickListener);
700        mStackScroller.addView(mKeyguardIconOverflowContainer);
701
702        SpeedBumpView speedBump = (SpeedBumpView) LayoutInflater.from(mContext).inflate(
703                        R.layout.status_bar_notification_speed_bump, mStackScroller, false);
704        mStackScroller.setSpeedBumpView(speedBump);
705        mEmptyShadeView = (EmptyShadeView) LayoutInflater.from(mContext).inflate(
706                R.layout.status_bar_no_notifications, mStackScroller, false);
707        mStackScroller.setEmptyShadeView(mEmptyShadeView);
708        mDismissView = (DismissView) LayoutInflater.from(mContext).inflate(
709                R.layout.status_bar_notification_dismiss_all, mStackScroller, false);
710        mDismissView.setOnButtonClickListener(new View.OnClickListener() {
711            @Override
712            public void onClick(View v) {
713                clearAllNotifications();
714            }
715        });
716        mStackScroller.setDismissView(mDismissView);
717        mExpandedContents = mStackScroller;
718
719        mScrimController = new ScrimController(mStatusBarWindow.findViewById(R.id.scrim_behind),
720                mStatusBarWindow.findViewById(R.id.scrim_in_front));
721        mStatusBarView.setScrimController(mScrimController);
722
723        mHeader = (StatusBarHeaderView) mStatusBarWindow.findViewById(R.id.header);
724        mHeader.setActivityStarter(this);
725        mKeyguardStatusBar = (KeyguardStatusBarView) mStatusBarWindow.findViewById(R.id.keyguard_header);
726        mStatusIconsKeyguard = (LinearLayout) mKeyguardStatusBar.findViewById(R.id.statusIcons);
727        mKeyguardStatusView = mStatusBarWindow.findViewById(R.id.keyguard_status_view);
728        mKeyguardBottomArea =
729                (KeyguardBottomAreaView) mStatusBarWindow.findViewById(R.id.keyguard_bottom_area);
730        mKeyguardBottomArea.setActivityStarter(this);
731        mKeyguardIndicationController = new KeyguardIndicationController(mContext,
732                (KeyguardIndicationTextView) mStatusBarWindow.findViewById(
733                        R.id.keyguard_indication_text));
734        mKeyguardBottomArea.setKeyguardIndicationController(mKeyguardIndicationController);
735
736        mTickerEnabled = res.getBoolean(R.bool.enable_ticker);
737        if (mTickerEnabled) {
738            final ViewStub tickerStub = (ViewStub) mStatusBarView.findViewById(R.id.ticker_stub);
739            if (tickerStub != null) {
740                mTickerView = tickerStub.inflate();
741                mTicker = new MyTicker(context, mStatusBarView);
742
743                TickerView tickerView = (TickerView) mStatusBarView.findViewById(R.id.tickerText);
744                tickerView.mTicker = mTicker;
745            }
746        }
747
748        mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
749
750        // set the inital view visibility
751        setAreThereNotifications();
752
753        // Other icons
754        mLocationController = new LocationControllerImpl(mContext); // will post a notification
755        mBatteryController = new BatteryController(mContext);
756        mBatteryController.addStateChangedCallback(new BatteryStateChangeCallback() {
757            @Override
758            public void onPowerSaveChanged() {
759                mHandler.post(mCheckBarModes);
760                if (mDozeServiceHost != null) {
761                    mDozeServiceHost.firePowerSaveChanged(mBatteryController.isPowerSave());
762                }
763            }
764            @Override
765            public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
766                // noop
767            }
768        });
769        mNetworkController = new NetworkControllerImpl(mContext);
770        mHotspotController = new HotspotControllerImpl(mContext);
771        mBluetoothController = new BluetoothControllerImpl(mContext);
772        mSecurityController = new SecurityControllerImpl(mContext);
773        if (mContext.getResources().getBoolean(R.bool.config_showRotationLock)) {
774            mRotationLockController = new RotationLockControllerImpl(mContext);
775        }
776        mUserInfoController = new UserInfoController(mContext);
777        mVolumeComponent = getComponent(VolumeComponent.class);
778        mZenModeController = mVolumeComponent.getZenController();
779        mCastController = new CastControllerImpl(mContext);
780        final SignalClusterView signalCluster =
781                (SignalClusterView) mStatusBarView.findViewById(R.id.signal_cluster);
782        final SignalClusterView signalClusterKeyguard =
783                (SignalClusterView) mKeyguardStatusBar.findViewById(R.id.signal_cluster);
784        final SignalClusterView signalClusterQs =
785                (SignalClusterView) mHeader.findViewById(R.id.signal_cluster);
786        mNetworkController.addSignalCluster(signalCluster);
787        mNetworkController.addSignalCluster(signalClusterKeyguard);
788        mNetworkController.addSignalCluster(signalClusterQs);
789        signalCluster.setNetworkController(mNetworkController);
790        signalClusterKeyguard.setNetworkController(mNetworkController);
791        signalClusterQs.setNetworkController(mNetworkController);
792        final boolean isAPhone = mNetworkController.hasVoiceCallingFeature();
793        if (isAPhone) {
794            mNetworkController.addEmergencyLabelView(mHeader);
795        }
796
797        mCarrierLabel = (TextView)mStatusBarWindow.findViewById(R.id.carrier_label);
798        mShowCarrierInPanel = (mCarrierLabel != null);
799        if (DEBUG) Log.v(TAG, "carrierlabel=" + mCarrierLabel + " show=" + mShowCarrierInPanel);
800        if (mShowCarrierInPanel) {
801            mCarrierLabel.setVisibility(mCarrierLabelVisible ? View.VISIBLE : View.INVISIBLE);
802
803            // for mobile devices, we always show mobile connection info here (SPN/PLMN)
804            // for other devices, we show whatever network is connected
805            if (mNetworkController.hasMobileDataFeature()) {
806                mNetworkController.addMobileLabelView(mCarrierLabel);
807            } else {
808                mNetworkController.addCombinedLabelView(mCarrierLabel);
809            }
810
811            // set up the dynamic hide/show of the label
812            // TODO: uncomment, handle this for the Stack scroller aswell
813//                ((NotificationRowLayout) mStackScroller)
814// .setOnSizeChangedListener(new OnSizeChangedListener() {
815//                @Override
816//                public void onSizeChanged(View view, int w, int h, int oldw, int oldh) {
817//                    updateCarrierLabelVisibility(false);
818        }
819
820        mFlashlightController = new FlashlightController(mContext);
821        mKeyguardBottomArea.setFlashlightController(mFlashlightController);
822        mKeyguardBottomArea.setPhoneStatusBar(this);
823        mAccessibilityController = new AccessibilityController(mContext);
824        mKeyguardBottomArea.setAccessibilityController(mAccessibilityController);
825        mNextAlarmController = new NextAlarmController(mContext);
826        mKeyguardMonitor = new KeyguardMonitor();
827        mUserSwitcherController = new UserSwitcherController(mContext, mKeyguardMonitor);
828
829        mKeyguardUserSwitcher = new KeyguardUserSwitcher(mContext,
830                (ViewStub) mStatusBarWindow.findViewById(R.id.keyguard_user_switcher),
831                mKeyguardStatusBar, mNotificationPanel, mUserSwitcherController);
832
833
834        // Set up the quick settings tile panel
835        mQSPanel = (QSPanel) mStatusBarWindow.findViewById(R.id.quick_settings_panel);
836        if (mQSPanel != null) {
837            final QSTileHost qsh = new QSTileHost(mContext, this,
838                    mBluetoothController, mLocationController, mRotationLockController,
839                    mNetworkController, mZenModeController, mHotspotController,
840                    mCastController, mFlashlightController,
841                    mUserSwitcherController, mKeyguardMonitor,
842                    mSecurityController);
843            mQSPanel.setHost(qsh);
844            mQSPanel.setTiles(qsh.getTiles());
845            mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow);
846            mQSPanel.setBrightnessMirror(mBrightnessMirrorController);
847            mHeader.setQSPanel(mQSPanel);
848            qsh.setCallback(new QSTileHost.Callback() {
849                @Override
850                public void onTilesChanged() {
851                    mQSPanel.setTiles(qsh.getTiles());
852                }
853            });
854        }
855
856        mBackdrop = (FrameLayout) mStatusBarWindow.findViewById(R.id.backdrop);
857        mBackdropFront = (ImageView) mBackdrop.findViewById(R.id.backdrop_front);
858        mBackdropBack = (ImageView) mBackdrop.findViewById(R.id.backdrop_back);
859
860        // User info. Trigger first load.
861        mHeader.setUserInfoController(mUserInfoController);
862        mKeyguardStatusBar.setUserInfoController(mUserInfoController);
863        mUserInfoController.reloadUserInfo();
864
865        mHeader.setBatteryController(mBatteryController);
866        ((BatteryMeterView) mStatusBarView.findViewById(R.id.battery)).setBatteryController(
867                mBatteryController);
868        mKeyguardStatusBar.setBatteryController(mBatteryController);
869        mHeader.setNextAlarmController(mNextAlarmController);
870
871        PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
872        mBroadcastReceiver.onReceive(mContext,
873                new Intent(pm.isScreenOn() ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF));
874
875        // receive broadcasts
876        IntentFilter filter = new IntentFilter();
877        filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
878        filter.addAction(Intent.ACTION_SCREEN_OFF);
879        filter.addAction(Intent.ACTION_SCREEN_ON);
880        if (DEBUG_MEDIA_FAKE_ARTWORK) {
881            filter.addAction("fake_artwork");
882        }
883        filter.addAction(ACTION_DEMO);
884        context.registerReceiver(mBroadcastReceiver, filter);
885
886        // listen for USER_SETUP_COMPLETE setting (per-user)
887        resetUserSetupObserver();
888
889        startGlyphRasterizeHack();
890        return mStatusBarView;
891    }
892
893    private void clearAllNotifications() {
894
895        // animate-swipe all dismissable notifications, then animate the shade closed
896        int numChildren = mStackScroller.getChildCount();
897
898        final ArrayList<View> viewsToHide = new ArrayList<View>(numChildren);
899        for (int i = 0; i < numChildren; i++) {
900            final View child = mStackScroller.getChildAt(i);
901            if (mStackScroller.canChildBeDismissed(child)) {
902                if (child.getVisibility() == View.VISIBLE) {
903                    viewsToHide.add(child);
904                }
905            }
906        }
907        if (viewsToHide.isEmpty()) {
908            animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
909            return;
910        }
911
912        addPostCollapseAction(new Runnable() {
913            @Override
914            public void run() {
915                try {
916                    mBarService.onClearAllNotifications(mCurrentUserId);
917                } catch (Exception ex) { }
918            }
919        });
920
921        performDismissAllAnimations(viewsToHide);
922
923    }
924
925    private void performDismissAllAnimations(ArrayList<View> hideAnimatedList) {
926        Runnable animationFinishAction = new Runnable() {
927            @Override
928            public void run() {
929                mStackScroller.post(new Runnable() {
930                    @Override
931                    public void run() {
932                        mStackScroller.setDismissAllInProgress(false);
933                    }
934                });
935                animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
936            }
937        };
938
939        // let's disable our normal animations
940        mStackScroller.setDismissAllInProgress(true);
941
942        // Decrease the delay for every row we animate to give the sense of
943        // accelerating the swipes
944        int rowDelayDecrement = 10;
945        int currentDelay = 140;
946        int totalDelay = 0;
947        int numItems = hideAnimatedList.size();
948        for (int i = 0; i < numItems; i++) {
949            View view = hideAnimatedList.get(i);
950            Runnable endRunnable = null;
951            if (i == numItems - 1) {
952                endRunnable = animationFinishAction;
953            }
954            mStackScroller.dismissViewAnimated(view, endRunnable, totalDelay, 260);
955            currentDelay = Math.max(50, currentDelay - rowDelayDecrement);
956            totalDelay += currentDelay;
957        }
958    }
959
960    /**
961     * Hack to improve glyph rasterization for scaled text views.
962     */
963    private void startGlyphRasterizeHack() {
964        mStatusBarView.getViewTreeObserver().addOnPreDrawListener(
965                new ViewTreeObserver.OnPreDrawListener() {
966            @Override
967            public boolean onPreDraw() {
968                if (mDrawCount == 1) {
969                    mStatusBarView.getViewTreeObserver().removeOnPreDrawListener(this);
970                    HardwareCanvas.setProperty("extraRasterBucket",
971                            Float.toString(StackScrollAlgorithm.DIMMED_SCALE));
972                    HardwareCanvas.setProperty("extraRasterBucket", Float.toString(
973                            mContext.getResources().getDimensionPixelSize(
974                                    R.dimen.qs_time_collapsed_size)
975                            / mContext.getResources().getDimensionPixelSize(
976                                    R.dimen.qs_time_expanded_size)));
977                }
978                mDrawCount++;
979                return true;
980            }
981        });
982    }
983
984    @Override
985    protected void setZenMode(int mode) {
986        super.setZenMode(mode);
987        if (mIconPolicy != null) {
988            mIconPolicy.setZenMode(mode);
989        }
990    }
991
992    private void startKeyguard() {
993        KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class);
994        mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this,
995                mStatusBarWindow, mStatusBarWindowManager, mScrimController);
996        mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback();
997    }
998
999    @Override
1000    protected View getStatusBarView() {
1001        return mStatusBarView;
1002    }
1003
1004    public StatusBarWindowView getStatusBarWindow() {
1005        return mStatusBarWindow;
1006    }
1007
1008    @Override
1009    protected WindowManager.LayoutParams getSearchLayoutParams(LayoutParams layoutParams) {
1010        boolean opaque = false;
1011        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
1012                LayoutParams.MATCH_PARENT,
1013                LayoutParams.MATCH_PARENT,
1014                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
1015                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
1016                | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
1017                | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
1018                (opaque ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT));
1019        if (ActivityManager.isHighEndGfx()) {
1020            lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
1021        }
1022        lp.gravity = Gravity.BOTTOM | Gravity.START;
1023        lp.setTitle("SearchPanel");
1024        lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
1025        | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
1026        return lp;
1027    }
1028
1029    @Override
1030    protected void updateSearchPanel() {
1031        super.updateSearchPanel();
1032        if (mNavigationBarView != null) {
1033            mNavigationBarView.setDelegateView(mSearchPanelView);
1034        }
1035    }
1036
1037    @Override
1038    public void showSearchPanel() {
1039        super.showSearchPanel();
1040        mHandler.removeCallbacks(mShowSearchPanel);
1041
1042        // we want to freeze the sysui state wherever it is
1043        mSearchPanelView.setSystemUiVisibility(mSystemUiVisibility);
1044
1045        if (mNavigationBarView != null) {
1046            WindowManager.LayoutParams lp =
1047                (android.view.WindowManager.LayoutParams) mNavigationBarView.getLayoutParams();
1048            lp.flags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
1049            mWindowManager.updateViewLayout(mNavigationBarView, lp);
1050        }
1051    }
1052
1053    @Override
1054    public void hideSearchPanel() {
1055        super.hideSearchPanel();
1056        if (mNavigationBarView != null) {
1057            WindowManager.LayoutParams lp =
1058                (android.view.WindowManager.LayoutParams) mNavigationBarView.getLayoutParams();
1059            lp.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
1060            mWindowManager.updateViewLayout(mNavigationBarView, lp);
1061        }
1062    }
1063
1064    public int getStatusBarHeight() {
1065        if (mNaturalBarHeight < 0) {
1066            final Resources res = mContext.getResources();
1067            mNaturalBarHeight =
1068                    res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
1069        }
1070        return mNaturalBarHeight;
1071    }
1072
1073    private View.OnClickListener mRecentsClickListener = new View.OnClickListener() {
1074        public void onClick(View v) {
1075            awakenDreams();
1076            toggleRecentApps();
1077        }
1078    };
1079
1080    private long mLastLockToAppLongPress;
1081    private AccessibilityManager mAccessibilityManager;
1082    private View.OnLongClickListener mLongPressBackRecentsListener =
1083            new View.OnLongClickListener() {
1084        @Override
1085        public boolean onLongClick(View v) {
1086            handleLongPressBackRecents(v);
1087            return true;
1088        }
1089    };
1090
1091    private int mShowSearchHoldoff = 0;
1092    private Runnable mShowSearchPanel = new Runnable() {
1093        public void run() {
1094            showSearchPanel();
1095            awakenDreams();
1096        }
1097    };
1098
1099    View.OnTouchListener mHomeActionListener = new View.OnTouchListener() {
1100        public boolean onTouch(View v, MotionEvent event) {
1101            switch(event.getAction()) {
1102                case MotionEvent.ACTION_DOWN:
1103                if (!shouldDisableNavbarGestures()) {
1104                    mHandler.removeCallbacks(mShowSearchPanel);
1105                    mHandler.postDelayed(mShowSearchPanel, mShowSearchHoldoff);
1106                }
1107            break;
1108
1109            case MotionEvent.ACTION_UP:
1110            case MotionEvent.ACTION_CANCEL:
1111                mHandler.removeCallbacks(mShowSearchPanel);
1112                awakenDreams();
1113            break;
1114        }
1115        return false;
1116        }
1117    };
1118
1119    private void awakenDreams() {
1120        if (mDreamManager != null) {
1121            try {
1122                mDreamManager.awaken();
1123            } catch (RemoteException e) {
1124                // fine, stay asleep then
1125            }
1126        }
1127    }
1128
1129    private void prepareNavigationBarView() {
1130        mNavigationBarView.reorient();
1131
1132        mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
1133        mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPreloadOnTouchListener);
1134        mNavigationBarView.getRecentsButton().setLongClickable(true);
1135        mNavigationBarView.getRecentsButton().setOnLongClickListener(mLongPressBackRecentsListener);
1136        mNavigationBarView.getBackButton().setLongClickable(true);
1137        mNavigationBarView.getBackButton().setOnLongClickListener(mLongPressBackRecentsListener);
1138        mNavigationBarView.getHomeButton().setOnTouchListener(mHomeActionListener);
1139        updateSearchPanel();
1140    }
1141
1142    // For small-screen devices (read: phones) that lack hardware navigation buttons
1143    private void addNavigationBar() {
1144        if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + mNavigationBarView);
1145        if (mNavigationBarView == null) return;
1146
1147        prepareNavigationBarView();
1148
1149        mWindowManager.addView(mNavigationBarView, getNavigationBarLayoutParams());
1150    }
1151
1152    private void repositionNavigationBar() {
1153        if (mNavigationBarView == null || !mNavigationBarView.isAttachedToWindow()) return;
1154
1155        prepareNavigationBarView();
1156
1157        mWindowManager.updateViewLayout(mNavigationBarView, getNavigationBarLayoutParams());
1158    }
1159
1160    private void notifyNavigationBarScreenOn(boolean screenOn) {
1161        if (mNavigationBarView == null) return;
1162        mNavigationBarView.notifyScreenOn(screenOn);
1163    }
1164
1165    private WindowManager.LayoutParams getNavigationBarLayoutParams() {
1166        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
1167                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
1168                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
1169                    0
1170                    | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
1171                    | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
1172                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
1173                    | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
1174                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
1175                PixelFormat.TRANSLUCENT);
1176        // this will allow the navbar to run in an overlay on devices that support this
1177        if (ActivityManager.isHighEndGfx()) {
1178            lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
1179        }
1180
1181        lp.setTitle("NavigationBar");
1182        lp.windowAnimations = 0;
1183        return lp;
1184    }
1185
1186    private void addHeadsUpView() {
1187        int headsUpHeight = mContext.getResources()
1188                .getDimensionPixelSize(R.dimen.heads_up_window_height);
1189        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
1190                LayoutParams.MATCH_PARENT, headsUpHeight,
1191                WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, // above the status bar!
1192                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
1193                    | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
1194                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
1195                    | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
1196                    | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
1197                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
1198                PixelFormat.TRANSLUCENT);
1199        lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
1200        lp.gravity = Gravity.TOP;
1201        lp.setTitle("Heads Up");
1202        lp.packageName = mContext.getPackageName();
1203        lp.windowAnimations = R.style.Animation_StatusBar_HeadsUp;
1204
1205        mWindowManager.addView(mHeadsUpNotificationView, lp);
1206    }
1207
1208    private void removeHeadsUpView() {
1209        mWindowManager.removeView(mHeadsUpNotificationView);
1210    }
1211
1212    public void refreshAllStatusBarIcons() {
1213        refreshAllIconsForLayout(mStatusIcons);
1214        refreshAllIconsForLayout(mStatusIconsKeyguard);
1215        refreshAllIconsForLayout(mNotificationIcons);
1216    }
1217
1218    private void refreshAllIconsForLayout(LinearLayout ll) {
1219        final int count = ll.getChildCount();
1220        for (int n = 0; n < count; n++) {
1221            View child = ll.getChildAt(n);
1222            if (child instanceof StatusBarIconView) {
1223                ((StatusBarIconView) child).updateDrawable();
1224            }
1225        }
1226    }
1227
1228    public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
1229        if (SPEW) Log.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
1230                + " icon=" + icon);
1231        StatusBarIconView view = new StatusBarIconView(mContext, slot, null);
1232        view.set(icon);
1233        mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(
1234                LayoutParams.WRAP_CONTENT, mIconSize));
1235        view = new StatusBarIconView(mContext, slot, null);
1236        view.set(icon);
1237        mStatusIconsKeyguard.addView(view, viewIndex, new LinearLayout.LayoutParams(
1238                LayoutParams.WRAP_CONTENT, mIconSize));
1239    }
1240
1241    public void updateIcon(String slot, int index, int viewIndex,
1242            StatusBarIcon old, StatusBarIcon icon) {
1243        if (SPEW) Log.d(TAG, "updateIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
1244                + " old=" + old + " icon=" + icon);
1245        StatusBarIconView view = (StatusBarIconView) mStatusIcons.getChildAt(viewIndex);
1246        view.set(icon);
1247        view = (StatusBarIconView) mStatusIconsKeyguard.getChildAt(viewIndex);
1248        view.set(icon);
1249    }
1250
1251    public void removeIcon(String slot, int index, int viewIndex) {
1252        if (SPEW) Log.d(TAG, "removeIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex);
1253        mStatusIcons.removeViewAt(viewIndex);
1254        mStatusIconsKeyguard.removeViewAt(viewIndex);
1255    }
1256
1257    public UserHandle getCurrentUserHandle() {
1258        return new UserHandle(mCurrentUserId);
1259    }
1260
1261    @Override
1262    public void addNotification(StatusBarNotification notification, RankingMap ranking) {
1263        if (DEBUG) Log.d(TAG, "addNotification key=" + notification.getKey());
1264        if (mUseHeadsUp && shouldInterrupt(notification)) {
1265            if (DEBUG) Log.d(TAG, "launching notification in heads up mode");
1266            Entry interruptionCandidate = new Entry(notification, null);
1267            ViewGroup holder = mHeadsUpNotificationView.getHolder();
1268            if (inflateViewsForHeadsUp(interruptionCandidate, holder)) {
1269                // 1. Populate mHeadsUpNotificationView
1270                mHeadsUpNotificationView.showNotification(interruptionCandidate);
1271
1272                // do not show the notification in the shade, yet.
1273                return;
1274            }
1275        }
1276
1277        Entry shadeEntry = createNotificationViews(notification);
1278        if (shadeEntry == null) {
1279            return;
1280        }
1281
1282        if (notification.getNotification().fullScreenIntent != null) {
1283            // Stop screensaver if the notification has a full-screen intent.
1284            // (like an incoming phone call)
1285            awakenDreams();
1286
1287            // not immersive & a full-screen alert should be shown
1288            if (DEBUG) Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
1289            try {
1290                notification.getNotification().fullScreenIntent.send();
1291            } catch (PendingIntent.CanceledException e) {
1292            }
1293        } else {
1294            // usual case: status bar visible & not immersive
1295
1296            // show the ticker if there isn't already a heads up
1297            if (mHeadsUpNotificationView.getEntry() == null) {
1298                tick(notification, true);
1299            }
1300        }
1301        addNotificationViews(shadeEntry, ranking);
1302        // Recalculate the position of the sliding windows and the titles.
1303        setAreThereNotifications();
1304        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
1305    }
1306
1307    public void displayNotificationFromHeadsUp(StatusBarNotification notification) {
1308        NotificationData.Entry shadeEntry = createNotificationViews(notification);
1309        if (shadeEntry == null) {
1310            return;
1311        }
1312        shadeEntry.setInterruption();
1313
1314        addNotificationViews(shadeEntry, null);
1315        // Recalculate the position of the sliding windows and the titles.
1316        setAreThereNotifications();
1317        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
1318    }
1319
1320    @Override
1321    public void resetHeadsUpDecayTimer() {
1322        mHandler.removeMessages(MSG_DECAY_HEADS_UP);
1323        if (mUseHeadsUp && mHeadsUpNotificationDecay > 0
1324                && mHeadsUpNotificationView.isClearable()) {
1325            mHandler.sendEmptyMessageDelayed(MSG_DECAY_HEADS_UP, mHeadsUpNotificationDecay);
1326        }
1327    }
1328
1329    @Override
1330    public void scheduleHeadsUpOpen() {
1331        mHandler.sendEmptyMessage(MSG_SHOW_HEADS_UP);
1332    }
1333
1334    @Override
1335    public void scheduleHeadsUpClose() {
1336        mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
1337    }
1338
1339    @Override
1340    public void scheduleHeadsUpEscalation() {
1341        mHandler.sendEmptyMessage(MSG_ESCALATE_HEADS_UP);
1342    }
1343
1344    @Override
1345    protected void updateNotificationRanking(RankingMap ranking) {
1346        mNotificationData.updateRanking(ranking);
1347        updateNotifications();
1348    }
1349
1350    @Override
1351    public void removeNotification(String key, RankingMap ranking) {
1352        if (ENABLE_HEADS_UP && mHeadsUpNotificationView.getEntry() != null
1353                && key.equals(mHeadsUpNotificationView.getEntry().notification.getKey())) {
1354            mHeadsUpNotificationView.clear();
1355        }
1356
1357        StatusBarNotification old = removeNotificationViews(key, ranking);
1358        if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
1359
1360        if (old != null) {
1361            // Cancel the ticker if it's still running
1362            if (mTickerEnabled) {
1363                mTicker.removeEntry(old);
1364            }
1365
1366            // Recalculate the position of the sliding windows and the titles.
1367            updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
1368
1369            if (CLOSE_PANEL_WHEN_EMPTIED && !hasActiveNotifications()
1370                    && !mNotificationPanel.isTracking() && !mNotificationPanel.isQsExpanded()) {
1371                if (mState == StatusBarState.SHADE) {
1372                    animateCollapsePanels();
1373                } else if (mState == StatusBarState.SHADE_LOCKED) {
1374                    goToKeyguard();
1375                }
1376            }
1377        }
1378        setAreThereNotifications();
1379    }
1380
1381    @Override
1382    protected void refreshLayout(int layoutDirection) {
1383        if (mNavigationBarView != null) {
1384            mNavigationBarView.setLayoutDirection(layoutDirection);
1385        }
1386        refreshAllStatusBarIcons();
1387    }
1388
1389    private void updateShowSearchHoldoff() {
1390        mShowSearchHoldoff = mContext.getResources().getInteger(
1391            R.integer.config_show_search_delay);
1392    }
1393
1394    private void updateNotificationShade() {
1395        if (mStackScroller == null) return;
1396
1397        // Do not modify the notifications during collapse.
1398        if (isCollapsing()) {
1399            addPostCollapseAction(new Runnable() {
1400                @Override
1401                public void run() {
1402                    updateNotificationShade();
1403                }
1404            });
1405            return;
1406        }
1407
1408        ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
1409        ArrayList<ExpandableNotificationRow> toShow = new ArrayList<>(activeNotifications.size());
1410        final int N = activeNotifications.size();
1411        for (int i=0; i<N; i++) {
1412            Entry ent = activeNotifications.get(i);
1413            int vis = ent.notification.getNotification().visibility;
1414
1415            // Display public version of the notification if we need to redact.
1416            final boolean hideSensitive =
1417                    !userAllowsPrivateNotificationsInPublic(ent.notification.getUserId());
1418            boolean sensitiveNote = vis == Notification.VISIBILITY_PRIVATE;
1419            boolean sensitivePackage = packageHasVisibilityOverride(ent.notification.getKey());
1420            boolean sensitive = (sensitiveNote && hideSensitive) || sensitivePackage;
1421            boolean showingPublic = sensitive && isLockscreenPublicMode();
1422            ent.row.setSensitive(sensitive);
1423            if (ent.autoRedacted && ent.legacy) {
1424                // TODO: Also fade this? Or, maybe easier (and better), provide a dark redacted form
1425                // for legacy auto redacted notifications.
1426                if (showingPublic) {
1427                    ent.row.setShowingLegacyBackground(false);
1428                } else {
1429                    ent.row.setShowingLegacyBackground(true);
1430                }
1431            }
1432            toShow.add(ent.row);
1433        }
1434
1435        ArrayList<View> toRemove = new ArrayList<View>();
1436        for (int i=0; i< mStackScroller.getChildCount(); i++) {
1437            View child = mStackScroller.getChildAt(i);
1438            if (!toShow.contains(child) && child instanceof ExpandableNotificationRow) {
1439                toRemove.add(child);
1440            }
1441        }
1442
1443        for (View remove : toRemove) {
1444            mStackScroller.removeView(remove);
1445        }
1446        for (int i=0; i<toShow.size(); i++) {
1447            View v = toShow.get(i);
1448            if (v.getParent() == null) {
1449                mStackScroller.addView(v);
1450            }
1451        }
1452
1453        // So after all this work notifications still aren't sorted correctly.
1454        // Let's do that now by advancing through toShow and mStackScroller in
1455        // lock-step, making sure mStackScroller matches what we see in toShow.
1456        int j = 0;
1457        for (int i = 0; i < mStackScroller.getChildCount(); i++) {
1458            View child = mStackScroller.getChildAt(i);
1459            if (!(child instanceof ExpandableNotificationRow)) {
1460                // We don't care about non-notification views.
1461                continue;
1462            }
1463
1464            if (child == toShow.get(j)) {
1465                // Everything is well, advance both lists.
1466                j++;
1467                continue;
1468            }
1469
1470            // Oops, wrong notification at this position. Put the right one
1471            // here and advance both lists.
1472            mStackScroller.changeViewPosition(toShow.get(j), i);
1473            j++;
1474        }
1475        updateRowStates();
1476        updateSpeedbump();
1477        updateClearAll();
1478        updateEmptyShadeView();
1479
1480        mNotificationPanel.setQsExpansionEnabled(isDeviceProvisioned());
1481        mShadeUpdates.check();
1482    }
1483
1484    private boolean packageHasVisibilityOverride(String key) {
1485        return mNotificationData.getVisibilityOverride(key)
1486                != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE;
1487    }
1488
1489    private void updateClearAll() {
1490        boolean showDismissView =
1491                mState != StatusBarState.KEYGUARD &&
1492                mNotificationData.hasActiveClearableNotifications();
1493        mStackScroller.updateDismissView(showDismissView);
1494    }
1495
1496    private void updateEmptyShadeView() {
1497        boolean showEmptyShade =
1498                mState != StatusBarState.KEYGUARD &&
1499                        mNotificationData.getActiveNotifications().size() == 0;
1500        mNotificationPanel.setShadeEmpty(showEmptyShade);
1501    }
1502
1503    private void updateSpeedbump() {
1504        int speedbumpIndex = -1;
1505        int currentIndex = 0;
1506        ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
1507        final int N = activeNotifications.size();
1508        for (int i = 0; i < N; i++) {
1509            Entry entry = activeNotifications.get(i);
1510            if (entry.row.getVisibility() != View.GONE &&
1511                    mNotificationData.isAmbient(entry.key)) {
1512                speedbumpIndex = currentIndex;
1513                break;
1514            }
1515            currentIndex++;
1516        }
1517        mStackScroller.updateSpeedBumpIndex(speedbumpIndex);
1518    }
1519
1520    @Override
1521    protected void updateNotifications() {
1522        // TODO: Move this into updateNotificationIcons()?
1523        if (mNotificationIcons == null) return;
1524
1525        mNotificationData.filterAndSort();
1526
1527        updateNotificationShade();
1528        updateNotificationIcons();
1529    }
1530
1531    private void updateNotificationIcons() {
1532        final LinearLayout.LayoutParams params
1533            = new LinearLayout.LayoutParams(mIconSize + 2*mIconHPadding, mNaturalBarHeight);
1534
1535        ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
1536        final int N = activeNotifications.size();
1537        ArrayList<StatusBarIconView> toShow = new ArrayList<>(N);
1538
1539        // Filter out notifications with low scores.
1540        for (int i = 0; i < N; i++) {
1541            Entry ent = activeNotifications.get(i);
1542            if (ent.notification.getScore() < HIDE_ICONS_BELOW_SCORE &&
1543                    !NotificationData.showNotificationEvenIfUnprovisioned(ent.notification)) {
1544                continue;
1545            }
1546            toShow.add(ent.icon);
1547        }
1548
1549        if (DEBUG) {
1550            Log.d(TAG, "refreshing icons: " + toShow.size() +
1551                    " notifications, mNotificationIcons=" + mNotificationIcons);
1552        }
1553
1554        ArrayList<View> toRemove = new ArrayList<View>();
1555        for (int i=0; i<mNotificationIcons.getChildCount(); i++) {
1556            View child = mNotificationIcons.getChildAt(i);
1557            if (!toShow.contains(child)) {
1558                toRemove.add(child);
1559            }
1560        }
1561
1562        for (View remove : toRemove) {
1563            mNotificationIcons.removeView(remove);
1564        }
1565
1566        for (int i=0; i<toShow.size(); i++) {
1567            View v = toShow.get(i);
1568            if (v.getParent() == null) {
1569                mNotificationIcons.addView(v, i, params);
1570            }
1571        }
1572    }
1573
1574    @Override
1575    protected void updateRowStates() {
1576        super.updateRowStates();
1577        mNotificationPanel.notifyVisibleChildrenChanged();
1578    }
1579
1580    protected void updateCarrierLabelVisibility(boolean force) {
1581        // TODO: Handle this for the notification stack scroller as well
1582        if (!mShowCarrierInPanel) return;
1583        // The idea here is to only show the carrier label when there is enough room to see it,
1584        // i.e. when there aren't enough notifications to fill the panel.
1585        if (SPEW) {
1586            Log.d(TAG, String.format("stackScrollerh=%d scrollh=%d carrierh=%d",
1587                    mStackScroller.getHeight(), mStackScroller.getHeight(),
1588                    mCarrierLabelHeight));
1589        }
1590
1591        // Emergency calls only is shown in the expanded header now.
1592        final boolean emergencyCallsShownElsewhere = true;
1593        final boolean makeVisible =
1594            !(emergencyCallsShownElsewhere && mNetworkController.isEmergencyOnly())
1595            && mStackScroller.getHeight() < (mNotificationPanel.getHeight()
1596                    - mCarrierLabelHeight - mStatusBarHeaderHeight)
1597            && mStackScroller.getVisibility() == View.VISIBLE
1598            && mState != StatusBarState.KEYGUARD;
1599
1600        if (force || mCarrierLabelVisible != makeVisible) {
1601            mCarrierLabelVisible = makeVisible;
1602            if (DEBUG) {
1603                Log.d(TAG, "making carrier label " + (makeVisible?"visible":"invisible"));
1604            }
1605            mCarrierLabel.animate().cancel();
1606            if (makeVisible) {
1607                mCarrierLabel.setVisibility(View.VISIBLE);
1608            }
1609            mCarrierLabel.animate()
1610                .alpha(makeVisible ? 1f : 0f)
1611                //.setStartDelay(makeVisible ? 500 : 0)
1612                //.setDuration(makeVisible ? 750 : 100)
1613                .setDuration(150)
1614                .setListener(makeVisible ? null : new AnimatorListenerAdapter() {
1615                    @Override
1616                    public void onAnimationEnd(Animator animation) {
1617                        if (!mCarrierLabelVisible) { // race
1618                            mCarrierLabel.setVisibility(View.INVISIBLE);
1619                            mCarrierLabel.setAlpha(0f);
1620                        }
1621                    }
1622                })
1623                .start();
1624        }
1625    }
1626
1627    @Override
1628    protected void setAreThereNotifications() {
1629
1630        if (SPEW) {
1631            final boolean clearable = hasActiveNotifications() &&
1632                    mNotificationData.hasActiveClearableNotifications();
1633            Log.d(TAG, "setAreThereNotifications: N=" +
1634                    mNotificationData.getActiveNotifications().size() + " any=" +
1635                    hasActiveNotifications() + " clearable=" + clearable);
1636        }
1637
1638        final View nlo = mStatusBarView.findViewById(R.id.notification_lights_out);
1639        final boolean showDot = hasActiveNotifications() && !areLightsOn();
1640        if (showDot != (nlo.getAlpha() == 1.0f)) {
1641            if (showDot) {
1642                nlo.setAlpha(0f);
1643                nlo.setVisibility(View.VISIBLE);
1644            }
1645            nlo.animate()
1646                .alpha(showDot?1:0)
1647                .setDuration(showDot?750:250)
1648                .setInterpolator(new AccelerateInterpolator(2.0f))
1649                .setListener(showDot ? null : new AnimatorListenerAdapter() {
1650                    @Override
1651                    public void onAnimationEnd(Animator _a) {
1652                        nlo.setVisibility(View.GONE);
1653                    }
1654                })
1655                .start();
1656        }
1657
1658        findAndUpdateMediaNotifications();
1659
1660        updateCarrierLabelVisibility(false);
1661    }
1662
1663    public void findAndUpdateMediaNotifications() {
1664        boolean metaDataChanged = false;
1665
1666        synchronized (mNotificationData) {
1667            ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
1668            final int N = activeNotifications.size();
1669            Entry mediaNotification = null;
1670            MediaController controller = null;
1671            for (int i = 0; i < N; i++) {
1672                final Entry entry = activeNotifications.get(i);
1673                if (isMediaNotification(entry)) {
1674                    final MediaSession.Token token = entry.notification.getNotification().extras
1675                            .getParcelable(Notification.EXTRA_MEDIA_SESSION);
1676                    if (token != null) {
1677                        controller = new MediaController(mContext, token);
1678                        if (controller != null) {
1679                            // we've got a live one, here
1680                            mediaNotification = entry;
1681                        }
1682                    }
1683                }
1684            }
1685
1686            if (mediaNotification == null) {
1687                // Still nothing? OK, let's just look for live media sessions and see if they match
1688                // one of our notifications. This will catch apps that aren't (yet!) using media
1689                // notifications.
1690
1691                if (mMediaSessionManager != null) {
1692                    final List<MediaController> sessions
1693                            = mMediaSessionManager.getActiveSessionsForUser(
1694                                    null,
1695                                    UserHandle.USER_ALL);
1696
1697                    for (MediaController aController : sessions) {
1698                        if (aController == null) continue;
1699                        final PlaybackState state = aController.getPlaybackState();
1700                        if (state == null) continue;
1701                        switch (state.getState()) {
1702                            case PlaybackState.STATE_STOPPED:
1703                            case PlaybackState.STATE_ERROR:
1704                                continue;
1705                            default:
1706                                // now to see if we have one like this
1707                                final String pkg = aController.getPackageName();
1708
1709                                for (int i = 0; i < N; i++) {
1710                                    final Entry entry = activeNotifications.get(i);
1711                                    if (entry.notification.getPackageName().equals(pkg)) {
1712                                        if (DEBUG_MEDIA) {
1713                                            Log.v(TAG, "DEBUG_MEDIA: found controller matching "
1714                                                + entry.notification.getKey());
1715                                        }
1716                                        controller = aController;
1717                                        mediaNotification = entry;
1718                                        break;
1719                                    }
1720                                }
1721                        }
1722                    }
1723                }
1724            }
1725
1726            if (controller != mMediaController) {
1727                // We have a new media session
1728
1729                if (mMediaController != null) {
1730                    // something old was playing
1731                    Log.v(TAG, "DEBUG_MEDIA: Disconnecting from old controller: "
1732                            + mMediaController);
1733                    mMediaController.unregisterCallback(mMediaListener);
1734                }
1735                mMediaController = controller;
1736
1737                if (mMediaController != null) {
1738                    mMediaController.registerCallback(mMediaListener);
1739                    mMediaMetadata = mMediaController.getMetadata();
1740                    if (DEBUG_MEDIA) {
1741                        Log.v(TAG, "DEBUG_MEDIA: insert listener, receive metadata: "
1742                                + mMediaMetadata);
1743                    }
1744
1745                    final String notificationKey = mediaNotification == null
1746                            ? null
1747                            : mediaNotification.notification.getKey();
1748
1749                    if (notificationKey == null || !notificationKey.equals(mMediaNotificationKey)) {
1750                        // we have a new notification!
1751                        if (DEBUG_MEDIA) {
1752                            Log.v(TAG, "DEBUG_MEDIA: Found new media notification: key="
1753                                    + notificationKey + " controller=" + controller);
1754                        }
1755                        mMediaNotificationKey = notificationKey;
1756                    }
1757                } else {
1758                    mMediaMetadata = null;
1759                    mMediaNotificationKey = null;
1760                }
1761
1762                metaDataChanged = true;
1763            } else {
1764                // Media session unchanged
1765
1766                if (DEBUG_MEDIA) {
1767                    Log.v(TAG, "DEBUG_MEDIA: Continuing media notification: key=" + mMediaNotificationKey);
1768                }
1769            }
1770        }
1771
1772        updateMediaMetaData(metaDataChanged);
1773    }
1774
1775    /**
1776     * Hide the album artwork that is fading out and release its bitmap.
1777     */
1778    private Runnable mHideBackdropFront = new Runnable() {
1779        @Override
1780        public void run() {
1781            if (DEBUG_MEDIA) {
1782                Log.v(TAG, "DEBUG_MEDIA: removing fade layer");
1783            }
1784            mBackdropFront.setVisibility(View.INVISIBLE);
1785            mBackdropFront.animate().cancel();
1786            mBackdropFront.setImageDrawable(null);
1787        }
1788    };
1789
1790    /**
1791     * Refresh or remove lockscreen artwork from media metadata.
1792     */
1793    public void updateMediaMetaData(boolean metaDataChanged) {
1794        if (!SHOW_LOCKSCREEN_MEDIA_ARTWORK) return;
1795
1796        if (mBackdrop == null) return; // called too early
1797
1798        if (DEBUG_MEDIA) {
1799            Log.v(TAG, "DEBUG_MEDIA: updating album art for notification " + mMediaNotificationKey
1800                + " metadata=" + mMediaMetadata
1801                + " metaDataChanged=" + metaDataChanged
1802                + " state=" + mState);
1803        }
1804
1805        Bitmap artworkBitmap = null;
1806        if (mMediaMetadata != null) {
1807            artworkBitmap = mMediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
1808            if (artworkBitmap == null) {
1809                artworkBitmap = mMediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
1810                // might still be null
1811            }
1812        }
1813
1814        final boolean hasArtwork = artworkBitmap != null;
1815
1816        if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK)
1817                && (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)) {
1818            // time to show some art!
1819            if (mBackdrop.getVisibility() != View.VISIBLE) {
1820                mBackdrop.setVisibility(View.VISIBLE);
1821                mBackdrop.animate().alpha(1f);
1822                metaDataChanged = true;
1823                if (DEBUG_MEDIA) {
1824                    Log.v(TAG, "DEBUG_MEDIA: Fading in album artwork");
1825                }
1826            }
1827            if (metaDataChanged) {
1828                if (mBackdropBack.getDrawable() != null) {
1829                    mBackdropFront.setImageDrawable(mBackdropBack.getDrawable());
1830                    mBackdropFront.setAlpha(1f);
1831                    mBackdropFront.setVisibility(View.VISIBLE);
1832                } else {
1833                    mBackdropFront.setVisibility(View.INVISIBLE);
1834                }
1835
1836                if (DEBUG_MEDIA_FAKE_ARTWORK) {
1837                    final int c = 0xFF000000 | (int)(Math.random() * 0xFFFFFF);
1838                    Log.v(TAG, String.format("DEBUG_MEDIA: setting new color: 0x%08x", c));
1839                    mBackdropBack.setBackgroundColor(0xFFFFFFFF);
1840                    mBackdropBack.setImageDrawable(new ColorDrawable(c));
1841                } else {
1842                    mBackdropBack.setImageBitmap(artworkBitmap);
1843                }
1844
1845                if (mBackdropFront.getVisibility() == View.VISIBLE) {
1846                    if (DEBUG_MEDIA) {
1847                        Log.v(TAG, "DEBUG_MEDIA: Crossfading album artwork from "
1848                                + mBackdropFront.getDrawable()
1849                                + " to "
1850                                + mBackdropBack.getDrawable());
1851                    }
1852                    mBackdropFront.animate()
1853                            .setDuration(250)
1854                            .alpha(0f).withEndAction(mHideBackdropFront);
1855                }
1856            }
1857        } else {
1858            // need to hide the album art, either because we are unlocked or because
1859            // the metadata isn't there to support it
1860            if (mBackdrop.getVisibility() != View.GONE) {
1861                if (DEBUG_MEDIA) {
1862                    Log.v(TAG, "DEBUG_MEDIA: Fading out album artwork");
1863                }
1864                mBackdrop.animate()
1865                        .alpha(0f)
1866                        .setInterpolator(mBackdropInterpolator)
1867                        .setDuration(300)
1868                        .setStartDelay(0)
1869                        .withEndAction(new Runnable() {
1870                            @Override
1871                            public void run() {
1872                                mBackdrop.setVisibility(View.GONE);
1873                                mBackdropFront.animate().cancel();
1874                                mBackdropBack.animate().cancel();
1875                                mHandler.post(mHideBackdropFront);
1876                            }
1877                        });
1878                if (mKeyguardFadingAway) {
1879                    mBackdrop.animate()
1880
1881                            // Make it disappear faster, as the focus should be on the activity behind.
1882                            .setDuration(mKeyguardFadingAwayDuration / 2)
1883                            .setStartDelay(mKeyguardFadingAwayDelay)
1884                            .setInterpolator(mLinearInterpolator)
1885                            .start();
1886                }
1887            }
1888        }
1889    }
1890
1891    public void showClock(boolean show) {
1892        if (mStatusBarView == null) return;
1893        View clock = mStatusBarView.findViewById(R.id.clock);
1894        if (clock != null) {
1895            clock.setVisibility(show ? View.VISIBLE : View.GONE);
1896        }
1897    }
1898
1899    private int adjustDisableFlags(int state) {
1900        if (!mLaunchTransitionFadingAway
1901                && (mExpandedVisible || mBouncerShowing || mWaitingForKeyguardExit)) {
1902            state |= StatusBarManager.DISABLE_NOTIFICATION_ICONS;
1903            state |= StatusBarManager.DISABLE_SYSTEM_INFO;
1904        }
1905        return state;
1906    }
1907
1908    /**
1909     * State is one or more of the DISABLE constants from StatusBarManager.
1910     */
1911    public void disable(int state, boolean animate) {
1912        mDisabledUnmodified = state;
1913        state = adjustDisableFlags(state);
1914        final int old = mDisabled;
1915        final int diff = state ^ old;
1916        mDisabled = state;
1917
1918        if (DEBUG) {
1919            Log.d(TAG, String.format("disable: 0x%08x -> 0x%08x (diff: 0x%08x)",
1920                old, state, diff));
1921        }
1922
1923        StringBuilder flagdbg = new StringBuilder();
1924        flagdbg.append("disable: < ");
1925        flagdbg.append(((state & StatusBarManager.DISABLE_EXPAND) != 0) ? "EXPAND" : "expand");
1926        flagdbg.append(((diff  & StatusBarManager.DISABLE_EXPAND) != 0) ? "* " : " ");
1927        flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "ICONS" : "icons");
1928        flagdbg.append(((diff  & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "* " : " ");
1929        flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "ALERTS" : "alerts");
1930        flagdbg.append(((diff  & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "* " : " ");
1931        flagdbg.append(((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "SYSTEM_INFO" : "system_info");
1932        flagdbg.append(((diff  & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "* " : " ");
1933        flagdbg.append(((state & StatusBarManager.DISABLE_BACK) != 0) ? "BACK" : "back");
1934        flagdbg.append(((diff  & StatusBarManager.DISABLE_BACK) != 0) ? "* " : " ");
1935        flagdbg.append(((state & StatusBarManager.DISABLE_HOME) != 0) ? "HOME" : "home");
1936        flagdbg.append(((diff  & StatusBarManager.DISABLE_HOME) != 0) ? "* " : " ");
1937        flagdbg.append(((state & StatusBarManager.DISABLE_RECENT) != 0) ? "RECENT" : "recent");
1938        flagdbg.append(((diff  & StatusBarManager.DISABLE_RECENT) != 0) ? "* " : " ");
1939        flagdbg.append(((state & StatusBarManager.DISABLE_CLOCK) != 0) ? "CLOCK" : "clock");
1940        flagdbg.append(((diff  & StatusBarManager.DISABLE_CLOCK) != 0) ? "* " : " ");
1941        flagdbg.append(((state & StatusBarManager.DISABLE_SEARCH) != 0) ? "SEARCH" : "search");
1942        flagdbg.append(((diff  & StatusBarManager.DISABLE_SEARCH) != 0) ? "* " : " ");
1943        flagdbg.append(">");
1944        Log.d(TAG, flagdbg.toString());
1945
1946        if ((diff & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
1947            mSystemIconArea.animate().cancel();
1948            if ((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
1949                animateStatusBarHide(mSystemIconArea, animate);
1950            } else {
1951                animateStatusBarShow(mSystemIconArea, animate);
1952            }
1953        }
1954
1955        if ((diff & StatusBarManager.DISABLE_CLOCK) != 0) {
1956            boolean show = (state & StatusBarManager.DISABLE_CLOCK) == 0;
1957            showClock(show);
1958        }
1959        if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
1960            if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {
1961                animateCollapsePanels();
1962            }
1963        }
1964
1965        if ((diff & (StatusBarManager.DISABLE_HOME
1966                        | StatusBarManager.DISABLE_RECENT
1967                        | StatusBarManager.DISABLE_BACK
1968                        | StatusBarManager.DISABLE_SEARCH)) != 0) {
1969            // the nav bar will take care of these
1970            if (mNavigationBarView != null) mNavigationBarView.setDisabledFlags(state);
1971
1972            if ((state & StatusBarManager.DISABLE_RECENT) != 0) {
1973                // close recents if it's visible
1974                mHandler.removeMessages(MSG_HIDE_RECENT_APPS);
1975                mHandler.sendEmptyMessage(MSG_HIDE_RECENT_APPS);
1976            }
1977        }
1978
1979        if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
1980            if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
1981                if (mTicking) {
1982                    haltTicker();
1983                }
1984                animateStatusBarHide(mNotificationIconArea, animate);
1985            } else {
1986                animateStatusBarShow(mNotificationIconArea, animate);
1987            }
1988        }
1989
1990        if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) {
1991            mDisableNotificationAlerts =
1992                    (state & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
1993            mHeadsUpObserver.onChange(true);
1994        }
1995    }
1996
1997    /**
1998     * Animates {@code v}, a view that is part of the status bar, out.
1999     */
2000    private void animateStatusBarHide(final View v, boolean animate) {
2001        v.animate().cancel();
2002        if (!animate) {
2003            v.setAlpha(0f);
2004            v.setVisibility(View.INVISIBLE);
2005            return;
2006        }
2007        v.animate()
2008                .alpha(0f)
2009                .setDuration(160)
2010                .setStartDelay(0)
2011                .setInterpolator(ALPHA_OUT)
2012                .withEndAction(new Runnable() {
2013                    @Override
2014                    public void run() {
2015                        v.setVisibility(View.INVISIBLE);
2016                    }
2017                });
2018    }
2019
2020    /**
2021     * Animates {@code v}, a view that is part of the status bar, in.
2022     */
2023    private void animateStatusBarShow(View v, boolean animate) {
2024        v.animate().cancel();
2025        v.setVisibility(View.VISIBLE);
2026        if (!animate) {
2027            v.setAlpha(1f);
2028            return;
2029        }
2030        v.animate()
2031                .alpha(1f)
2032                .setDuration(320)
2033                .setInterpolator(ALPHA_IN)
2034                .setStartDelay(50)
2035
2036                // We need to clean up any pending end action from animateStatusBarHide if we call
2037                // both hide and show in the same frame before the animation actually gets started.
2038                // cancel() doesn't really remove the end action.
2039                .withEndAction(null);
2040
2041        // Synchronize the motion with the Keyguard fading if necessary.
2042        if (mKeyguardFadingAway) {
2043            v.animate()
2044                    .setDuration(mKeyguardFadingAwayDuration)
2045                    .setInterpolator(mLinearOutSlowIn)
2046                    .setStartDelay(mKeyguardFadingAwayDelay)
2047                    .start();
2048        }
2049    }
2050
2051    @Override
2052    protected BaseStatusBar.H createHandler() {
2053        return new PhoneStatusBar.H();
2054    }
2055
2056    @Override
2057    public void startActivity(Intent intent, boolean dismissShade, boolean afterKeyguardGone) {
2058        startActivityDismissingKeyguard(intent, false, dismissShade, afterKeyguardGone);
2059    }
2060
2061    public ScrimController getScrimController() {
2062        return mScrimController;
2063    }
2064
2065    public void setQsExpanded(boolean expanded) {
2066        mStatusBarWindowManager.setQsExpanded(expanded);
2067    }
2068
2069    public boolean isGoingToNotificationShade() {
2070        return mLeaveOpenOnKeyguardHide;
2071    }
2072
2073    public boolean isQsExpanded() {
2074        return mNotificationPanel.isQsExpanded();
2075    }
2076
2077    public boolean isFalsingThresholdNeeded() {
2078        boolean onKeyguard = getBarState() == StatusBarState.KEYGUARD;
2079        boolean isMethodInSecure = mUnlockMethodCache.isMethodInsecure();
2080        return onKeyguard && isMethodInSecure;
2081    }
2082
2083    @Override  // NotificationData.Environment
2084    public String getCurrentMediaNotificationKey() {
2085        return mMediaNotificationKey;
2086    }
2087
2088    /**
2089     * All changes to the status bar and notifications funnel through here and are batched.
2090     */
2091    private class H extends BaseStatusBar.H {
2092        public void handleMessage(Message m) {
2093            super.handleMessage(m);
2094            switch (m.what) {
2095                case MSG_OPEN_NOTIFICATION_PANEL:
2096                    animateExpandNotificationsPanel();
2097                    break;
2098                case MSG_OPEN_SETTINGS_PANEL:
2099                    animateExpandSettingsPanel();
2100                    break;
2101                case MSG_CLOSE_PANELS:
2102                    animateCollapsePanels();
2103                    break;
2104                case MSG_SHOW_HEADS_UP:
2105                    setHeadsUpVisibility(true);
2106                    break;
2107                case MSG_DECAY_HEADS_UP:
2108                    mHeadsUpNotificationView.release();
2109                    setHeadsUpVisibility(false);
2110                    break;
2111                case MSG_HIDE_HEADS_UP:
2112                    mHeadsUpNotificationView.release();
2113                    setHeadsUpVisibility(false);
2114                    break;
2115                case MSG_ESCALATE_HEADS_UP:
2116                    escalateHeadsUp();
2117                    setHeadsUpVisibility(false);
2118                    break;
2119            }
2120        }
2121    }
2122
2123    /**  if the interrupting notification had a fullscreen intent, fire it now.  */
2124    private void escalateHeadsUp() {
2125        if (mHeadsUpNotificationView.getEntry() != null) {
2126            final StatusBarNotification sbn = mHeadsUpNotificationView.getEntry().notification;
2127            mHeadsUpNotificationView.release();
2128            final Notification notification = sbn.getNotification();
2129            if (notification.fullScreenIntent != null) {
2130                if (DEBUG)
2131                    Log.d(TAG, "converting a heads up to fullScreen");
2132                try {
2133                    notification.fullScreenIntent.send();
2134                } catch (PendingIntent.CanceledException e) {
2135                }
2136            }
2137        }
2138    }
2139
2140    View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() {
2141        public void onFocusChange(View v, boolean hasFocus) {
2142            // Because 'v' is a ViewGroup, all its children will be (un)selected
2143            // too, which allows marqueeing to work.
2144            v.setSelected(hasFocus);
2145        }
2146    };
2147
2148    boolean panelsEnabled() {
2149        return (mDisabled & StatusBarManager.DISABLE_EXPAND) == 0;
2150    }
2151
2152    void makeExpandedVisible(boolean force) {
2153        if (SPEW) Log.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
2154        if (!force && (mExpandedVisible || !panelsEnabled())) {
2155            return;
2156        }
2157
2158        mExpandedVisible = true;
2159        if (mNavigationBarView != null)
2160            mNavigationBarView.setSlippery(true);
2161
2162        updateCarrierLabelVisibility(true);
2163
2164        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
2165
2166        // Expand the window to encompass the full screen in anticipation of the drag.
2167        // This is only possible to do atomically because the status bar is at the top of the screen!
2168        mStatusBarWindowManager.setStatusBarExpanded(true);
2169        mStatusBarView.setFocusable(false);
2170
2171        visibilityChanged(true);
2172        mWaitingForKeyguardExit = false;
2173        disable(mDisabledUnmodified, !force /* animate */);
2174        setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true);
2175    }
2176
2177    public void animateCollapsePanels() {
2178        animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
2179    }
2180
2181    private final Runnable mAnimateCollapsePanels = new Runnable() {
2182        @Override
2183        public void run() {
2184            animateCollapsePanels();
2185        }
2186    };
2187
2188    public void postAnimateCollapsePanels() {
2189        mHandler.post(mAnimateCollapsePanels);
2190    }
2191
2192    public void animateCollapsePanels(int flags) {
2193        animateCollapsePanels(flags, false /* force */);
2194    }
2195
2196    public void animateCollapsePanels(int flags, boolean force) {
2197        if (!force &&
2198                (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)) {
2199            runPostCollapseRunnables();
2200            return;
2201        }
2202        if (SPEW) {
2203            Log.d(TAG, "animateCollapse():"
2204                    + " mExpandedVisible=" + mExpandedVisible
2205                    + " flags=" + flags);
2206        }
2207
2208        if ((flags & CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL) == 0) {
2209            if (!mHandler.hasMessages(MSG_HIDE_RECENT_APPS)) {
2210                mHandler.removeMessages(MSG_HIDE_RECENT_APPS);
2211                mHandler.sendEmptyMessage(MSG_HIDE_RECENT_APPS);
2212            }
2213        }
2214
2215        if ((flags & CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL) == 0) {
2216            mHandler.removeMessages(MSG_CLOSE_SEARCH_PANEL);
2217            mHandler.sendEmptyMessage(MSG_CLOSE_SEARCH_PANEL);
2218        }
2219
2220        if (mStatusBarWindow != null) {
2221            // release focus immediately to kick off focus change transition
2222            mStatusBarWindowManager.setStatusBarFocusable(false);
2223
2224            mStatusBarWindow.cancelExpandHelper();
2225            mStatusBarView.collapseAllPanels(true);
2226        }
2227    }
2228
2229    private void runPostCollapseRunnables() {
2230        int size = mPostCollapseRunnables.size();
2231        for (int i = 0; i < size; i++) {
2232            mPostCollapseRunnables.get(i).run();
2233        }
2234        mPostCollapseRunnables.clear();
2235    }
2236
2237    public ViewPropertyAnimator setVisibilityWhenDone(
2238            final ViewPropertyAnimator a, final View v, final int vis) {
2239        a.setListener(new AnimatorListenerAdapter() {
2240            @Override
2241            public void onAnimationEnd(Animator animation) {
2242                v.setVisibility(vis);
2243                a.setListener(null); // oneshot
2244            }
2245        });
2246        return a;
2247    }
2248
2249    public Animator setVisibilityWhenDone(
2250            final Animator a, final View v, final int vis) {
2251        a.addListener(new AnimatorListenerAdapter() {
2252            @Override
2253            public void onAnimationEnd(Animator animation) {
2254                v.setVisibility(vis);
2255            }
2256        });
2257        return a;
2258    }
2259
2260    public Animator interpolator(TimeInterpolator ti, Animator a) {
2261        a.setInterpolator(ti);
2262        return a;
2263    }
2264
2265    public Animator startDelay(int d, Animator a) {
2266        a.setStartDelay(d);
2267        return a;
2268    }
2269
2270    public Animator start(Animator a) {
2271        a.start();
2272        return a;
2273    }
2274
2275    final TimeInterpolator mAccelerateInterpolator = new AccelerateInterpolator();
2276    final TimeInterpolator mDecelerateInterpolator = new DecelerateInterpolator();
2277    final int FLIP_DURATION_OUT = 125;
2278    final int FLIP_DURATION_IN = 225;
2279    final int FLIP_DURATION = (FLIP_DURATION_IN + FLIP_DURATION_OUT);
2280
2281    Animator mScrollViewAnim, mClearButtonAnim;
2282
2283    @Override
2284    public void animateExpandNotificationsPanel() {
2285        if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible);
2286        if (!panelsEnabled()) {
2287            return ;
2288        }
2289
2290        mNotificationPanel.expand();
2291
2292        if (false) postStartTracing();
2293    }
2294
2295    @Override
2296    public void animateExpandSettingsPanel() {
2297        if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible);
2298        if (!panelsEnabled()) {
2299            return;
2300        }
2301
2302        // Settings are not available in setup
2303        if (!mUserSetup) return;
2304
2305        mNotificationPanel.expand();
2306        mNotificationPanel.openQs();
2307
2308        if (false) postStartTracing();
2309    }
2310
2311    public void animateCollapseQuickSettings() {
2312        mStatusBarView.collapseAllPanels(true);
2313    }
2314
2315    void makeExpandedInvisible() {
2316        if (SPEW) Log.d(TAG, "makeExpandedInvisible: mExpandedVisible=" + mExpandedVisible
2317                + " mExpandedVisible=" + mExpandedVisible);
2318
2319        if (!mExpandedVisible || mStatusBarWindow == null) {
2320            return;
2321        }
2322
2323        // Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868)
2324        mStatusBarView.collapseAllPanels(/*animate=*/ false);
2325
2326        // reset things to their proper state
2327        if (mScrollViewAnim != null) mScrollViewAnim.cancel();
2328        if (mClearButtonAnim != null) mClearButtonAnim.cancel();
2329
2330        mStackScroller.setVisibility(View.VISIBLE);
2331        mNotificationPanel.setVisibility(View.GONE);
2332
2333        setAreThereNotifications(); // show the clear button
2334
2335        mNotificationPanel.closeQs();
2336
2337        mExpandedVisible = false;
2338        if (mNavigationBarView != null)
2339            mNavigationBarView.setSlippery(false);
2340        visibilityChanged(false);
2341
2342        // Shrink the window to the size of the status bar only
2343        mStatusBarWindowManager.setStatusBarExpanded(false);
2344        mStatusBarView.setFocusable(true);
2345
2346        // Close any "App info" popups that might have snuck on-screen
2347        dismissPopups();
2348
2349        runPostCollapseRunnables();
2350        setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
2351        showBouncer();
2352        disable(mDisabledUnmodified, true /* animate */);
2353    }
2354
2355    public boolean interceptTouchEvent(MotionEvent event) {
2356        if (DEBUG_GESTURES) {
2357            if (event.getActionMasked() != MotionEvent.ACTION_MOVE) {
2358                EventLog.writeEvent(EventLogTags.SYSUI_STATUSBAR_TOUCH,
2359                        event.getActionMasked(), (int) event.getX(), (int) event.getY(), mDisabled);
2360            }
2361
2362        }
2363
2364        if (SPEW) {
2365            Log.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled="
2366                + mDisabled + " mTracking=" + mTracking);
2367        } else if (CHATTY) {
2368            if (event.getAction() != MotionEvent.ACTION_MOVE) {
2369                Log.d(TAG, String.format(
2370                            "panel: %s at (%f, %f) mDisabled=0x%08x",
2371                            MotionEvent.actionToString(event.getAction()),
2372                            event.getRawX(), event.getRawY(), mDisabled));
2373            }
2374        }
2375
2376        if (DEBUG_GESTURES) {
2377            mGestureRec.add(event);
2378        }
2379
2380        if (mStatusBarWindowState == WINDOW_STATE_SHOWING) {
2381            final boolean upOrCancel =
2382                    event.getAction() == MotionEvent.ACTION_UP ||
2383                    event.getAction() == MotionEvent.ACTION_CANCEL;
2384            if (upOrCancel && !mExpandedVisible) {
2385                setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
2386            } else {
2387                setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true);
2388            }
2389        }
2390        return false;
2391    }
2392
2393    public GestureRecorder getGestureRecorder() {
2394        return mGestureRec;
2395    }
2396
2397    private void setNavigationIconHints(int hints) {
2398        if (hints == mNavigationIconHints) return;
2399
2400        mNavigationIconHints = hints;
2401
2402        if (mNavigationBarView != null) {
2403            mNavigationBarView.setNavigationIconHints(hints);
2404        }
2405        checkBarModes();
2406    }
2407
2408    @Override // CommandQueue
2409    public void setWindowState(int window, int state) {
2410        boolean showing = state == WINDOW_STATE_SHOWING;
2411        if (mStatusBarWindow != null
2412                && window == StatusBarManager.WINDOW_STATUS_BAR
2413                && mStatusBarWindowState != state) {
2414            mStatusBarWindowState = state;
2415            if (DEBUG_WINDOW_STATE) Log.d(TAG, "Status bar " + windowStateToString(state));
2416            if (!showing) {
2417                mStatusBarView.collapseAllPanels(false);
2418            }
2419        }
2420        if (mNavigationBarView != null
2421                && window == StatusBarManager.WINDOW_NAVIGATION_BAR
2422                && mNavigationBarWindowState != state) {
2423            mNavigationBarWindowState = state;
2424            if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state));
2425        }
2426    }
2427
2428    @Override // CommandQueue
2429    public void buzzBeepBlinked() {
2430        if (mDozeServiceHost != null) {
2431            mDozeServiceHost.fireBuzzBeepBlinked();
2432        }
2433    }
2434
2435    @Override
2436    public void notificationLightOff() {
2437        if (mDozeServiceHost != null) {
2438            mDozeServiceHost.fireNotificationLight(false);
2439        }
2440    }
2441
2442    @Override
2443    public void notificationLightPulse(int argb, int onMillis, int offMillis) {
2444        if (mDozeServiceHost != null) {
2445            mDozeServiceHost.fireNotificationLight(true);
2446        }
2447    }
2448
2449    @Override // CommandQueue
2450    public void setSystemUiVisibility(int vis, int mask) {
2451        final int oldVal = mSystemUiVisibility;
2452        final int newVal = (oldVal&~mask) | (vis&mask);
2453        final int diff = newVal ^ oldVal;
2454        if (DEBUG) Log.d(TAG, String.format(
2455                "setSystemUiVisibility vis=%s mask=%s oldVal=%s newVal=%s diff=%s",
2456                Integer.toHexString(vis), Integer.toHexString(mask),
2457                Integer.toHexString(oldVal), Integer.toHexString(newVal),
2458                Integer.toHexString(diff)));
2459        if (diff != 0) {
2460            // we never set the recents bit via this method, so save the prior state to prevent
2461            // clobbering the bit below
2462            final boolean wasRecentsVisible = (mSystemUiVisibility & View.RECENT_APPS_VISIBLE) > 0;
2463
2464            mSystemUiVisibility = newVal;
2465
2466            // update low profile
2467            if ((diff & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) {
2468                final boolean lightsOut = (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0;
2469                if (lightsOut) {
2470                    animateCollapsePanels();
2471                    if (mTicking) {
2472                        haltTicker();
2473                    }
2474                }
2475
2476                setAreThereNotifications();
2477            }
2478
2479            // update status bar mode
2480            final int sbMode = computeBarMode(oldVal, newVal, mStatusBarView.getBarTransitions(),
2481                    View.STATUS_BAR_TRANSIENT, View.STATUS_BAR_TRANSLUCENT);
2482
2483            // update navigation bar mode
2484            final int nbMode = mNavigationBarView == null ? -1 : computeBarMode(
2485                    oldVal, newVal, mNavigationBarView.getBarTransitions(),
2486                    View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_TRANSLUCENT);
2487            final boolean sbModeChanged = sbMode != -1;
2488            final boolean nbModeChanged = nbMode != -1;
2489            boolean checkBarModes = false;
2490            if (sbModeChanged && sbMode != mStatusBarMode) {
2491                mStatusBarMode = sbMode;
2492                checkBarModes = true;
2493            }
2494            if (nbModeChanged && nbMode != mNavigationBarMode) {
2495                mNavigationBarMode = nbMode;
2496                checkBarModes = true;
2497            }
2498            if (checkBarModes) {
2499                checkBarModes();
2500            }
2501            if (sbModeChanged || nbModeChanged) {
2502                // update transient bar autohide
2503                if (mStatusBarMode == MODE_SEMI_TRANSPARENT || mNavigationBarMode == MODE_SEMI_TRANSPARENT) {
2504                    scheduleAutohide();
2505                } else {
2506                    cancelAutohide();
2507                }
2508            }
2509
2510            // ready to unhide
2511            if ((vis & View.STATUS_BAR_UNHIDE) != 0) {
2512                mSystemUiVisibility &= ~View.STATUS_BAR_UNHIDE;
2513            }
2514            if ((vis & View.NAVIGATION_BAR_UNHIDE) != 0) {
2515                mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE;
2516            }
2517
2518            // restore the recents bit
2519            if (wasRecentsVisible) {
2520                mSystemUiVisibility |= View.RECENT_APPS_VISIBLE;
2521            }
2522
2523            // send updated sysui visibility to window manager
2524            notifyUiVisibilityChanged(mSystemUiVisibility);
2525        }
2526    }
2527
2528    private int computeBarMode(int oldVis, int newVis, BarTransitions transitions,
2529            int transientFlag, int translucentFlag) {
2530        final int oldMode = barMode(oldVis, transientFlag, translucentFlag);
2531        final int newMode = barMode(newVis, transientFlag, translucentFlag);
2532        if (oldMode == newMode) {
2533            return -1; // no mode change
2534        }
2535        return newMode;
2536    }
2537
2538    private int barMode(int vis, int transientFlag, int translucentFlag) {
2539        return (vis & transientFlag) != 0 ? MODE_SEMI_TRANSPARENT
2540                : (vis & translucentFlag) != 0 ? MODE_TRANSLUCENT
2541                : (vis & View.SYSTEM_UI_TRANSPARENT) != 0 ? MODE_TRANSPARENT
2542                : (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0 ? MODE_LIGHTS_OUT
2543                : MODE_OPAQUE;
2544    }
2545
2546    private void checkBarModes() {
2547        if (mDemoMode) return;
2548        checkBarMode(mStatusBarMode, mStatusBarWindowState, mStatusBarView.getBarTransitions());
2549        if (mNavigationBarView != null) {
2550            checkBarMode(mNavigationBarMode,
2551                    mNavigationBarWindowState, mNavigationBarView.getBarTransitions());
2552        }
2553    }
2554
2555    private void checkBarMode(int mode, int windowState, BarTransitions transitions) {
2556        final boolean powerSave = mBatteryController.isPowerSave();
2557        final boolean anim = (mScreenOn == null || mScreenOn) && windowState != WINDOW_STATE_HIDDEN
2558                && !powerSave;
2559        if (powerSave && getBarState() == StatusBarState.SHADE) {
2560            mode = MODE_WARNING;
2561        }
2562        transitions.transitionTo(mode, anim);
2563    }
2564
2565    private void finishBarAnimations() {
2566        mStatusBarView.getBarTransitions().finishAnimations();
2567        if (mNavigationBarView != null) {
2568            mNavigationBarView.getBarTransitions().finishAnimations();
2569        }
2570    }
2571
2572    private final Runnable mCheckBarModes = new Runnable() {
2573        @Override
2574        public void run() {
2575            checkBarModes();
2576        }
2577    };
2578
2579    @Override
2580    public void setInteracting(int barWindow, boolean interacting) {
2581        mInteractingWindows = interacting
2582                ? (mInteractingWindows | barWindow)
2583                : (mInteractingWindows & ~barWindow);
2584        if (mInteractingWindows != 0) {
2585            suspendAutohide();
2586        } else {
2587            resumeSuspendedAutohide();
2588        }
2589        checkBarModes();
2590    }
2591
2592    private void resumeSuspendedAutohide() {
2593        if (mAutohideSuspended) {
2594            scheduleAutohide();
2595            mHandler.postDelayed(mCheckBarModes, 500); // longer than home -> launcher
2596        }
2597    }
2598
2599    private void suspendAutohide() {
2600        mHandler.removeCallbacks(mAutohide);
2601        mHandler.removeCallbacks(mCheckBarModes);
2602        mAutohideSuspended = (mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0;
2603    }
2604
2605    private void cancelAutohide() {
2606        mAutohideSuspended = false;
2607        mHandler.removeCallbacks(mAutohide);
2608    }
2609
2610    private void scheduleAutohide() {
2611        cancelAutohide();
2612        mHandler.postDelayed(mAutohide, AUTOHIDE_TIMEOUT_MS);
2613    }
2614
2615    private void checkUserAutohide(View v, MotionEvent event) {
2616        if ((mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0  // a transient bar is revealed
2617                && event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar
2618                && event.getX() == 0 && event.getY() == 0  // a touch outside both bars
2619                ) {
2620            userAutohide();
2621        }
2622    }
2623
2624    private void userAutohide() {
2625        cancelAutohide();
2626        mHandler.postDelayed(mAutohide, 350); // longer than app gesture -> flag clear
2627    }
2628
2629    private boolean areLightsOn() {
2630        return 0 == (mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE);
2631    }
2632
2633    public void setLightsOn(boolean on) {
2634        Log.v(TAG, "setLightsOn(" + on + ")");
2635        if (on) {
2636            setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE);
2637        } else {
2638            setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE, View.SYSTEM_UI_FLAG_LOW_PROFILE);
2639        }
2640    }
2641
2642    private void notifyUiVisibilityChanged(int vis) {
2643        try {
2644            mWindowManagerService.statusBarVisibilityChanged(vis);
2645        } catch (RemoteException ex) {
2646        }
2647    }
2648
2649    public void topAppWindowChanged(boolean showMenu) {
2650        if (DEBUG) {
2651            Log.d(TAG, (showMenu?"showing":"hiding") + " the MENU button");
2652        }
2653        if (mNavigationBarView != null) {
2654            mNavigationBarView.setMenuVisibility(showMenu);
2655        }
2656
2657        // See above re: lights-out policy for legacy apps.
2658        if (showMenu) setLightsOn(true);
2659    }
2660
2661    @Override
2662    public void setImeWindowStatus(IBinder token, int vis, int backDisposition,
2663            boolean showImeSwitcher) {
2664        boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0;
2665        int flags = mNavigationIconHints;
2666        if ((backDisposition == InputMethodService.BACK_DISPOSITION_WILL_DISMISS) || imeShown) {
2667            flags |= NAVIGATION_HINT_BACK_ALT;
2668        } else {
2669            flags &= ~NAVIGATION_HINT_BACK_ALT;
2670        }
2671        if (showImeSwitcher) {
2672            flags |= NAVIGATION_HINT_IME_SHOWN;
2673        } else {
2674            flags &= ~NAVIGATION_HINT_IME_SHOWN;
2675        }
2676
2677        setNavigationIconHints(flags);
2678    }
2679
2680    @Override
2681    protected void tick(StatusBarNotification n, boolean firstTime) {
2682        if (!mTickerEnabled) return;
2683
2684        // no ticking in lights-out mode
2685        if (!areLightsOn()) return;
2686
2687        // no ticking in Setup
2688        if (!isDeviceProvisioned()) return;
2689
2690        // not for you
2691        if (!isNotificationForCurrentProfiles(n)) return;
2692
2693        // Show the ticker if one is requested. Also don't do this
2694        // until status bar window is attached to the window manager,
2695        // because...  well, what's the point otherwise?  And trying to
2696        // run a ticker without being attached will crash!
2697        if (n.getNotification().tickerText != null && mStatusBarWindow != null
2698                && mStatusBarWindow.getWindowToken() != null) {
2699            if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS
2700                    | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
2701                mTicker.addEntry(n);
2702            }
2703        }
2704    }
2705
2706    private class MyTicker extends Ticker {
2707        MyTicker(Context context, View sb) {
2708            super(context, sb);
2709            if (!mTickerEnabled) {
2710                Log.w(TAG, "MyTicker instantiated with mTickerEnabled=false", new Throwable());
2711            }
2712        }
2713
2714        @Override
2715        public void tickerStarting() {
2716            if (!mTickerEnabled) return;
2717            mTicking = true;
2718            mStatusBarContents.setVisibility(View.GONE);
2719            mTickerView.setVisibility(View.VISIBLE);
2720            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null));
2721            mStatusBarContents.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null));
2722        }
2723
2724        @Override
2725        public void tickerDone() {
2726            if (!mTickerEnabled) return;
2727            mStatusBarContents.setVisibility(View.VISIBLE);
2728            mTickerView.setVisibility(View.GONE);
2729            mStatusBarContents.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null));
2730            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out,
2731                        mTickingDoneListener));
2732        }
2733
2734        public void tickerHalting() {
2735            if (!mTickerEnabled) return;
2736            if (mStatusBarContents.getVisibility() != View.VISIBLE) {
2737                mStatusBarContents.setVisibility(View.VISIBLE);
2738                mStatusBarContents
2739                        .startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
2740            }
2741            mTickerView.setVisibility(View.GONE);
2742            // we do not animate the ticker away at this point, just get rid of it (b/6992707)
2743        }
2744    }
2745
2746    Animation.AnimationListener mTickingDoneListener = new Animation.AnimationListener() {;
2747        public void onAnimationEnd(Animation animation) {
2748            mTicking = false;
2749        }
2750        public void onAnimationRepeat(Animation animation) {
2751        }
2752        public void onAnimationStart(Animation animation) {
2753        }
2754    };
2755
2756    private Animation loadAnim(int id, Animation.AnimationListener listener) {
2757        Animation anim = AnimationUtils.loadAnimation(mContext, id);
2758        if (listener != null) {
2759            anim.setAnimationListener(listener);
2760        }
2761        return anim;
2762    }
2763
2764    public static String viewInfo(View v) {
2765        return "[(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom()
2766                + ") " + v.getWidth() + "x" + v.getHeight() + "]";
2767    }
2768
2769    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2770        synchronized (mQueueLock) {
2771            pw.println("Current Status Bar state:");
2772            pw.println("  mExpandedVisible=" + mExpandedVisible
2773                    + ", mTrackingPosition=" + mTrackingPosition);
2774            pw.println("  mTickerEnabled=" + mTickerEnabled);
2775            if (mTickerEnabled) {
2776                pw.println("  mTicking=" + mTicking);
2777                pw.println("  mTickerView: " + viewInfo(mTickerView));
2778            }
2779            pw.println("  mTracking=" + mTracking);
2780            pw.println("  mDisplayMetrics=" + mDisplayMetrics);
2781            pw.println("  mStackScroller: " + viewInfo(mStackScroller));
2782            pw.println("  mStackScroller: " + viewInfo(mStackScroller)
2783                    + " scroll " + mStackScroller.getScrollX()
2784                    + "," + mStackScroller.getScrollY());
2785        }
2786
2787        pw.print("  mInteractingWindows="); pw.println(mInteractingWindows);
2788        pw.print("  mStatusBarWindowState=");
2789        pw.println(windowStateToString(mStatusBarWindowState));
2790        pw.print("  mStatusBarMode=");
2791        pw.println(BarTransitions.modeToString(mStatusBarMode));
2792        pw.print("  mDozing="); pw.println(mDozing);
2793        pw.print("  mZenMode=");
2794        pw.println(Settings.Global.zenModeToString(mZenMode));
2795        pw.print("  mUseHeadsUp=");
2796        pw.println(mUseHeadsUp);
2797        pw.print("  interrupting package: ");
2798        pw.println(hunStateToString(mHeadsUpNotificationView.getEntry()));
2799        dumpBarTransitions(pw, "mStatusBarView", mStatusBarView.getBarTransitions());
2800        if (mNavigationBarView != null) {
2801            pw.print("  mNavigationBarWindowState=");
2802            pw.println(windowStateToString(mNavigationBarWindowState));
2803            pw.print("  mNavigationBarMode=");
2804            pw.println(BarTransitions.modeToString(mNavigationBarMode));
2805            dumpBarTransitions(pw, "mNavigationBarView", mNavigationBarView.getBarTransitions());
2806        }
2807
2808        pw.print("  mNavigationBarView=");
2809        if (mNavigationBarView == null) {
2810            pw.println("null");
2811        } else {
2812            mNavigationBarView.dump(fd, pw, args);
2813        }
2814
2815        pw.print("  mMediaSessionManager=");
2816        pw.println(mMediaSessionManager);
2817        pw.print("  mMediaNotificationKey=");
2818        pw.println(mMediaNotificationKey);
2819        pw.print("  mMediaController=");
2820        pw.print(mMediaController);
2821        if (mMediaController != null) {
2822            pw.print(" state=" + mMediaController.getPlaybackState());
2823        }
2824        pw.println();
2825        pw.print("  mMediaMetadata=");
2826        pw.print(mMediaMetadata);
2827        if (mMediaMetadata != null) {
2828            pw.print(" title=" + mMediaMetadata.getText(MediaMetadata.METADATA_KEY_TITLE));
2829        }
2830        pw.println();
2831
2832        pw.println("  Panels: ");
2833        if (mNotificationPanel != null) {
2834            pw.println("    mNotificationPanel=" +
2835                mNotificationPanel + " params=" + mNotificationPanel.getLayoutParams().debug(""));
2836            pw.print  ("      ");
2837            mNotificationPanel.dump(fd, pw, args);
2838        }
2839
2840        if (DUMPTRUCK) {
2841            synchronized (mNotificationData) {
2842                mNotificationData.dump(pw, "  ");
2843            }
2844
2845            int N = mStatusIcons.getChildCount();
2846            pw.println("  system icons: " + N);
2847            for (int i=0; i<N; i++) {
2848                StatusBarIconView ic = (StatusBarIconView) mStatusIcons.getChildAt(i);
2849                pw.println("    [" + i + "] icon=" + ic);
2850            }
2851
2852            if (false) {
2853                pw.println("see the logcat for a dump of the views we have created.");
2854                // must happen on ui thread
2855                mHandler.post(new Runnable() {
2856                        public void run() {
2857                            mStatusBarView.getLocationOnScreen(mAbsPos);
2858                            Log.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
2859                                    + ") " + mStatusBarView.getWidth() + "x"
2860                                    + getStatusBarHeight());
2861                            mStatusBarView.debug();
2862                        }
2863                    });
2864            }
2865        }
2866
2867        if (DEBUG_GESTURES) {
2868            pw.print("  status bar gestures: ");
2869            mGestureRec.dump(fd, pw, args);
2870        }
2871
2872        if (mNetworkController != null) {
2873            mNetworkController.dump(fd, pw, args);
2874        }
2875        if (mBluetoothController != null) {
2876            mBluetoothController.dump(fd, pw, args);
2877        }
2878        if (mCastController != null) {
2879            mCastController.dump(fd, pw, args);
2880        }
2881        if (mUserSwitcherController != null) {
2882            mUserSwitcherController.dump(fd, pw, args);
2883        }
2884        if (mBatteryController != null) {
2885            mBatteryController.dump(fd, pw, args);
2886        }
2887        if (mNextAlarmController != null) {
2888            mNextAlarmController.dump(fd, pw, args);
2889        }
2890        if (mSecurityController != null) {
2891            mSecurityController.dump(fd, pw, args);
2892        }
2893    }
2894
2895    private String hunStateToString(Entry entry) {
2896        if (entry == null) return "null";
2897        if (entry.notification == null) return "corrupt";
2898        return entry.notification.getPackageName();
2899    }
2900
2901    private static void dumpBarTransitions(PrintWriter pw, String var, BarTransitions transitions) {
2902        pw.print("  "); pw.print(var); pw.print(".BarTransitions.mMode=");
2903        pw.println(BarTransitions.modeToString(transitions.getMode()));
2904    }
2905
2906    @Override
2907    public void createAndAddWindows() {
2908        addStatusBarWindow();
2909    }
2910
2911    private void addStatusBarWindow() {
2912        makeStatusBarView();
2913        mStatusBarWindowManager = new StatusBarWindowManager(mContext);
2914        mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
2915    }
2916
2917    static final float saturate(float a) {
2918        return a < 0f ? 0f : (a > 1f ? 1f : a);
2919    }
2920
2921    @Override
2922    public void updateExpandedViewPos(int thingy) {
2923        if (SPEW) Log.v(TAG, "updateExpandedViewPos");
2924
2925        // on larger devices, the notification panel is propped open a bit
2926        mNotificationPanel.setMinimumHeight(
2927                (int)(mNotificationPanelMinHeightFrac * mCurrentDisplaySize.y));
2928
2929        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mNotificationPanel.getLayoutParams();
2930        lp.gravity = mNotificationPanelGravity;
2931        mNotificationPanel.setLayoutParams(lp);
2932
2933        updateCarrierLabelVisibility(false);
2934    }
2935
2936    // called by makeStatusbar and also by PhoneStatusBarView
2937    void updateDisplaySize() {
2938        mDisplay.getMetrics(mDisplayMetrics);
2939        mDisplay.getSize(mCurrentDisplaySize);
2940        if (DEBUG_GESTURES) {
2941            mGestureRec.tag("display",
2942                    String.format("%dx%d", mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
2943        }
2944    }
2945
2946    public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned,
2947            final boolean dismissShade, final boolean afterKeyguardGone) {
2948        if (onlyProvisioned && !isDeviceProvisioned()) return;
2949
2950        final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
2951        dismissKeyguardThenExecute(new OnDismissAction() {
2952            @Override
2953            public boolean onDismiss() {
2954                AsyncTask.execute(new Runnable() {
2955                    public void run() {
2956                        try {
2957                            if (keyguardShowing && !afterKeyguardGone) {
2958                                ActivityManagerNative.getDefault()
2959                                        .keyguardWaitingForActivityDrawn();
2960                            }
2961                            intent.setFlags(
2962                                    Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
2963                            mContext.startActivityAsUser(
2964                                    intent, new UserHandle(UserHandle.USER_CURRENT));
2965                            overrideActivityPendingAppTransition(
2966                                    keyguardShowing && !afterKeyguardGone);
2967                        } catch (RemoteException e) {
2968                        }
2969                    }
2970                });
2971                if (dismissShade) {
2972                    animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */);
2973                }
2974                return true;
2975            }
2976        }, afterKeyguardGone);
2977    }
2978
2979    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
2980        public void onReceive(Context context, Intent intent) {
2981            if (DEBUG) Log.v(TAG, "onReceive: " + intent);
2982            String action = intent.getAction();
2983            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
2984                int flags = CommandQueue.FLAG_EXCLUDE_NONE;
2985                String reason = intent.getStringExtra("reason");
2986                if (reason != null && reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) {
2987                    flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL;
2988                }
2989                animateCollapsePanels(flags);
2990            }
2991            else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
2992                mScreenOn = false;
2993                notifyNavigationBarScreenOn(false);
2994                notifyHeadsUpScreenOn(false);
2995                finishBarAnimations();
2996                stopNotificationLogging();
2997                resetUserExpandedStates();
2998            }
2999            else if (Intent.ACTION_SCREEN_ON.equals(action)) {
3000                mScreenOn = true;
3001                // work around problem where mDisplay.getRotation() is not stable while screen is off (bug 7086018)
3002                repositionNavigationBar();
3003                notifyNavigationBarScreenOn(true);
3004                startNotificationLoggingIfScreenOnAndVisible();
3005            }
3006            else if (ACTION_DEMO.equals(action)) {
3007                Bundle bundle = intent.getExtras();
3008                if (bundle != null) {
3009                    String command = bundle.getString("command", "").trim().toLowerCase();
3010                    if (command.length() > 0) {
3011                        try {
3012                            dispatchDemoCommand(command, bundle);
3013                        } catch (Throwable t) {
3014                            Log.w(TAG, "Error running demo command, intent=" + intent, t);
3015                        }
3016                    }
3017                }
3018            } else if ("fake_artwork".equals(action)) {
3019                if (DEBUG_MEDIA_FAKE_ARTWORK) {
3020                    updateMediaMetaData(true);
3021                }
3022            }
3023        }
3024    };
3025
3026    private void resetUserExpandedStates() {
3027        ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
3028        final int notificationCount = activeNotifications.size();
3029        for (int i = 0; i < notificationCount; i++) {
3030            NotificationData.Entry entry = activeNotifications.get(i);
3031            if (entry.row != null) {
3032                entry.row.resetUserExpansion();
3033            }
3034        }
3035    }
3036
3037    @Override
3038    protected void dismissKeyguardThenExecute(final OnDismissAction action,
3039            boolean afterKeyguardGone) {
3040        if (mStatusBarKeyguardViewManager.isShowing()) {
3041            if (UnlockMethodCache.getInstance(mContext).isMethodInsecure()
3042                    && mNotificationPanel.isLaunchTransitionRunning() && !afterKeyguardGone) {
3043                action.onDismiss();
3044                mNotificationPanel.setLaunchTransitionEndRunnable(new Runnable() {
3045                    @Override
3046                    public void run() {
3047                        mStatusBarKeyguardViewManager.dismiss();
3048                    }
3049                });
3050            } else {
3051                mStatusBarKeyguardViewManager.dismissWithAction(action, afterKeyguardGone);
3052            }
3053        } else {
3054            action.onDismiss();
3055        }
3056    }
3057
3058    // SystemUIService notifies SystemBars of configuration changes, which then calls down here
3059    @Override
3060    protected void onConfigurationChanged(Configuration newConfig) {
3061        super.onConfigurationChanged(newConfig); // calls refreshLayout
3062
3063        if (DEBUG) {
3064            Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration());
3065        }
3066        updateDisplaySize(); // populates mDisplayMetrics
3067
3068        updateResources();
3069        updateClockSize();
3070        repositionNavigationBar();
3071        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
3072        updateShowSearchHoldoff();
3073        updateRowStates();
3074    }
3075
3076    @Override
3077    public void userSwitched(int newUserId) {
3078        if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId);
3079        animateCollapsePanels();
3080        updateNotifications();
3081        resetUserSetupObserver();
3082        setControllerUsers();
3083    }
3084
3085    private void setControllerUsers() {
3086        if (mZenModeController != null) {
3087            mZenModeController.setUserId(mCurrentUserId);
3088        }
3089    }
3090
3091    private void resetUserSetupObserver() {
3092        mContext.getContentResolver().unregisterContentObserver(mUserSetupObserver);
3093        mUserSetupObserver.onChange(false);
3094        mContext.getContentResolver().registerContentObserver(
3095                Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), true,
3096                mUserSetupObserver,
3097                mCurrentUserId);
3098    }
3099
3100    private void setHeadsUpVisibility(boolean vis) {
3101        if (!ENABLE_HEADS_UP) return;
3102        if (DEBUG) Log.v(TAG, (vis ? "showing" : "hiding") + " heads up window");
3103        EventLog.writeEvent(EventLogTags.SYSUI_HEADS_UP_STATUS,
3104                vis ? mHeadsUpNotificationView.getKey() : "",
3105                vis ? 1 : 0);
3106        mHeadsUpNotificationView.setVisibility(vis ? View.VISIBLE : View.GONE);
3107    }
3108
3109    public void onHeadsUpDismissed() {
3110        mHeadsUpNotificationView.dismiss();
3111    }
3112
3113    /**
3114     * Reload some of our resources when the configuration changes.
3115     *
3116     * We don't reload everything when the configuration changes -- we probably
3117     * should, but getting that smooth is tough.  Someday we'll fix that.  In the
3118     * meantime, just update the things that we know change.
3119     */
3120    void updateResources() {
3121        // Update the quick setting tiles
3122        if (mQSPanel != null) {
3123            mQSPanel.updateResources();
3124        }
3125
3126        loadDimens();
3127        mLinearOutSlowIn = AnimationUtils.loadInterpolator(
3128                mContext, android.R.interpolator.linear_out_slow_in);
3129
3130        if (mNotificationPanel != null) {
3131            mNotificationPanel.updateResources();
3132        }
3133        if (mHeadsUpNotificationView != null) {
3134            mHeadsUpNotificationView.updateResources();
3135        }
3136        if (mBrightnessMirrorController != null) {
3137            mBrightnessMirrorController.updateResources();
3138        }
3139    }
3140
3141    private void updateClockSize() {
3142        if (mStatusBarView == null) return;
3143        TextView clock = (TextView) mStatusBarView.findViewById(R.id.clock);
3144        if (clock != null) {
3145            FontSizeUtils.updateFontSize(clock, R.dimen.status_bar_clock_size);
3146        }
3147    }
3148    protected void loadDimens() {
3149        final Resources res = mContext.getResources();
3150
3151        mNaturalBarHeight = res.getDimensionPixelSize(
3152                com.android.internal.R.dimen.status_bar_height);
3153
3154        int newIconSize = res.getDimensionPixelSize(
3155            com.android.internal.R.dimen.status_bar_icon_size);
3156        int newIconHPadding = res.getDimensionPixelSize(
3157            R.dimen.status_bar_icon_padding);
3158
3159        if (newIconHPadding != mIconHPadding || newIconSize != mIconSize) {
3160//            Log.d(TAG, "size=" + newIconSize + " padding=" + newIconHPadding);
3161            mIconHPadding = newIconHPadding;
3162            mIconSize = newIconSize;
3163            //reloadAllNotificationIcons(); // reload the tray
3164        }
3165
3166        mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
3167
3168        mNotificationPanelGravity = res.getInteger(R.integer.notification_panel_layout_gravity);
3169        if (mNotificationPanelGravity <= 0) {
3170            mNotificationPanelGravity = Gravity.START | Gravity.TOP;
3171        }
3172
3173        mCarrierLabelHeight = res.getDimensionPixelSize(R.dimen.carrier_label_height);
3174        mStatusBarHeaderHeight = res.getDimensionPixelSize(R.dimen.status_bar_header_height);
3175
3176        mNotificationPanelMinHeightFrac = res.getFraction(R.dimen.notification_panel_min_height_frac, 1, 1);
3177        if (mNotificationPanelMinHeightFrac < 0f || mNotificationPanelMinHeightFrac > 1f) {
3178            mNotificationPanelMinHeightFrac = 0f;
3179        }
3180
3181        mHeadsUpNotificationDecay = res.getInteger(R.integer.heads_up_notification_decay);
3182        mRowMinHeight =  res.getDimensionPixelSize(R.dimen.notification_min_height);
3183        mRowMaxHeight =  res.getDimensionPixelSize(R.dimen.notification_max_height);
3184
3185        mKeyguardMaxNotificationCount = res.getInteger(R.integer.keyguard_max_notification_count);
3186
3187        if (DEBUG) Log.v(TAG, "updateResources");
3188    }
3189
3190    // Visibility reporting
3191
3192    @Override
3193    protected void visibilityChanged(boolean visible) {
3194        mVisible = visible;
3195        if (visible) {
3196            startNotificationLoggingIfScreenOnAndVisible();
3197        } else {
3198            stopNotificationLogging();
3199        }
3200        super.visibilityChanged(visible);
3201    }
3202
3203    private void stopNotificationLogging() {
3204        // Report all notifications as invisible and turn down the
3205        // reporter.
3206        if (!mCurrentlyVisibleNotifications.isEmpty()) {
3207            logNotificationVisibilityChanges(
3208                    Collections.<String>emptyList(), mCurrentlyVisibleNotifications);
3209            mCurrentlyVisibleNotifications.clear();
3210        }
3211        mHandler.removeCallbacks(mVisibilityReporter);
3212        mStackScroller.setChildLocationsChangedListener(null);
3213    }
3214
3215    private void startNotificationLoggingIfScreenOnAndVisible() {
3216        if (mVisible && mScreenOn) {
3217            mStackScroller.setChildLocationsChangedListener(mNotificationLocationsChangedListener);
3218            // Some transitions like mScreenOn=false -> mScreenOn=true don't
3219            // cause the scroller to emit child location events. Hence generate
3220            // one ourselves to guarantee that we're reporting visible
3221            // notifications.
3222            // (Note that in cases where the scroller does emit events, this
3223            // additional event doesn't break anything.)
3224            mNotificationLocationsChangedListener.onChildLocationsChanged(mStackScroller);
3225        }
3226    }
3227
3228    private void logNotificationVisibilityChanges(
3229            Collection<String> newlyVisible, Collection<String> noLongerVisible) {
3230        if (newlyVisible.isEmpty() && noLongerVisible.isEmpty()) {
3231            return;
3232        }
3233        String[] newlyVisibleAr = newlyVisible.toArray(new String[newlyVisible.size()]);
3234        String[] noLongerVisibleAr = noLongerVisible.toArray(new String[noLongerVisible.size()]);
3235        try {
3236            mBarService.onNotificationVisibilityChanged(newlyVisibleAr, noLongerVisibleAr);
3237        } catch (RemoteException e) {
3238            // Ignore.
3239        }
3240    }
3241
3242    //
3243    // tracing
3244    //
3245
3246    void postStartTracing() {
3247        mHandler.postDelayed(mStartTracing, 3000);
3248    }
3249
3250    void vibrate() {
3251        android.os.Vibrator vib = (android.os.Vibrator)mContext.getSystemService(
3252                Context.VIBRATOR_SERVICE);
3253        vib.vibrate(250, VIBRATION_ATTRIBUTES);
3254    }
3255
3256    Runnable mStartTracing = new Runnable() {
3257        public void run() {
3258            vibrate();
3259            SystemClock.sleep(250);
3260            Log.d(TAG, "startTracing");
3261            android.os.Debug.startMethodTracing("/data/statusbar-traces/trace");
3262            mHandler.postDelayed(mStopTracing, 10000);
3263        }
3264    };
3265
3266    Runnable mStopTracing = new Runnable() {
3267        public void run() {
3268            android.os.Debug.stopMethodTracing();
3269            Log.d(TAG, "stopTracing");
3270            vibrate();
3271        }
3272    };
3273
3274    @Override
3275    protected void haltTicker() {
3276        if (mTickerEnabled) {
3277            mTicker.halt();
3278        }
3279    }
3280
3281    @Override
3282    protected boolean shouldDisableNavbarGestures() {
3283        return !isDeviceProvisioned()
3284                || mExpandedVisible
3285                || (mDisabled & StatusBarManager.DISABLE_SEARCH) != 0;
3286    }
3287
3288    public void postStartSettingsActivity(final Intent intent, int delay) {
3289        mHandler.postDelayed(new Runnable() {
3290            @Override
3291            public void run() {
3292                handleStartSettingsActivity(intent, true /*onlyProvisioned*/);
3293            }
3294        }, delay);
3295    }
3296
3297    private void handleStartSettingsActivity(Intent intent, boolean onlyProvisioned) {
3298        startActivityDismissingKeyguard(intent, onlyProvisioned, true /* dismissShade */,
3299                false /* afterKeyguardGone */);
3300    }
3301
3302    private static class FastColorDrawable extends Drawable {
3303        private final int mColor;
3304
3305        public FastColorDrawable(int color) {
3306            mColor = 0xff000000 | color;
3307        }
3308
3309        @Override
3310        public void draw(Canvas canvas) {
3311            canvas.drawColor(mColor, PorterDuff.Mode.SRC);
3312        }
3313
3314        @Override
3315        public void setAlpha(int alpha) {
3316        }
3317
3318        @Override
3319        public void setColorFilter(ColorFilter cf) {
3320        }
3321
3322        @Override
3323        public int getOpacity() {
3324            return PixelFormat.OPAQUE;
3325        }
3326
3327        @Override
3328        public void setBounds(int left, int top, int right, int bottom) {
3329        }
3330
3331        @Override
3332        public void setBounds(Rect bounds) {
3333        }
3334    }
3335
3336    @Override
3337    public void destroy() {
3338        super.destroy();
3339        if (mStatusBarWindow != null) {
3340            mWindowManager.removeViewImmediate(mStatusBarWindow);
3341            mStatusBarWindow = null;
3342        }
3343        if (mNavigationBarView != null) {
3344            mWindowManager.removeViewImmediate(mNavigationBarView);
3345            mNavigationBarView = null;
3346        }
3347        mContext.unregisterReceiver(mBroadcastReceiver);
3348    }
3349
3350    private boolean mDemoModeAllowed;
3351    private boolean mDemoMode;
3352    private DemoStatusIcons mDemoStatusIcons;
3353
3354    @Override
3355    public void dispatchDemoCommand(String command, Bundle args) {
3356        if (!mDemoModeAllowed) {
3357            mDemoModeAllowed = Settings.Global.getInt(mContext.getContentResolver(),
3358                    "sysui_demo_allowed", 0) != 0;
3359        }
3360        if (!mDemoModeAllowed) return;
3361        if (command.equals(COMMAND_ENTER)) {
3362            mDemoMode = true;
3363        } else if (command.equals(COMMAND_EXIT)) {
3364            mDemoMode = false;
3365            checkBarModes();
3366        } else if (!mDemoMode) {
3367            // automatically enter demo mode on first demo command
3368            dispatchDemoCommand(COMMAND_ENTER, new Bundle());
3369        }
3370        boolean modeChange = command.equals(COMMAND_ENTER) || command.equals(COMMAND_EXIT);
3371        if (modeChange || command.equals(COMMAND_CLOCK)) {
3372            dispatchDemoCommandToView(command, args, R.id.clock);
3373        }
3374        if (modeChange || command.equals(COMMAND_BATTERY)) {
3375            dispatchDemoCommandToView(command, args, R.id.battery);
3376        }
3377        if (modeChange || command.equals(COMMAND_STATUS)) {
3378            if (mDemoStatusIcons == null) {
3379                mDemoStatusIcons = new DemoStatusIcons(mStatusIcons, mIconSize);
3380            }
3381            mDemoStatusIcons.dispatchDemoCommand(command, args);
3382        }
3383        if (mNetworkController != null && (modeChange || command.equals(COMMAND_NETWORK))) {
3384            mNetworkController.dispatchDemoCommand(command, args);
3385        }
3386        if (modeChange || command.equals(COMMAND_NOTIFICATIONS)) {
3387            View notifications = mStatusBarView == null ? null
3388                    : mStatusBarView.findViewById(R.id.notification_icon_area);
3389            if (notifications != null) {
3390                String visible = args.getString("visible");
3391                int vis = mDemoMode && "false".equals(visible) ? View.INVISIBLE : View.VISIBLE;
3392                notifications.setVisibility(vis);
3393            }
3394        }
3395        if (command.equals(COMMAND_BARS)) {
3396            String mode = args.getString("mode");
3397            int barMode = "opaque".equals(mode) ? MODE_OPAQUE :
3398                    "translucent".equals(mode) ? MODE_TRANSLUCENT :
3399                    "semi-transparent".equals(mode) ? MODE_SEMI_TRANSPARENT :
3400                    "transparent".equals(mode) ? MODE_TRANSPARENT :
3401                    "warning".equals(mode) ? MODE_WARNING :
3402                    -1;
3403            if (barMode != -1) {
3404                boolean animate = true;
3405                if (mStatusBarView != null) {
3406                    mStatusBarView.getBarTransitions().transitionTo(barMode, animate);
3407                }
3408                if (mNavigationBarView != null) {
3409                    mNavigationBarView.getBarTransitions().transitionTo(barMode, animate);
3410                }
3411            }
3412        }
3413    }
3414
3415    private void dispatchDemoCommandToView(String command, Bundle args, int id) {
3416        if (mStatusBarView == null) return;
3417        View v = mStatusBarView.findViewById(id);
3418        if (v instanceof DemoMode) {
3419            ((DemoMode)v).dispatchDemoCommand(command, args);
3420        }
3421    }
3422
3423    /**
3424     * @return The {@link StatusBarState} the status bar is in.
3425     */
3426    public int getBarState() {
3427        return mState;
3428    }
3429
3430    public void showKeyguard() {
3431        setBarState(StatusBarState.KEYGUARD);
3432        updateKeyguardState(false /* goingToFullShade */, false /* fromShadeLocked */);
3433        instantExpandNotificationsPanel();
3434        mLeaveOpenOnKeyguardHide = false;
3435        if (mDraggedDownRow != null) {
3436            mDraggedDownRow.setUserLocked(false);
3437            mDraggedDownRow.notifyHeightChanged();
3438            mDraggedDownRow = null;
3439        }
3440    }
3441
3442    public boolean isCollapsing() {
3443        return mNotificationPanel.isCollapsing();
3444    }
3445
3446    public void addPostCollapseAction(Runnable r) {
3447        mPostCollapseRunnables.add(r);
3448    }
3449
3450    public boolean isInLaunchTransition() {
3451        return mNotificationPanel.isLaunchTransitionRunning()
3452                || mNotificationPanel.isLaunchTransitionFinished();
3453    }
3454
3455    /**
3456     * Fades the content of the keyguard away after the launch transition is done.
3457     *
3458     * @param beforeFading the runnable to be run when the circle is fully expanded and the fading
3459     *                     starts
3460     * @param endRunnable the runnable to be run when the transition is done
3461     */
3462    public void fadeKeyguardAfterLaunchTransition(final Runnable beforeFading,
3463            final Runnable endRunnable) {
3464        Runnable hideRunnable = new Runnable() {
3465            @Override
3466            public void run() {
3467                mLaunchTransitionFadingAway = true;
3468                if (beforeFading != null) {
3469                    beforeFading.run();
3470                }
3471                mNotificationPanel.setAlpha(1);
3472                mNotificationPanel.animate()
3473                        .alpha(0)
3474                        .setStartDelay(FADE_KEYGUARD_START_DELAY)
3475                        .setDuration(FADE_KEYGUARD_DURATION)
3476                        .withLayer()
3477                        .withEndAction(new Runnable() {
3478                            @Override
3479                            public void run() {
3480                                mNotificationPanel.setAlpha(1);
3481                                if (endRunnable != null) {
3482                                    endRunnable.run();
3483                                }
3484                                mLaunchTransitionFadingAway = false;
3485                            }
3486                        });
3487            }
3488        };
3489        if (mNotificationPanel.isLaunchTransitionRunning()) {
3490            mNotificationPanel.setLaunchTransitionEndRunnable(hideRunnable);
3491        } else {
3492            hideRunnable.run();
3493        }
3494    }
3495
3496    /**
3497     * @return true if we would like to stay in the shade, false if it should go away entirely
3498     */
3499    public boolean hideKeyguard() {
3500        boolean staying = mLeaveOpenOnKeyguardHide;
3501        setBarState(StatusBarState.SHADE);
3502        if (mLeaveOpenOnKeyguardHide) {
3503            mLeaveOpenOnKeyguardHide = false;
3504            mNotificationPanel.animateToFullShade(calculateGoingToFullShadeDelay());
3505            if (mDraggedDownRow != null) {
3506                mDraggedDownRow.setUserLocked(false);
3507                mDraggedDownRow = null;
3508            }
3509        } else {
3510            instantCollapseNotificationPanel();
3511        }
3512        updateKeyguardState(staying, false /* fromShadeLocked */);
3513        return staying;
3514    }
3515
3516    public long calculateGoingToFullShadeDelay() {
3517        return mKeyguardFadingAwayDelay + mKeyguardFadingAwayDuration;
3518    }
3519
3520    /**
3521     * Notifies the status bar the Keyguard is fading away with the specified timings.
3522     *
3523     * @param delay the animation delay in miliseconds
3524     * @param fadeoutDuration the duration of the exit animation, in milliseconds
3525     */
3526    public void setKeyguardFadingAway(long delay, long fadeoutDuration) {
3527        mKeyguardFadingAway = true;
3528        mKeyguardFadingAwayDelay = delay;
3529        mKeyguardFadingAwayDuration = fadeoutDuration;
3530        mWaitingForKeyguardExit = false;
3531        disable(mDisabledUnmodified, true /* animate */);
3532    }
3533
3534    /**
3535     * Notifies that the Keyguard fading away animation is done.
3536     */
3537    public void finishKeyguardFadingAway() {
3538        mKeyguardFadingAway = false;
3539    }
3540
3541    private void updatePublicMode() {
3542        setLockscreenPublicMode(
3543                (mStatusBarKeyguardViewManager.isShowing() ||
3544                        mStatusBarKeyguardViewManager.isOccluded())
3545                && mStatusBarKeyguardViewManager.isSecure());
3546    }
3547
3548    private void updateKeyguardState(boolean goingToFullShade, boolean fromShadeLocked) {
3549        if (mState == StatusBarState.KEYGUARD) {
3550            mKeyguardIndicationController.setVisible(true);
3551            mNotificationPanel.resetViews();
3552            mKeyguardUserSwitcher.setKeyguard(true, fromShadeLocked);
3553        } else {
3554            mKeyguardIndicationController.setVisible(false);
3555            mKeyguardUserSwitcher.setKeyguard(false,
3556                    goingToFullShade || mState == StatusBarState.SHADE_LOCKED || fromShadeLocked);
3557        }
3558        if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
3559            mScrimController.setKeyguardShowing(true);
3560        } else {
3561            mScrimController.setKeyguardShowing(false);
3562        }
3563        mNotificationPanel.setBarState(mState, mKeyguardFadingAway, goingToFullShade);
3564        updateDozingState();
3565        updatePublicMode();
3566        updateStackScrollerState(goingToFullShade);
3567        updateNotifications();
3568        checkBarModes();
3569        updateCarrierLabelVisibility(false);
3570        updateMediaMetaData(false);
3571        mKeyguardMonitor.notifyKeyguardState(mStatusBarKeyguardViewManager.isShowing(),
3572                mStatusBarKeyguardViewManager.isSecure());
3573    }
3574
3575    private void updateDozingState() {
3576        if (mState != StatusBarState.KEYGUARD) {
3577            return;
3578        }
3579        mNotificationPanel.setDozing(mDozing);
3580        if (mDozing) {
3581            mKeyguardBottomArea.setVisibility(View.INVISIBLE);
3582            mStackScroller.setDark(true, false /*animate*/);
3583        } else {
3584            mKeyguardBottomArea.setVisibility(View.VISIBLE);
3585            mStackScroller.setDark(false, false /*animate*/);
3586        }
3587        mScrimController.setDozing(mDozing);
3588    }
3589
3590    public void updateStackScrollerState(boolean goingToFullShade) {
3591        if (mStackScroller == null) return;
3592        boolean onKeyguard = mState == StatusBarState.KEYGUARD;
3593        mStackScroller.setHideSensitive(isLockscreenPublicMode(), goingToFullShade);
3594        mStackScroller.setDimmed(onKeyguard, false /* animate */);
3595        mStackScroller.setExpandingEnabled(!onKeyguard);
3596        ActivatableNotificationView activatedChild = mStackScroller.getActivatedChild();
3597        mStackScroller.setActivatedChild(null);
3598        if (activatedChild != null) {
3599            activatedChild.makeInactive(false /* animate */);
3600        }
3601    }
3602
3603    public void userActivity() {
3604        if (mState == StatusBarState.KEYGUARD) {
3605            mKeyguardViewMediatorCallback.userActivity();
3606        }
3607    }
3608
3609    public boolean interceptMediaKey(KeyEvent event) {
3610        return mState == StatusBarState.KEYGUARD
3611                && mStatusBarKeyguardViewManager.interceptMediaKey(event);
3612    }
3613
3614    public boolean onMenuPressed() {
3615        return mState == StatusBarState.KEYGUARD && mStatusBarKeyguardViewManager.onMenuPressed();
3616    }
3617
3618    public boolean onBackPressed() {
3619        if (mStatusBarKeyguardViewManager.onBackPressed()) {
3620            return true;
3621        }
3622        if (mNotificationPanel.isQsExpanded()) {
3623            if (mNotificationPanel.isQsDetailShowing()) {
3624                mNotificationPanel.closeQsDetail();
3625            } else {
3626                mNotificationPanel.animateCloseQs();
3627            }
3628            return true;
3629        }
3630        if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED) {
3631            animateCollapsePanels();
3632            return true;
3633        }
3634        return false;
3635    }
3636
3637    public boolean onSpacePressed() {
3638        if (mScreenOn != null && mScreenOn
3639                && (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)) {
3640            animateCollapsePanels(0 /* flags */, true /* force */);
3641            return true;
3642        }
3643        return false;
3644    }
3645
3646    private void showBouncer() {
3647        if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
3648            mWaitingForKeyguardExit = mStatusBarKeyguardViewManager.isShowing();
3649            mStatusBarKeyguardViewManager.dismiss();
3650        }
3651    }
3652
3653    private void instantExpandNotificationsPanel() {
3654
3655        // Make our window larger and the panel expanded.
3656        makeExpandedVisible(true);
3657        mNotificationPanel.instantExpand();
3658    }
3659
3660    private void instantCollapseNotificationPanel() {
3661        mNotificationPanel.setExpandedFraction(0);
3662    }
3663
3664    @Override
3665    public void onActivated(ActivatableNotificationView view) {
3666        mKeyguardIndicationController.showTransientIndication(R.string.notification_tap_again);
3667        ActivatableNotificationView previousView = mStackScroller.getActivatedChild();
3668        if (previousView != null) {
3669            previousView.makeInactive(true /* animate */);
3670        }
3671        mStackScroller.setActivatedChild(view);
3672    }
3673
3674    /**
3675     * @param state The {@link StatusBarState} to set.
3676     */
3677    public void setBarState(int state) {
3678        mState = state;
3679        mStatusBarWindowManager.setStatusBarState(state);
3680    }
3681
3682    @Override
3683    public void onActivationReset(ActivatableNotificationView view) {
3684        if (view == mStackScroller.getActivatedChild()) {
3685            mKeyguardIndicationController.hideTransientIndication();
3686            mStackScroller.setActivatedChild(null);
3687        }
3688    }
3689
3690    public void onTrackingStarted() {
3691        runPostCollapseRunnables();
3692    }
3693
3694    public void onUnlockHintStarted() {
3695        mKeyguardIndicationController.showTransientIndication(R.string.keyguard_unlock);
3696    }
3697
3698    public void onHintFinished() {
3699        // Delay the reset a bit so the user can read the text.
3700        mKeyguardIndicationController.hideTransientIndicationDelayed(HINT_RESET_DELAY_MS);
3701    }
3702
3703    public void onCameraHintStarted() {
3704        mKeyguardIndicationController.showTransientIndication(R.string.camera_hint);
3705    }
3706
3707    public void onPhoneHintStarted() {
3708        mKeyguardIndicationController.showTransientIndication(R.string.phone_hint);
3709    }
3710
3711    public void onTrackingStopped(boolean expand) {
3712        if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
3713            if (!expand && !mUnlockMethodCache.isMethodInsecure()) {
3714                showBouncer();
3715            }
3716        }
3717    }
3718
3719    @Override
3720    protected int getMaxKeyguardNotifications() {
3721        return mKeyguardMaxNotificationCount;
3722    }
3723
3724    public NavigationBarView getNavigationBarView() {
3725        return mNavigationBarView;
3726    }
3727
3728    // ---------------------- DragDownHelper.OnDragDownListener ------------------------------------
3729
3730    @Override
3731    public boolean onDraggedDown(View startingChild) {
3732        if (hasActiveNotifications()) {
3733
3734            // We have notifications, go to locked shade.
3735            goToLockedShade(startingChild);
3736            return true;
3737        } else {
3738
3739            // No notifications - abort gesture.
3740            return false;
3741        }
3742    }
3743
3744    @Override
3745    public void onDragDownReset() {
3746        mStackScroller.setDimmed(true /* dimmed */, true /* animated */);
3747    }
3748
3749    @Override
3750    public void onThresholdReached() {
3751        mStackScroller.setDimmed(false /* dimmed */, true /* animate */);
3752    }
3753
3754    @Override
3755    public void onTouchSlopExceeded() {
3756        mStackScroller.removeLongPressCallback();
3757    }
3758
3759    @Override
3760    public void setEmptyDragAmount(float amount) {
3761        mNotificationPanel.setEmptyDragAmount(amount);
3762    }
3763
3764    /**
3765     * If secure with redaction: Show bouncer, go to unlocked shade.
3766     *
3767     * <p>If secure without redaction or no security: Go to {@link StatusBarState#SHADE_LOCKED}.</p>
3768     *
3769     * @param expandView The view to expand after going to the shade.
3770     */
3771    public void goToLockedShade(View expandView) {
3772        ExpandableNotificationRow row = null;
3773        if (expandView instanceof ExpandableNotificationRow) {
3774            row = (ExpandableNotificationRow) expandView;
3775            row.setUserExpanded(true);
3776        }
3777        if (isLockscreenPublicMode() && !userAllowsPrivateNotificationsInPublic(mCurrentUserId)) {
3778            mLeaveOpenOnKeyguardHide = true;
3779            showBouncer();
3780            mDraggedDownRow = row;
3781        } else {
3782            mNotificationPanel.animateToFullShade(0 /* delay */);
3783            setBarState(StatusBarState.SHADE_LOCKED);
3784            updateKeyguardState(false /* goingToFullShade */, false /* fromShadeLocked */);
3785            if (row != null) {
3786                row.setUserLocked(false);
3787            }
3788        }
3789    }
3790
3791    /**
3792     * Goes back to the keyguard after hanging around in {@link StatusBarState#SHADE_LOCKED}.
3793     */
3794    public void goToKeyguard() {
3795        if (mState == StatusBarState.SHADE_LOCKED) {
3796            mStackScroller.onGoToKeyguard();
3797            setBarState(StatusBarState.KEYGUARD);
3798            updateKeyguardState(false /* goingToFullShade */, true /* fromShadeLocked*/);
3799        }
3800    }
3801
3802    /**
3803     * @return a ViewGroup that spans the entire panel which contains the quick settings
3804     */
3805    public ViewGroup getQuickSettingsOverlayParent() {
3806        return mNotificationPanel;
3807    }
3808
3809    public long getKeyguardFadingAwayDelay() {
3810        return mKeyguardFadingAwayDelay;
3811    }
3812
3813    public long getKeyguardFadingAwayDuration() {
3814        return mKeyguardFadingAwayDuration;
3815    }
3816
3817    public LinearLayout getSystemIcons() {
3818        return mSystemIcons;
3819    }
3820
3821    public LinearLayout getSystemIconArea() {
3822        return mSystemIconArea;
3823    }
3824
3825    @Override
3826    public void setBouncerShowing(boolean bouncerShowing) {
3827        super.setBouncerShowing(bouncerShowing);
3828        disable(mDisabledUnmodified, true /* animate */);
3829    }
3830
3831    public void onScreenTurnedOff() {
3832        mStackScroller.setAnimationsEnabled(false);
3833    }
3834
3835    public void onScreenTurnedOn() {
3836        mStackScroller.setAnimationsEnabled(true);
3837        mNotificationPanel.onScreenTurnedOn();
3838    }
3839
3840    /**
3841     * This handles long-press of both back and recents.  They are
3842     * handled together to capture them both being long-pressed
3843     * at the same time to exit screen pinning (lock task).
3844     *
3845     * When accessibility mode is on, only a long-press from recents
3846     * is required to exit.
3847     *
3848     * In all other circumstances we try to pass through long-press events
3849     * for Back, so that apps can still use it.  Which can be from two things.
3850     * 1) Not currently in screen pinning (lock task).
3851     * 2) Back is long-pressed without recents.
3852     */
3853    private void handleLongPressBackRecents(View v) {
3854        try {
3855            boolean sendBackLongPress = false;
3856            IActivityManager activityManager = ActivityManagerNative.getDefault();
3857            if (mAccessibilityManager == null) {
3858                mAccessibilityManager = (AccessibilityManager)
3859                        mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
3860            }
3861            boolean isAccessiblityEnabled = mAccessibilityManager.isEnabled();
3862            if (activityManager.isInLockTaskMode() && !isAccessiblityEnabled) {
3863                long time = System.currentTimeMillis();
3864                // If we recently long-pressed the other button then they were
3865                // long-pressed 'together'
3866                if ((time - mLastLockToAppLongPress) < LOCK_TO_APP_GESTURE_TOLERENCE) {
3867                    activityManager.stopLockTaskModeOnCurrent();
3868                } else if ((v.getId() == R.id.back)
3869                        && !mNavigationBarView.getRecentsButton().isPressed()) {
3870                    // If we aren't pressing recents right now then they presses
3871                    // won't be together, so send the standard long-press action.
3872                    sendBackLongPress = true;
3873                }
3874                mLastLockToAppLongPress = time;
3875            } else {
3876                // If this is back still need to handle sending the long-press event.
3877                if (v.getId() == R.id.back) {
3878                    sendBackLongPress = true;
3879                } else if (isAccessiblityEnabled && activityManager.isInLockTaskMode()) {
3880                    // When in accessibility mode a long press that is recents (not back)
3881                    // should stop lock task.
3882                    activityManager.stopLockTaskModeOnCurrent();
3883                }
3884            }
3885            if (sendBackLongPress) {
3886                KeyButtonView keyButtonView = (KeyButtonView) v;
3887                keyButtonView.sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
3888                keyButtonView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
3889            }
3890        } catch (RemoteException e) {
3891            Log.d(TAG, "Unable to reach activity manager", e);
3892        }
3893    }
3894
3895    // Recents
3896
3897    @Override
3898    protected void showRecents(boolean triggeredFromAltTab) {
3899        // Set the recents visibility flag
3900        mSystemUiVisibility |= View.RECENT_APPS_VISIBLE;
3901        notifyUiVisibilityChanged(mSystemUiVisibility);
3902        super.showRecents(triggeredFromAltTab);
3903    }
3904
3905    @Override
3906    protected void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
3907        // Unset the recents visibility flag
3908        mSystemUiVisibility &= ~View.RECENT_APPS_VISIBLE;
3909        notifyUiVisibilityChanged(mSystemUiVisibility);
3910        super.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
3911    }
3912
3913    @Override
3914    protected void toggleRecents() {
3915        // Toggle the recents visibility flag
3916        mSystemUiVisibility ^= View.RECENT_APPS_VISIBLE;
3917        notifyUiVisibilityChanged(mSystemUiVisibility);
3918        super.toggleRecents();
3919    }
3920
3921    @Override
3922    public void onVisibilityChanged(boolean visible) {
3923        // Update the recents visibility flag
3924        if (visible) {
3925            mSystemUiVisibility |= View.RECENT_APPS_VISIBLE;
3926        } else {
3927            mSystemUiVisibility &= ~View.RECENT_APPS_VISIBLE;
3928        }
3929        notifyUiVisibilityChanged(mSystemUiVisibility);
3930    }
3931
3932    public boolean hasActiveNotifications() {
3933        return !mNotificationData.getActiveNotifications().isEmpty();
3934    }
3935
3936    private final class ShadeUpdates {
3937        private final ArraySet<String> mVisibleNotifications = new ArraySet<String>();
3938        private final ArraySet<String> mNewVisibleNotifications = new ArraySet<String>();
3939
3940        public void check() {
3941            mNewVisibleNotifications.clear();
3942            ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
3943            for (int i = 0; i < activeNotifications.size(); i++) {
3944                final Entry entry = activeNotifications.get(i);
3945                final boolean visible = entry.row != null
3946                        && entry.row.getVisibility() == View.VISIBLE;
3947                if (visible) {
3948                    mNewVisibleNotifications.add(entry.key + entry.notification.getPostTime());
3949                }
3950            }
3951            final boolean updates = !mVisibleNotifications.containsAll(mNewVisibleNotifications);
3952            mVisibleNotifications.clear();
3953            mVisibleNotifications.addAll(mNewVisibleNotifications);
3954
3955            // We have new notifications
3956            if (updates && mDozeServiceHost != null) {
3957                mDozeServiceHost.fireNewNotifications();
3958            }
3959        }
3960    }
3961
3962    private final class DozeServiceHost implements DozeService.Host {
3963        // Amount of time to allow to update the time shown on the screen before releasing
3964        // the wakelock.  This timeout is design to compensate for the fact that we don't
3965        // currently have a way to know when time display contents have actually been
3966        // refreshed once we've finished rendering a new frame.
3967        private static final long PROCESSING_TIME = 500;
3968
3969        private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
3970        private final H mHandler = new H();
3971
3972        private DozeService mCurrentDozeService;
3973
3974        @Override
3975        public String toString() {
3976            return "PSB.DozeServiceHost[mCallbacks=" + mCallbacks.size() + " mCurrentDozeService="
3977                    + mCurrentDozeService + "]";
3978        }
3979
3980        public void firePowerSaveChanged(boolean active) {
3981            for (Callback callback : mCallbacks) {
3982                callback.onPowerSaveChanged(active);
3983            }
3984        }
3985
3986        public void fireBuzzBeepBlinked() {
3987            for (Callback callback : mCallbacks) {
3988                callback.onBuzzBeepBlinked();
3989            }
3990        }
3991
3992        public void fireNotificationLight(boolean on) {
3993            for (Callback callback : mCallbacks) {
3994                callback.onNotificationLight(on);
3995            }
3996        }
3997
3998        public void fireNewNotifications() {
3999            for (Callback callback : mCallbacks) {
4000                callback.onNewNotifications();
4001            }
4002        }
4003
4004        @Override
4005        public void addCallback(Callback callback) {
4006            mCallbacks.add(callback);
4007        }
4008
4009        @Override
4010        public void removeCallback(Callback callback) {
4011            mCallbacks.remove(callback);
4012        }
4013
4014        @Override
4015        public void requestDoze(DozeService dozeService) {
4016            if (dozeService == null) return;
4017            dozeService.stayAwake(PROCESSING_TIME);
4018            mHandler.obtainMessage(H.REQUEST_DOZE, dozeService).sendToTarget();
4019        }
4020
4021        @Override
4022        public void requestPulse(int pulses, boolean delayed, DozeService dozeService) {
4023            if (dozeService == null) return;
4024            dozeService.stayAwake(PROCESSING_TIME);
4025            mHandler.obtainMessage(H.REQUEST_PULSE, pulses, delayed ? 1 : 0, dozeService)
4026                    .sendToTarget();
4027        }
4028
4029        @Override
4030        public void dozingStopped(DozeService dozeService) {
4031            if (dozeService == null) return;
4032            dozeService.stayAwake(PROCESSING_TIME);
4033            mHandler.obtainMessage(H.DOZING_STOPPED, dozeService).sendToTarget();
4034        }
4035
4036        @Override
4037        public boolean isPowerSaveActive() {
4038            return mBatteryController != null && mBatteryController.isPowerSave();
4039        }
4040
4041        private void handleRequestDoze(DozeService dozeService) {
4042            mCurrentDozeService = dozeService;
4043            if (!mDozing) {
4044                mDozing = true;
4045                updateDozingState();
4046            }
4047            mCurrentDozeService.startDozing();
4048        }
4049
4050        private void handleRequestPulse(int pulses, boolean delayed, DozeService dozeService) {
4051            if (!dozeService.equals(mCurrentDozeService)) return;
4052            final long stayAwake = mScrimController.pulse(pulses, delayed);
4053            mCurrentDozeService.stayAwake(stayAwake);
4054        }
4055
4056        private void handleDozingStopped(DozeService dozeService) {
4057            if (dozeService.equals(mCurrentDozeService)) {
4058                mCurrentDozeService = null;
4059            }
4060            if (mDozing) {
4061                mDozing = false;
4062                updateDozingState();
4063            }
4064        }
4065
4066        private final class H extends Handler {
4067            private static final int REQUEST_DOZE = 1;
4068            private static final int REQUEST_PULSE = 2;
4069            private static final int DOZING_STOPPED = 3;
4070
4071            @Override
4072            public void handleMessage(Message msg) {
4073                if (msg.what == REQUEST_DOZE) {
4074                    handleRequestDoze((DozeService) msg.obj);
4075                } else if (msg.what == REQUEST_PULSE) {
4076                    handleRequestPulse(msg.arg1, msg.arg2 != 0, (DozeService) msg.obj);
4077                } else if (msg.what == DOZING_STOPPED) {
4078                    handleDozingStopped((DozeService) msg.obj);
4079                }
4080            }
4081        }
4082    }
4083}
4084