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