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