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