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