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