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