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