PhoneStatusBar.java revision 37fe69322517809d38c1934239a6e117c39d40e7
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.keyguard.KeyguardHostView.OnDismissAction;
26import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
27import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
28import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
29import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCENT;
30import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
31
32import android.animation.Animator;
33import android.animation.AnimatorListenerAdapter;
34import android.animation.TimeInterpolator;
35import android.animation.ValueAnimator;
36import android.app.ActivityManager;
37import android.app.ActivityManagerNative;
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.Canvas;
49import android.graphics.ColorFilter;
50import android.graphics.PixelFormat;
51import android.graphics.Point;
52import android.graphics.PorterDuff;
53import android.graphics.Rect;
54import android.graphics.drawable.Drawable;
55import android.inputmethodservice.InputMethodService;
56import android.media.AudioManager;
57import android.os.Bundle;
58import android.os.Handler;
59import android.os.IBinder;
60import android.os.Message;
61import android.os.PowerManager;
62import android.os.RemoteException;
63import android.os.SystemClock;
64import android.os.UserHandle;
65import android.provider.Settings;
66import android.provider.Settings.Global;
67import android.service.notification.NotificationListenerService.Ranking;
68import android.service.notification.StatusBarNotification;
69import android.util.ArraySet;
70import android.util.DisplayMetrics;
71import android.util.EventLog;
72import android.util.Log;
73import android.view.Display;
74import android.view.Gravity;
75import android.view.KeyEvent;
76import android.view.LayoutInflater;
77import android.view.MotionEvent;
78import android.view.VelocityTracker;
79import android.view.View;
80import android.view.ViewGroup;
81import android.view.ViewGroup.LayoutParams;
82import android.view.ViewPropertyAnimator;
83import android.view.ViewTreeObserver;
84import android.view.WindowManager;
85import android.view.animation.AccelerateInterpolator;
86import android.view.animation.Animation;
87import android.view.animation.AnimationUtils;
88import android.view.animation.DecelerateInterpolator;
89import android.widget.FrameLayout;
90import android.widget.LinearLayout;
91import android.widget.TextView;
92
93import com.android.internal.statusbar.StatusBarIcon;
94import com.android.keyguard.ViewMediatorCallback;
95import com.android.systemui.DemoMode;
96import com.android.systemui.EventLogTags;
97import com.android.systemui.R;
98import com.android.systemui.keyguard.KeyguardViewMediator;
99import com.android.systemui.qs.CircularClipper;
100import com.android.systemui.qs.QSPanel;
101import com.android.systemui.qs.QSTile;
102import com.android.systemui.statusbar.BaseStatusBar;
103import com.android.systemui.statusbar.CommandQueue;
104import com.android.systemui.statusbar.DragDownHelper;
105import com.android.systemui.statusbar.ExpandableNotificationRow;
106import com.android.systemui.statusbar.GestureRecorder;
107import com.android.systemui.statusbar.InterceptedNotifications;
108import com.android.systemui.statusbar.NotificationData;
109import com.android.systemui.statusbar.NotificationData.Entry;
110import com.android.systemui.statusbar.NotificationOverflowContainer;
111import com.android.systemui.statusbar.SignalClusterView;
112import com.android.systemui.statusbar.SpeedBumpView;
113import com.android.systemui.statusbar.StatusBarIconView;
114import com.android.systemui.statusbar.StatusBarState;
115import com.android.systemui.statusbar.policy.BatteryController;
116import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
117import com.android.systemui.statusbar.policy.CastControllerImpl;
118import com.android.systemui.statusbar.policy.DateView;
119import com.android.systemui.statusbar.policy.HeadsUpNotificationView;
120import com.android.systemui.statusbar.policy.UserInfoController;
121import com.android.systemui.statusbar.policy.LocationControllerImpl;
122import com.android.systemui.statusbar.policy.NetworkControllerImpl;
123import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
124import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
125import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
126import com.android.systemui.statusbar.stack.NotificationStackScrollLayout.OnChildLocationsChangedListener;
127import com.android.systemui.statusbar.stack.StackScrollState.ViewState;
128
129import java.io.FileDescriptor;
130import java.io.PrintWriter;
131import java.util.ArrayList;
132import java.util.Collection;
133import java.util.Collections;
134
135public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
136        DragDownHelper.OnDragDownListener, ActivityStarter {
137    static final String TAG = "PhoneStatusBar";
138    public static final boolean DEBUG = BaseStatusBar.DEBUG;
139    public static final boolean SPEW = false;
140    public static final boolean DUMPTRUCK = true; // extra dumpsys info
141    public static final boolean DEBUG_GESTURES = false;
142
143    public static final boolean DEBUG_WINDOW_STATE = false;
144
145    public static final boolean SETTINGS_DRAG_SHORTCUT = true;
146
147    // additional instrumentation for testing purposes; intended to be left on during development
148    public static final boolean CHATTY = DEBUG;
149
150    public static final String ACTION_STATUSBAR_START
151            = "com.android.internal.policy.statusbar.START";
152
153    private static final int MSG_OPEN_NOTIFICATION_PANEL = 1000;
154    private static final int MSG_CLOSE_PANELS = 1001;
155    private static final int MSG_OPEN_SETTINGS_PANEL = 1002;
156    // 1020-1030 reserved for BaseStatusBar
157
158    private static final boolean CLOSE_PANEL_WHEN_EMPTIED = true;
159
160    private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10; // see NotificationManagerService
161    private static final int HIDE_ICONS_BELOW_SCORE = Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER;
162
163    /**
164     * Default value of {@link android.provider.Settings.Global#LOCK_SCREEN_SHOW_NOTIFICATIONS}.
165     */
166    private static final boolean ALLOW_NOTIFICATIONS_DEFAULT = false;
167
168    private static final int STATUS_OR_NAV_TRANSIENT =
169            View.STATUS_BAR_TRANSIENT | View.NAVIGATION_BAR_TRANSIENT;
170    private static final long AUTOHIDE_TIMEOUT_MS = 3000;
171
172    /** The minimum delay in ms between reports of notification visibility. */
173    private static final int VISIBILITY_REPORT_MIN_DELAY_MS = 500;
174
175    // fling gesture tuning parameters, scaled to display density
176    private float mSelfExpandVelocityPx; // classic value: 2000px/s
177    private float mSelfCollapseVelocityPx; // classic value: 2000px/s (will be negated to collapse "up")
178    private float mFlingExpandMinVelocityPx; // classic value: 200px/s
179    private float mFlingCollapseMinVelocityPx; // classic value: 200px/s
180    private float mCollapseMinDisplayFraction; // classic value: 0.08 (25px/min(320px,480px) on G1)
181    private float mExpandMinDisplayFraction; // classic value: 0.5 (drag open halfway to expand)
182    private float mFlingGestureMaxXVelocityPx; // classic value: 150px/s
183
184    private float mExpandAccelPx; // classic value: 2000px/s/s
185    private float mCollapseAccelPx; // classic value: 2000px/s/s (will be negated to collapse "up")
186
187    private float mFlingGestureMaxOutputVelocityPx; // how fast can it really go? (should be a little
188                                                    // faster than mSelfCollapseVelocityPx)
189
190    PhoneStatusBarPolicy mIconPolicy;
191
192    // These are no longer handled by the policy, because we need custom strategies for them
193    BluetoothControllerImpl mBluetoothController;
194    BatteryController mBatteryController;
195    LocationControllerImpl mLocationController;
196    NetworkControllerImpl mNetworkController;
197    RotationLockControllerImpl mRotationLockController;
198    UserInfoController mUserInfoController;
199    ZenModeControllerImpl mZenModeController;
200    CastControllerImpl mCastController;
201
202    int mNaturalBarHeight = -1;
203    int mIconSize = -1;
204    int mIconHPadding = -1;
205    Display mDisplay;
206    Point mCurrentDisplaySize = new Point();
207
208    StatusBarWindowView mStatusBarWindow;
209    PhoneStatusBarView mStatusBarView;
210    private int mStatusBarWindowState = WINDOW_STATE_SHOWING;
211    private StatusBarWindowManager mStatusBarWindowManager;
212
213    int mPixelFormat;
214    Object mQueueLock = new Object();
215
216    // viewgroup containing the normal contents of the statusbar
217    LinearLayout mStatusBarContents;
218
219    // right-hand icons
220    LinearLayout mSystemIconArea;
221    LinearLayout mSystemIcons;
222
223    // left-hand icons
224    LinearLayout mStatusIcons;
225    // the icons themselves
226    IconMerger mNotificationIcons;
227    // [+>
228    View mMoreIcon;
229
230    // expanded notifications
231    NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window
232    View mExpandedContents;
233    int mNotificationPanelGravity;
234    int mNotificationPanelMarginBottomPx;
235    float mNotificationPanelMinHeightFrac;
236    TextView mNotificationPanelDebugText;
237
238    // settings
239    View mFlipSettingsView;
240    private QSPanel mQSPanel;
241
242    // top bar
243    StatusBarHeaderView mHeader;
244    View mKeyguardStatusView;
245    KeyguardBottomAreaView mKeyguardBottomArea;
246    boolean mLeaveOpenOnKeyguardHide;
247    KeyguardIndicationTextView mKeyguardIndicationTextView;
248
249    // TODO: Fetch phrase from search/hotword provider.
250    String mKeyguardHotwordPhrase = "";
251    int mKeyguardMaxNotificationCount;
252    View mDateTimeView;
253
254    // carrier/wifi label
255    private TextView mCarrierLabel;
256    private boolean mCarrierLabelVisible = false;
257    private int mCarrierLabelHeight;
258    private TextView mEmergencyCallLabel;
259    private int mStatusBarHeaderHeight;
260
261    private boolean mShowCarrierInPanel = false;
262
263    // position
264    int[] mPositionTmp = new int[2];
265    boolean mExpandedVisible;
266
267    // the date view
268    DateView mDateView;
269
270    // for heads up notifications
271    private HeadsUpNotificationView mHeadsUpNotificationView;
272    private int mHeadsUpNotificationDecay;
273
274    // on-screen navigation buttons
275    private NavigationBarView mNavigationBarView = null;
276    private int mNavigationBarWindowState = WINDOW_STATE_SHOWING;
277
278    // the tracker view
279    int mTrackingPosition; // the position of the top of the tracking view.
280
281    // ticker
282    private Ticker mTicker;
283    private View mTickerView;
284    private boolean mTicking;
285
286    // Tracking finger for opening/closing.
287    int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore
288    boolean mTracking;
289    VelocityTracker mVelocityTracker;
290
291    int[] mAbsPos = new int[2];
292    Runnable mPostCollapseCleanup = null;
293
294    // for disabling the status bar
295    int mDisabled = 0;
296
297    // tracking calls to View.setSystemUiVisibility()
298    int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE;
299
300    DisplayMetrics mDisplayMetrics = new DisplayMetrics();
301
302    // XXX: gesture research
303    private final GestureRecorder mGestureRec = DEBUG_GESTURES
304        ? new GestureRecorder("/sdcard/statusbar_gestures.dat")
305        : null;
306
307    private int mNavigationIconHints = 0;
308    private final Animator.AnimatorListener mMakeIconsInvisible = new AnimatorListenerAdapter() {
309        @Override
310        public void onAnimationEnd(Animator animation) {
311            // double-check to avoid races
312            if (mStatusBarContents.getAlpha() == 0) {
313                if (DEBUG) Log.d(TAG, "makeIconsInvisible");
314                mStatusBarContents.setVisibility(View.INVISIBLE);
315            }
316        }
317    };
318
319    // ensure quick settings is disabled until the current user makes it through the setup wizard
320    private boolean mUserSetup = false;
321    private ContentObserver mUserSetupObserver = new ContentObserver(new Handler()) {
322        @Override
323        public void onChange(boolean selfChange) {
324            final boolean userSetup = 0 != Settings.Secure.getIntForUser(
325                    mContext.getContentResolver(),
326                    Settings.Secure.USER_SETUP_COMPLETE,
327                    0 /*default */,
328                    mCurrentUserId);
329            if (MULTIUSER_DEBUG) Log.d(TAG, String.format("User setup changed: " +
330                    "selfChange=%s userSetup=%s mUserSetup=%s",
331                    selfChange, userSetup, mUserSetup));
332
333            if (userSetup != mUserSetup) {
334                mUserSetup = userSetup;
335                if (mNotificationPanel != null) {
336                    mNotificationPanel.setQsExpansionEnabled(isDeviceProvisioned() && userSetup);
337                }
338                if (!mUserSetup && mStatusBarView != null)
339                    animateCollapseQuickSettings();
340            }
341        }
342    };
343
344    final private ContentObserver mHeadsUpObserver = new ContentObserver(mHandler) {
345        @Override
346        public void onChange(boolean selfChange) {
347            boolean wasUsing = mUseHeadsUp;
348            mUseHeadsUp = ENABLE_HEADS_UP && Settings.Global.HEADS_UP_OFF != Settings.Global.getInt(
349                    mContext.getContentResolver(), Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
350                    Settings.Global.HEADS_UP_OFF);
351            mHeadsUpTicker = mUseHeadsUp && 0 != Settings.Global.getInt(
352                    mContext.getContentResolver(), SETTING_HEADS_UP_TICKER, 0);
353            Log.d(TAG, "heads up is " + (mUseHeadsUp ? "enabled" : "disabled"));
354            if (wasUsing != mUseHeadsUp) {
355                if (!mUseHeadsUp) {
356                    Log.d(TAG, "dismissing any existing heads up notification on disable event");
357                    setHeadsUpVisibility(false);
358                    mHeadsUpNotificationView.setNotification(null);
359                    removeHeadsUpView();
360                } else {
361                    addHeadsUpView();
362                }
363            }
364        }
365    };
366
367    private int mInteractingWindows;
368    private boolean mAutohideSuspended;
369    private int mStatusBarMode;
370    private int mNavigationBarMode;
371    private Boolean mScreenOn;
372
373    private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
374    private ViewMediatorCallback mKeyguardViewMediatorCallback;
375
376    private final Runnable mAutohide = new Runnable() {
377        @Override
378        public void run() {
379            int requested = mSystemUiVisibility & ~STATUS_OR_NAV_TRANSIENT;
380            if (mSystemUiVisibility != requested) {
381                notifyUiVisibilityChanged(requested);
382            }
383        }};
384
385    private Runnable mOnFlipRunnable;
386    private InterceptedNotifications mIntercepted;
387    private VelocityTracker mSettingsTracker;
388    private float mSettingsDownY;
389    private boolean mSettingsStarted;
390    private boolean mSettingsCancelled;
391    private boolean mSettingsClosing;
392    private boolean mVisible;
393
394    private final OnChildLocationsChangedListener mOnChildLocationsChangedListener =
395            new OnChildLocationsChangedListener() {
396        @Override
397        public void onChildLocationsChanged(NotificationStackScrollLayout stackScrollLayout) {
398            userActivity();
399        }
400    };
401
402    public void setOnFlipRunnable(Runnable onFlipRunnable) {
403        mOnFlipRunnable = onFlipRunnable;
404    }
405
406    /** Keys of notifications currently visible to the user. */
407    private final ArraySet<String> mCurrentlyVisibleNotifications = new ArraySet<String>();
408    private long mLastVisibilityReportUptimeMs;
409
410    private static final int VISIBLE_LOCATIONS = ViewState.LOCATION_FIRST_CARD
411            | ViewState.LOCATION_TOP_STACK_PEEKING
412            | ViewState.LOCATION_MAIN_AREA
413            | ViewState.LOCATION_BOTTOM_STACK_PEEKING;
414
415    private final OnChildLocationsChangedListener mNotificationLocationsChangedListener =
416            new OnChildLocationsChangedListener() {
417                @Override
418                public void onChildLocationsChanged(
419                        NotificationStackScrollLayout stackScrollLayout) {
420                    if (mHandler.hasCallbacks(mVisibilityReporter)) {
421                        // Visibilities will be reported when the existing
422                        // callback is executed.
423                        return;
424                    }
425                    // Calculate when we're allowed to run the visibility
426                    // reporter. Note that this timestamp might already have
427                    // passed. That's OK, the callback will just be executed
428                    // ASAP.
429                    long nextReportUptimeMs =
430                            mLastVisibilityReportUptimeMs + VISIBILITY_REPORT_MIN_DELAY_MS;
431                    mHandler.postAtTime(mVisibilityReporter, nextReportUptimeMs);
432                }
433            };
434
435    // Tracks notifications currently visible in mNotificationStackScroller and
436    // emits visibility events via NoMan on changes.
437    private final Runnable mVisibilityReporter = new Runnable() {
438        private final ArrayList<String> mTmpNewlyVisibleNotifications = new ArrayList<String>();
439        private final ArrayList<String> mTmpCurrentlyVisibleNotifications = new ArrayList<String>();
440
441        @Override
442        public void run() {
443            mLastVisibilityReportUptimeMs = SystemClock.uptimeMillis();
444
445            // 1. Loop over mNotificationData entries:
446            //   A. Keep list of visible notifications.
447            //   B. Keep list of previously hidden, now visible notifications.
448            // 2. Compute no-longer visible notifications by removing currently
449            //    visible notifications from the set of previously visible
450            //    notifications.
451            // 3. Report newly visible and no-longer visible notifications.
452            // 4. Keep currently visible notifications for next report.
453            int N = mNotificationData.size();
454            for (int i = 0; i < N; i++) {
455                Entry entry = mNotificationData.get(i);
456                String key = entry.notification.getKey();
457                boolean previouslyVisible = mCurrentlyVisibleNotifications.contains(key);
458                boolean currentlyVisible =
459                        (mStackScroller.getChildLocation(entry.row) & VISIBLE_LOCATIONS) != 0;
460                if (currentlyVisible) {
461                    // Build new set of visible notifications.
462                    mTmpCurrentlyVisibleNotifications.add(key);
463                }
464                if (!previouslyVisible && currentlyVisible) {
465                    mTmpNewlyVisibleNotifications.add(key);
466                }
467            }
468            ArraySet<String> noLongerVisibleNotifications = mCurrentlyVisibleNotifications;
469            noLongerVisibleNotifications.removeAll(mTmpCurrentlyVisibleNotifications);
470
471            logNotificationVisibilityChanges(
472                    mTmpNewlyVisibleNotifications, noLongerVisibleNotifications);
473
474            mCurrentlyVisibleNotifications.clear();
475            mCurrentlyVisibleNotifications.addAll(mTmpCurrentlyVisibleNotifications);
476
477            mTmpNewlyVisibleNotifications.clear();
478            mTmpCurrentlyVisibleNotifications.clear();
479        }
480    };
481
482    private final View.OnClickListener mOverflowClickListener = new View.OnClickListener() {
483        @Override
484        public void onClick(View v) {
485            goToLockedShade(null);
486        }
487    };
488
489    @Override
490    public void setZenMode(int mode) {
491        super.setZenMode(mode);
492        if (!isDeviceProvisioned()) return;
493        final boolean zen = mode != Settings.Global.ZEN_MODE_OFF;
494        if (!zen) {
495            mIntercepted.releaseIntercepted();
496        }
497        if (mIconPolicy != null) {
498            mIconPolicy.setZenMode(zen);
499        }
500    }
501
502    @Override
503    public void start() {
504        mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
505                .getDefaultDisplay();
506        updateDisplaySize();
507        mIntercepted = new InterceptedNotifications(mContext, this);
508        super.start(); // calls createAndAddWindows()
509
510        addNavigationBar();
511
512        // Lastly, call to the icon policy to install/update all the icons.
513        mIconPolicy = new PhoneStatusBarPolicy(mContext);
514        mSettingsObserver.onChange(false); // set up
515
516        mHeadsUpObserver.onChange(true); // set up
517        if (ENABLE_HEADS_UP) {
518            mContext.getContentResolver().registerContentObserver(
519                    Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED), true,
520                    mHeadsUpObserver);
521            mContext.getContentResolver().registerContentObserver(
522                    Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true,
523                    mHeadsUpObserver);
524        }
525        startKeyguard();
526    }
527
528    // ================================================================================
529    // Constructing the view
530    // ================================================================================
531    protected PhoneStatusBarView makeStatusBarView() {
532        final Context context = mContext;
533
534        Resources res = context.getResources();
535
536        updateDisplaySize(); // populates mDisplayMetrics
537        loadDimens();
538
539        mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
540
541        mStatusBarWindow = (StatusBarWindowView) View.inflate(context,
542                R.layout.super_status_bar, null);
543        mStatusBarWindow.mService = this;
544        mStatusBarWindow.setOnTouchListener(new View.OnTouchListener() {
545            @Override
546            public boolean onTouch(View v, MotionEvent event) {
547                checkUserAutohide(v, event);
548                if (event.getAction() == MotionEvent.ACTION_DOWN) {
549                    if (mExpandedVisible) {
550                        animateCollapsePanels();
551                    }
552                }
553                return mStatusBarWindow.onTouchEvent(event);
554            }});
555
556        mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar);
557        mStatusBarView.setBar(this);
558
559        PanelHolder holder = (PanelHolder) mStatusBarWindow.findViewById(R.id.panel_holder);
560        mStatusBarView.setPanelHolder(holder);
561
562        mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById(
563                R.id.notification_panel);
564        mNotificationPanel.setStatusBar(this);
565
566        // make the header non-responsive to clicks
567        mNotificationPanel.findViewById(R.id.header).setOnTouchListener(
568                new View.OnTouchListener() {
569                    @Override
570                    public boolean onTouch(View v, MotionEvent event) {
571                        return true; // e eats everything
572                    }
573                });
574
575        if (!ActivityManager.isHighEndGfx()) {
576            mStatusBarWindow.setBackground(null);
577            mNotificationPanel.setBackground(new FastColorDrawable(context.getResources().getColor(
578                    R.color.notification_panel_solid_background)));
579        }
580        if (ENABLE_HEADS_UP) {
581            mHeadsUpNotificationView =
582                    (HeadsUpNotificationView) View.inflate(context, R.layout.heads_up, null);
583            mHeadsUpNotificationView.setVisibility(View.GONE);
584            mHeadsUpNotificationView.setBar(this);
585        }
586        if (MULTIUSER_DEBUG) {
587            mNotificationPanelDebugText = (TextView) mNotificationPanel.findViewById(
588                    R.id.header_debug_info);
589            mNotificationPanelDebugText.setVisibility(View.VISIBLE);
590        }
591
592        updateShowSearchHoldoff();
593
594        try {
595            boolean showNav = mWindowManagerService.hasNavigationBar();
596            if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav);
597            if (showNav) {
598                mNavigationBarView =
599                    (NavigationBarView) View.inflate(context, R.layout.navigation_bar, null);
600
601                mNavigationBarView.setDisabledFlags(mDisabled);
602                mNavigationBarView.setBar(this);
603                mNavigationBarView.setOnTouchListener(new View.OnTouchListener() {
604                    @Override
605                    public boolean onTouch(View v, MotionEvent event) {
606                        checkUserAutohide(v, event);
607                        return false;
608                    }});
609            }
610        } catch (RemoteException ex) {
611            // no window manager? good luck with that
612        }
613
614        // figure out which pixel-format to use for the status bar.
615        mPixelFormat = PixelFormat.OPAQUE;
616
617        mSystemIconArea = (LinearLayout) mStatusBarView.findViewById(R.id.system_icon_area);
618        mSystemIcons = (LinearLayout) mStatusBarView.findViewById(R.id.system_icons);
619        mStatusIcons = (LinearLayout)mStatusBarView.findViewById(R.id.statusIcons);
620        mNotificationIcons = (IconMerger)mStatusBarView.findViewById(R.id.notificationIcons);
621        mMoreIcon = mStatusBarView.findViewById(R.id.moreIcon);
622        mNotificationIcons.setOverflowIndicator(mMoreIcon);
623        mStatusBarContents = (LinearLayout)mStatusBarView.findViewById(R.id.status_bar_contents);
624        mTickerView = mStatusBarView.findViewById(R.id.ticker);
625
626        mStackScroller = (NotificationStackScrollLayout) mStatusBarWindow.findViewById(
627                R.id.notification_stack_scroller);
628        mStackScroller.setLongPressListener(getNotificationLongClicker());
629        mStackScroller.setChildLocationsChangedListener(mOnChildLocationsChangedListener);
630
631        mKeyguardIconOverflowContainer =
632                (NotificationOverflowContainer) LayoutInflater.from(mContext).inflate(
633                        R.layout.status_bar_notification_keyguard_overflow, mStackScroller, false);
634        mKeyguardIconOverflowContainer.setOnActivatedListener(this);
635        mKeyguardIconOverflowContainer.setOnClickListener(mOverflowClickListener);
636        mStackScroller.addView(mKeyguardIconOverflowContainer);
637
638        SpeedBumpView speedBump = (SpeedBumpView) LayoutInflater.from(mContext).inflate(
639                        R.layout.status_bar_notification_speed_bump, mStackScroller, false);
640        mStackScroller.setSpeedBumpView(speedBump);
641
642        mExpandedContents = mStackScroller;
643
644        mHeader = (StatusBarHeaderView) mStatusBarWindow.findViewById(R.id.header);
645        mHeader.setActivityStarter(this);
646        mKeyguardStatusView = mStatusBarWindow.findViewById(R.id.keyguard_status_view);
647        mKeyguardBottomArea =
648                (KeyguardBottomAreaView) mStatusBarWindow.findViewById(R.id.keyguard_bottom_area);
649        mKeyguardBottomArea.setActivityStarter(this);
650        mKeyguardIndicationTextView = (KeyguardIndicationTextView) mStatusBarWindow.findViewById(
651                R.id.keyguard_indication_text);
652        mDateView = (DateView)mStatusBarWindow.findViewById(R.id.date);
653
654        mDateTimeView = mHeader.findViewById(R.id.datetime);
655        if (mDateTimeView != null) {
656            mDateTimeView.setOnClickListener(mClockClickListener);
657            mDateTimeView.setEnabled(true);
658        }
659
660        mNotificationPanel.setSystemUiVisibility(
661                View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS |
662                View.STATUS_BAR_DISABLE_CLOCK);
663
664        mTicker = new MyTicker(context, mStatusBarView);
665
666        TickerView tickerView = (TickerView)mStatusBarView.findViewById(R.id.tickerText);
667        tickerView.mTicker = mTicker;
668
669        mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
670
671        // set the inital view visibility
672        setAreThereNotifications();
673
674        // Other icons
675        mLocationController = new LocationControllerImpl(mContext); // will post a notification
676        mBatteryController = new BatteryController(mContext);
677        mNetworkController = new NetworkControllerImpl(mContext);
678        mBluetoothController = new BluetoothControllerImpl(mContext);
679        if (mContext.getResources().getBoolean(R.bool.config_showRotationLock)) {
680            mRotationLockController = new RotationLockControllerImpl(mContext);
681        }
682        mUserInfoController = new UserInfoController(mContext);
683        mZenModeController = new ZenModeControllerImpl(mContext, mHandler);
684        mCastController = new CastControllerImpl(mContext);
685        final SignalClusterView signalCluster =
686                (SignalClusterView)mStatusBarView.findViewById(R.id.signal_cluster);
687
688
689        mNetworkController.addSignalCluster(signalCluster);
690        signalCluster.setNetworkController(mNetworkController);
691
692        final boolean isAPhone = mNetworkController.hasVoiceCallingFeature();
693        if (isAPhone) {
694            mEmergencyCallLabel =
695                    (TextView) mStatusBarWindow.findViewById(R.id.emergency_calls_only);
696            // TODO: Uncomment when correctly positioned
697//            if (mEmergencyCallLabel != null) {
698//                mNetworkController.addEmergencyLabelView(mEmergencyCallLabel);
699//                mEmergencyCallLabel.setOnClickListener(new View.OnClickListener() {
700//                    public void onClick(View v) { }});
701//                mEmergencyCallLabel.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
702//                    @Override
703//                    public void onLayoutChange(View v, int left, int top, int right, int bottom,
704//                            int oldLeft, int oldTop, int oldRight, int oldBottom) {
705//                        updateCarrierLabelVisibility(false);
706//                    }});
707//            }
708        }
709
710        mCarrierLabel = (TextView)mStatusBarWindow.findViewById(R.id.carrier_label);
711        mShowCarrierInPanel = (mCarrierLabel != null);
712        if (DEBUG) Log.v(TAG, "carrierlabel=" + mCarrierLabel + " show=" + mShowCarrierInPanel);
713        if (mShowCarrierInPanel) {
714            mCarrierLabel.setVisibility(mCarrierLabelVisible ? View.VISIBLE : View.INVISIBLE);
715
716            // for mobile devices, we always show mobile connection info here (SPN/PLMN)
717            // for other devices, we show whatever network is connected
718            if (mNetworkController.hasMobileDataFeature()) {
719                mNetworkController.addMobileLabelView(mCarrierLabel);
720            } else {
721                mNetworkController.addCombinedLabelView(mCarrierLabel);
722            }
723
724            // set up the dynamic hide/show of the label
725            // TODO: uncomment, handle this for the Stack scroller aswell
726//                ((NotificationRowLayout) mStackScroller)
727// .setOnSizeChangedListener(new OnSizeChangedListener() {
728//                @Override
729//                public void onSizeChanged(View view, int w, int h, int oldw, int oldh) {
730//                    updateCarrierLabelVisibility(false);
731        }
732
733        // Set up the quick settings tile panel
734        mQSPanel = (QSPanel) mStatusBarWindow.findViewById(R.id.quick_settings_panel);
735        if (mQSPanel != null) {
736            mQSPanel.setUtils(new CircularClipper.Utils() {
737                @Override
738                public ValueAnimator createRevealAnimator(View v, int centerX, int centerY,
739                        float startRadius, float endRadius) {
740                    return v.createRevealAnimator(centerX, centerY, startRadius, endRadius);
741                }
742            });
743            final QSTileHost qsh = new QSTileHost(mContext, this,
744                    mBluetoothController, mLocationController, mRotationLockController,
745                    mNetworkController, mZenModeController, null /*tethering*/,
746                    mCastController);
747            for (QSTile<?> tile : qsh.getTiles()) {
748                mQSPanel.addTile(tile);
749            }
750            mHeader.setQSPanel(mQSPanel);
751        }
752
753        // User info. Trigger first load.
754        mHeader.setUserInfoController(mUserInfoController);
755        mUserInfoController.reloadUserInfo();
756
757        PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
758        mBroadcastReceiver.onReceive(mContext,
759                new Intent(pm.isScreenOn() ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF));
760
761        // receive broadcasts
762        IntentFilter filter = new IntentFilter();
763        filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
764        filter.addAction(Intent.ACTION_SCREEN_OFF);
765        filter.addAction(Intent.ACTION_SCREEN_ON);
766        filter.addAction(ACTION_DEMO);
767        context.registerReceiver(mBroadcastReceiver, filter);
768
769        // listen for USER_SETUP_COMPLETE setting (per-user)
770        resetUserSetupObserver();
771
772        return mStatusBarView;
773    }
774
775    private void startKeyguard() {
776        KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class);
777        mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this,
778                mStatusBarWindow, mStatusBarWindowManager);
779        mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback();
780    }
781
782    @Override
783    protected void onShowSearchPanel() {
784        if (mNavigationBarView != null) {
785            mNavigationBarView.getBarTransitions().setContentVisible(false);
786        }
787    }
788
789    @Override
790    protected void onHideSearchPanel() {
791        if (mNavigationBarView != null) {
792            mNavigationBarView.getBarTransitions().setContentVisible(true);
793        }
794    }
795
796    @Override
797    protected View getStatusBarView() {
798        return mStatusBarView;
799    }
800
801    @Override
802    protected WindowManager.LayoutParams getSearchLayoutParams(LayoutParams layoutParams) {
803        boolean opaque = false;
804        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
805                LayoutParams.MATCH_PARENT,
806                LayoutParams.MATCH_PARENT,
807                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
808                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
809                | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
810                | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
811                (opaque ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT));
812        if (ActivityManager.isHighEndGfx()) {
813            lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
814        }
815        lp.gravity = Gravity.BOTTOM | Gravity.START;
816        lp.setTitle("SearchPanel");
817        // TODO: Define custom animation for Search panel
818        lp.windowAnimations = com.android.internal.R.style.Animation_RecentApplications;
819        lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
820        | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
821        return lp;
822    }
823
824    @Override
825    protected void updateSearchPanel() {
826        super.updateSearchPanel();
827        if (mNavigationBarView != null) {
828            mNavigationBarView.setDelegateView(mSearchPanelView);
829        }
830    }
831
832    @Override
833    public void showSearchPanel() {
834        super.showSearchPanel();
835        mHandler.removeCallbacks(mShowSearchPanel);
836
837        // we want to freeze the sysui state wherever it is
838        mSearchPanelView.setSystemUiVisibility(mSystemUiVisibility);
839
840        if (mNavigationBarView != null) {
841            WindowManager.LayoutParams lp =
842                (android.view.WindowManager.LayoutParams) mNavigationBarView.getLayoutParams();
843            lp.flags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
844            mWindowManager.updateViewLayout(mNavigationBarView, lp);
845        }
846    }
847
848    @Override
849    public void hideSearchPanel() {
850        super.hideSearchPanel();
851        if (mNavigationBarView != null) {
852            WindowManager.LayoutParams lp =
853                (android.view.WindowManager.LayoutParams) mNavigationBarView.getLayoutParams();
854            lp.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
855            mWindowManager.updateViewLayout(mNavigationBarView, lp);
856        }
857    }
858
859    public int getStatusBarHeight() {
860        if (mNaturalBarHeight < 0) {
861            final Resources res = mContext.getResources();
862            mNaturalBarHeight =
863                    res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
864        }
865        return mNaturalBarHeight;
866    }
867
868    private View.OnClickListener mRecentsClickListener = new View.OnClickListener() {
869        public void onClick(View v) {
870            awakenDreams();
871            toggleRecentApps();
872        }
873    };
874
875    private int mShowSearchHoldoff = 0;
876    private Runnable mShowSearchPanel = new Runnable() {
877        public void run() {
878            showSearchPanel();
879            awakenDreams();
880        }
881    };
882
883    View.OnTouchListener mHomeSearchActionListener = new View.OnTouchListener() {
884        public boolean onTouch(View v, MotionEvent event) {
885            switch(event.getAction()) {
886            case MotionEvent.ACTION_DOWN:
887                if (!shouldDisableNavbarGestures()) {
888                    mHandler.removeCallbacks(mShowSearchPanel);
889                    mHandler.postDelayed(mShowSearchPanel, mShowSearchHoldoff);
890                }
891            break;
892
893            case MotionEvent.ACTION_UP:
894            case MotionEvent.ACTION_CANCEL:
895                mHandler.removeCallbacks(mShowSearchPanel);
896                awakenDreams();
897            break;
898        }
899        return false;
900        }
901    };
902
903    private void awakenDreams() {
904        if (mDreamManager != null) {
905            try {
906                mDreamManager.awaken();
907            } catch (RemoteException e) {
908                // fine, stay asleep then
909            }
910        }
911    }
912
913    private void prepareNavigationBarView() {
914        mNavigationBarView.reorient();
915
916        mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
917        mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPreloadOnTouchListener);
918        mNavigationBarView.getHomeButton().setOnTouchListener(mHomeSearchActionListener);
919        mNavigationBarView.getSearchLight().setOnTouchListener(mHomeSearchActionListener);
920        updateSearchPanel();
921    }
922
923    // For small-screen devices (read: phones) that lack hardware navigation buttons
924    private void addNavigationBar() {
925        if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + mNavigationBarView);
926        if (mNavigationBarView == null) return;
927
928        prepareNavigationBarView();
929
930        mWindowManager.addView(mNavigationBarView, getNavigationBarLayoutParams());
931    }
932
933    private void repositionNavigationBar() {
934        if (mNavigationBarView == null || !mNavigationBarView.isAttachedToWindow()) return;
935
936        prepareNavigationBarView();
937
938        mWindowManager.updateViewLayout(mNavigationBarView, getNavigationBarLayoutParams());
939    }
940
941    private void notifyNavigationBarScreenOn(boolean screenOn) {
942        if (mNavigationBarView == null) return;
943        mNavigationBarView.notifyScreenOn(screenOn);
944    }
945
946    private WindowManager.LayoutParams getNavigationBarLayoutParams() {
947        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
948                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
949                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
950                    0
951                    | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
952                    | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
953                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
954                    | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
955                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
956                PixelFormat.TRANSLUCENT);
957        // this will allow the navbar to run in an overlay on devices that support this
958        if (ActivityManager.isHighEndGfx()) {
959            lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
960        }
961
962        lp.setTitle("NavigationBar");
963        lp.windowAnimations = 0;
964        return lp;
965    }
966
967    private void addHeadsUpView() {
968        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
969                LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT,
970                WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, // above the status bar!
971                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
972                    | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
973                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
974                    | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
975                    | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
976                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
977                PixelFormat.TRANSLUCENT);
978        lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
979        lp.gravity = Gravity.TOP;
980        lp.setTitle("Heads Up");
981        lp.packageName = mContext.getPackageName();
982        lp.windowAnimations = R.style.Animation_StatusBar_HeadsUp;
983
984        mWindowManager.addView(mHeadsUpNotificationView, lp);
985    }
986
987    private void removeHeadsUpView() {
988        mWindowManager.removeView(mHeadsUpNotificationView);
989    }
990
991    public void refreshAllStatusBarIcons() {
992        refreshAllIconsForLayout(mStatusIcons);
993        refreshAllIconsForLayout(mNotificationIcons);
994    }
995
996    private void refreshAllIconsForLayout(LinearLayout ll) {
997        final int count = ll.getChildCount();
998        for (int n = 0; n < count; n++) {
999            View child = ll.getChildAt(n);
1000            if (child instanceof StatusBarIconView) {
1001                ((StatusBarIconView) child).updateDrawable();
1002            }
1003        }
1004    }
1005
1006    public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
1007        if (SPEW) Log.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
1008                + " icon=" + icon);
1009        StatusBarIconView view = new StatusBarIconView(mContext, slot, null);
1010        view.set(icon);
1011        mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(mIconSize, mIconSize));
1012    }
1013
1014    public void updateIcon(String slot, int index, int viewIndex,
1015            StatusBarIcon old, StatusBarIcon icon) {
1016        if (SPEW) Log.d(TAG, "updateIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
1017                + " old=" + old + " icon=" + icon);
1018        StatusBarIconView view = (StatusBarIconView)mStatusIcons.getChildAt(viewIndex);
1019        view.set(icon);
1020    }
1021
1022    public void removeIcon(String slot, int index, int viewIndex) {
1023        if (SPEW) Log.d(TAG, "removeIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex);
1024        mStatusIcons.removeViewAt(viewIndex);
1025    }
1026
1027    public UserHandle getCurrentUserHandle() {
1028        return new UserHandle(mCurrentUserId);
1029    }
1030
1031    @Override
1032    public void addNotificationInternal(StatusBarNotification notification, Ranking ranking) {
1033        if (DEBUG) Log.d(TAG, "addNotification score=" + notification.getScore());
1034        Entry shadeEntry = createNotificationViews(notification);
1035        if (shadeEntry == null) {
1036            return;
1037        }
1038        if (mZenMode != Global.ZEN_MODE_OFF && mIntercepted.tryIntercept(notification)) {
1039            // Forward the ranking so we can sort the new notification.
1040            mNotificationData.updateRanking(ranking);
1041            return;
1042        }
1043        if (mUseHeadsUp && shouldInterrupt(notification)) {
1044            if (DEBUG) Log.d(TAG, "launching notification in heads up mode");
1045            Entry interruptionCandidate = new Entry(notification, null);
1046            ViewGroup holder = mHeadsUpNotificationView.getHolder();
1047            if (inflateViewsForHeadsUp(interruptionCandidate, holder)) {
1048                mInterruptingNotificationTime = System.currentTimeMillis();
1049                mInterruptingNotificationEntry = interruptionCandidate;
1050                shadeEntry.setInterruption();
1051
1052                // 1. Populate mHeadsUpNotificationView
1053                mHeadsUpNotificationView.setNotification(mInterruptingNotificationEntry);
1054
1055                // 2. Animate mHeadsUpNotificationView in
1056                mHandler.sendEmptyMessage(MSG_SHOW_HEADS_UP);
1057
1058                // 3. Set alarm to age the notification off
1059                resetHeadsUpDecayTimer();
1060            }
1061        } else if (notification.getNotification().fullScreenIntent != null) {
1062            // Stop screensaver if the notification has a full-screen intent.
1063            // (like an incoming phone call)
1064            awakenDreams();
1065
1066            // not immersive & a full-screen alert should be shown
1067            if (DEBUG) Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
1068            try {
1069                notification.getNotification().fullScreenIntent.send();
1070            } catch (PendingIntent.CanceledException e) {
1071            }
1072        } else {
1073            // usual case: status bar visible & not immersive
1074
1075            // show the ticker if there isn't already a heads up
1076            if (mInterruptingNotificationEntry == null) {
1077                tick(notification, true);
1078            }
1079        }
1080        addNotificationViews(shadeEntry, ranking);
1081        // Recalculate the position of the sliding windows and the titles.
1082        setAreThereNotifications();
1083        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
1084    }
1085
1086    @Override
1087    public void resetHeadsUpDecayTimer() {
1088        mHandler.removeMessages(MSG_HIDE_HEADS_UP);
1089        if (mUseHeadsUp && mHeadsUpNotificationDecay > 0
1090                && mHeadsUpNotificationView.isClearable()) {
1091            mHandler.sendEmptyMessageDelayed(MSG_HIDE_HEADS_UP, mHeadsUpNotificationDecay);
1092        }
1093    }
1094
1095    @Override
1096    public void updateNotification(StatusBarNotification notification) {
1097        super.updateNotification(notification);
1098        mIntercepted.update(notification);
1099    }
1100
1101    @Override
1102    public void removeNotificationInternal(String key, Ranking ranking) {
1103        StatusBarNotification old = removeNotificationViews(key, ranking);
1104        if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
1105
1106        if (old != null) {
1107            // Cancel the ticker if it's still running
1108            mTicker.removeEntry(old);
1109
1110            // Recalculate the position of the sliding windows and the titles.
1111            updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
1112
1113            if (ENABLE_HEADS_UP && mInterruptingNotificationEntry != null
1114                    && old == mInterruptingNotificationEntry.notification) {
1115                mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
1116            }
1117
1118            if (CLOSE_PANEL_WHEN_EMPTIED && mNotificationData.size() == 0
1119                    && !mNotificationPanel.isTracking() && mState != StatusBarState.KEYGUARD) {
1120                animateCollapsePanels();
1121            }
1122        }
1123        mIntercepted.remove(key);
1124        setAreThereNotifications();
1125    }
1126
1127    @Override
1128    protected void refreshLayout(int layoutDirection) {
1129        if (mNavigationBarView != null) {
1130            mNavigationBarView.setLayoutDirection(layoutDirection);
1131        }
1132        refreshAllStatusBarIcons();
1133    }
1134
1135    private void updateShowSearchHoldoff() {
1136        mShowSearchHoldoff = mContext.getResources().getInteger(
1137            R.integer.config_show_search_delay);
1138    }
1139
1140    private void updateNotificationShade() {
1141        if (mStackScroller == null) return;
1142
1143        int N = mNotificationData.size();
1144
1145        ArrayList<View> toShow = new ArrayList<View>();
1146
1147        final boolean provisioned = isDeviceProvisioned();
1148        // If the device hasn't been through Setup, we only show system notifications
1149        for (int i=0; i<N; i++) {
1150            Entry ent = mNotificationData.get(i);
1151            if (!(provisioned || showNotificationEvenIfUnprovisioned(ent.notification))) continue;
1152
1153            // TODO How do we want to badge notifcations from profiles.
1154            if (!notificationIsForCurrentProfiles(ent.notification)) continue;
1155
1156            final int vis = ent.notification.getNotification().visibility;
1157            if (vis != Notification.VISIBILITY_SECRET) {
1158                // when isLockscreenPublicMode() we show the public form of VISIBILITY_PRIVATE notifications
1159                ent.row.setShowingPublic(isLockscreenPublicMode()
1160                        && vis == Notification.VISIBILITY_PRIVATE
1161                        && !userAllowsPrivateNotificationsInPublic(ent.notification.getUserId()));
1162                toShow.add(ent.row);
1163            }
1164        }
1165
1166        ArrayList<View> toRemove = new ArrayList<View>();
1167        for (int i=0; i< mStackScroller.getChildCount(); i++) {
1168            View child = mStackScroller.getChildAt(i);
1169            if (!toShow.contains(child) && child instanceof ExpandableNotificationRow) {
1170                toRemove.add(child);
1171            }
1172        }
1173
1174        for (View remove : toRemove) {
1175            mStackScroller.removeView(remove);
1176        }
1177
1178        for (int i=0; i<toShow.size(); i++) {
1179            View v = toShow.get(i);
1180            if (v.getParent() == null) {
1181                mStackScroller.addView(v);
1182            }
1183        }
1184
1185        // So after all this work notifications still aren't sorted correctly.
1186        // Let's do that now by advancing through toShow and mStackScroller in
1187        // lock-step, making sure mStackScroller matches what we see in toShow.
1188        int j = 0;
1189        for (int i = 0; i < mStackScroller.getChildCount(); i++) {
1190            View child = mStackScroller.getChildAt(i);
1191            if (!(child instanceof ExpandableNotificationRow)) {
1192                // We don't care about non-notification views.
1193                continue;
1194            }
1195
1196            if (child == toShow.get(j)) {
1197                // Everything is well, advance both lists.
1198                j++;
1199                continue;
1200            }
1201
1202            // Oops, wrong notification at this position. Put the right one
1203            // here and advance both lists.
1204            mStackScroller.changeViewPosition(toShow.get(j), i);
1205            j++;
1206        }
1207        updateRowStates();
1208        updateSpeedbump();
1209        mNotificationPanel.setQsExpansionEnabled(provisioned && mUserSetup);
1210    }
1211
1212    private void updateSpeedbump() {
1213        int speedbumpIndex = -1;
1214        int currentIndex = 0;
1215        for (int i = 0; i < mNotificationData.size(); i++) {
1216            Entry entry = mNotificationData.get(i);
1217            if (entry.row.getParent() == null) {
1218                // This view isn't even added, so the stack scroller doesn't
1219                // know about it. Ignore completely.
1220                continue;
1221            }
1222            if (entry.row.getVisibility() != View.GONE &&
1223                    mNotificationData.isAmbient(entry.key)) {
1224                speedbumpIndex = currentIndex;
1225                break;
1226            }
1227            currentIndex++;
1228        }
1229        mStackScroller.updateSpeedBumpIndex(speedbumpIndex);
1230    }
1231
1232    @Override
1233    protected void updateNotifications() {
1234        // TODO: Move this into updateNotificationIcons()?
1235        if (mNotificationIcons == null) return;
1236
1237        updateNotificationShade();
1238        updateNotificationIcons();
1239    }
1240
1241    private void updateNotificationIcons() {
1242        final LinearLayout.LayoutParams params
1243            = new LinearLayout.LayoutParams(mIconSize + 2*mIconHPadding, mNaturalBarHeight);
1244
1245        int N = mNotificationData.size();
1246
1247        if (DEBUG) {
1248            Log.d(TAG, "refreshing icons: " + N + " notifications, mNotificationIcons=" +
1249                    mNotificationIcons);
1250        }
1251
1252        ArrayList<View> toShow = new ArrayList<View>();
1253
1254        final boolean provisioned = isDeviceProvisioned();
1255        // If the device hasn't been through Setup, we only show system notifications
1256        for (int i=0; i<N; i++) {
1257            Entry ent = mNotificationData.get(i);
1258            if (!((provisioned && ent.notification.getScore() >= HIDE_ICONS_BELOW_SCORE)
1259                    || showNotificationEvenIfUnprovisioned(ent.notification))) continue;
1260            if (!notificationIsForCurrentProfiles(ent.notification)) continue;
1261            if (isLockscreenPublicMode()
1262                    && ent.notification.getNotification().visibility
1263                            == Notification.VISIBILITY_SECRET
1264                    && !userAllowsPrivateNotificationsInPublic(ent.notification.getUserId())) {
1265                // in "public" mode (atop a secure keyguard), secret notifs are totally hidden
1266                continue;
1267            }
1268            if (mIntercepted.isSyntheticEntry(ent)) {
1269                continue;
1270            }
1271            toShow.add(ent.icon);
1272        }
1273
1274        ArrayList<View> toRemove = new ArrayList<View>();
1275        for (int i=0; i<mNotificationIcons.getChildCount(); i++) {
1276            View child = mNotificationIcons.getChildAt(i);
1277            if (!toShow.contains(child)) {
1278                toRemove.add(child);
1279            }
1280        }
1281
1282        for (View remove : toRemove) {
1283            mNotificationIcons.removeView(remove);
1284        }
1285
1286        for (int i=0; i<toShow.size(); i++) {
1287            View v = toShow.get(i);
1288            if (v.getParent() == null) {
1289                mNotificationIcons.addView(v, i, params);
1290            }
1291        }
1292    }
1293
1294    protected void updateCarrierLabelVisibility(boolean force) {
1295        // TODO: Handle this for the notification stack scroller as well
1296        if (!mShowCarrierInPanel) return;
1297        // The idea here is to only show the carrier label when there is enough room to see it,
1298        // i.e. when there aren't enough notifications to fill the panel.
1299        if (SPEW) {
1300            Log.d(TAG, String.format("stackScrollerh=%d scrollh=%d carrierh=%d",
1301                    mStackScroller.getHeight(), mStackScroller.getHeight(),
1302                    mCarrierLabelHeight));
1303        }
1304
1305        final boolean emergencyCallsShownElsewhere = mEmergencyCallLabel != null;
1306        final boolean makeVisible =
1307            !(emergencyCallsShownElsewhere && mNetworkController.isEmergencyOnly())
1308            && mStackScroller.getHeight() < (mNotificationPanel.getHeight()
1309                    - mCarrierLabelHeight - mStatusBarHeaderHeight)
1310            && mStackScroller.getVisibility() == View.VISIBLE
1311            && mState != StatusBarState.KEYGUARD;
1312
1313        if (force || mCarrierLabelVisible != makeVisible) {
1314            mCarrierLabelVisible = makeVisible;
1315            if (DEBUG) {
1316                Log.d(TAG, "making carrier label " + (makeVisible?"visible":"invisible"));
1317            }
1318            mCarrierLabel.animate().cancel();
1319            if (makeVisible) {
1320                mCarrierLabel.setVisibility(View.VISIBLE);
1321            }
1322            mCarrierLabel.animate()
1323                .alpha(makeVisible ? 1f : 0f)
1324                //.setStartDelay(makeVisible ? 500 : 0)
1325                //.setDuration(makeVisible ? 750 : 100)
1326                .setDuration(150)
1327                .setListener(makeVisible ? null : new AnimatorListenerAdapter() {
1328                    @Override
1329                    public void onAnimationEnd(Animator animation) {
1330                        if (!mCarrierLabelVisible) { // race
1331                            mCarrierLabel.setVisibility(View.INVISIBLE);
1332                            mCarrierLabel.setAlpha(0f);
1333                        }
1334                    }
1335                })
1336                .start();
1337        }
1338    }
1339
1340    @Override
1341    protected void setAreThereNotifications() {
1342        final boolean any = mNotificationData.size() > 0;
1343
1344        final boolean clearable = any && mNotificationData.hasClearableItems();
1345
1346        if (SPEW) {
1347            Log.d(TAG, "setAreThereNotifications: N=" + mNotificationData.size()
1348                    + " any=" + any + " clearable=" + clearable);
1349        }
1350
1351        final View nlo = mStatusBarView.findViewById(R.id.notification_lights_out);
1352        final boolean showDot = (any&&!areLightsOn());
1353        if (showDot != (nlo.getAlpha() == 1.0f)) {
1354            if (showDot) {
1355                nlo.setAlpha(0f);
1356                nlo.setVisibility(View.VISIBLE);
1357            }
1358            nlo.animate()
1359                .alpha(showDot?1:0)
1360                .setDuration(showDot?750:250)
1361                .setInterpolator(new AccelerateInterpolator(2.0f))
1362                .setListener(showDot ? null : new AnimatorListenerAdapter() {
1363                    @Override
1364                    public void onAnimationEnd(Animator _a) {
1365                        nlo.setVisibility(View.GONE);
1366                    }
1367                })
1368                .start();
1369        }
1370
1371        updateCarrierLabelVisibility(false);
1372    }
1373
1374    public void showClock(boolean show) {
1375        if (mStatusBarView == null) return;
1376        View clock = mStatusBarView.findViewById(R.id.clock);
1377        if (clock != null) {
1378            clock.setVisibility(show ? View.VISIBLE : View.GONE);
1379        }
1380    }
1381
1382    /**
1383     * State is one or more of the DISABLE constants from StatusBarManager.
1384     */
1385    public void disable(int state) {
1386        final int old = mDisabled;
1387        final int diff = state ^ old;
1388        mDisabled = state;
1389
1390        if (DEBUG) {
1391            Log.d(TAG, String.format("disable: 0x%08x -> 0x%08x (diff: 0x%08x)",
1392                old, state, diff));
1393        }
1394
1395        StringBuilder flagdbg = new StringBuilder();
1396        flagdbg.append("disable: < ");
1397        flagdbg.append(((state & StatusBarManager.DISABLE_EXPAND) != 0) ? "EXPAND" : "expand");
1398        flagdbg.append(((diff  & StatusBarManager.DISABLE_EXPAND) != 0) ? "* " : " ");
1399        flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "ICONS" : "icons");
1400        flagdbg.append(((diff  & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "* " : " ");
1401        flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "ALERTS" : "alerts");
1402        flagdbg.append(((diff  & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "* " : " ");
1403        flagdbg.append(((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "SYSTEM_INFO" : "system_info");
1404        flagdbg.append(((diff  & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "* " : " ");
1405        flagdbg.append(((state & StatusBarManager.DISABLE_BACK) != 0) ? "BACK" : "back");
1406        flagdbg.append(((diff  & StatusBarManager.DISABLE_BACK) != 0) ? "* " : " ");
1407        flagdbg.append(((state & StatusBarManager.DISABLE_HOME) != 0) ? "HOME" : "home");
1408        flagdbg.append(((diff  & StatusBarManager.DISABLE_HOME) != 0) ? "* " : " ");
1409        flagdbg.append(((state & StatusBarManager.DISABLE_RECENT) != 0) ? "RECENT" : "recent");
1410        flagdbg.append(((diff  & StatusBarManager.DISABLE_RECENT) != 0) ? "* " : " ");
1411        flagdbg.append(((state & StatusBarManager.DISABLE_CLOCK) != 0) ? "CLOCK" : "clock");
1412        flagdbg.append(((diff  & StatusBarManager.DISABLE_CLOCK) != 0) ? "* " : " ");
1413        flagdbg.append(((state & StatusBarManager.DISABLE_SEARCH) != 0) ? "SEARCH" : "search");
1414        flagdbg.append(((diff  & StatusBarManager.DISABLE_SEARCH) != 0) ? "* " : " ");
1415        flagdbg.append(">");
1416        Log.d(TAG, flagdbg.toString());
1417
1418        if ((diff & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
1419            mSystemIconArea.animate().cancel();
1420            if ((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
1421                mSystemIconArea.animate()
1422                    .alpha(0f)
1423                    .translationY(mNaturalBarHeight*0.5f)
1424                    .setDuration(175)
1425                    .setInterpolator(new DecelerateInterpolator(1.5f))
1426                    .setListener(mMakeIconsInvisible)
1427                    .start();
1428            } else {
1429                mSystemIconArea.setVisibility(View.VISIBLE);
1430                mSystemIconArea.animate()
1431                    .alpha(1f)
1432                    .translationY(0)
1433                    .setStartDelay(0)
1434                    .setInterpolator(new DecelerateInterpolator(1.5f))
1435                    .setDuration(175)
1436                    .start();
1437            }
1438        }
1439
1440        if ((diff & StatusBarManager.DISABLE_CLOCK) != 0) {
1441            boolean show = (state & StatusBarManager.DISABLE_CLOCK) == 0;
1442            showClock(show);
1443        }
1444        if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
1445            if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {
1446                animateCollapsePanels();
1447            }
1448        }
1449
1450        if ((diff & (StatusBarManager.DISABLE_HOME
1451                        | StatusBarManager.DISABLE_RECENT
1452                        | StatusBarManager.DISABLE_BACK
1453                        | StatusBarManager.DISABLE_SEARCH)) != 0) {
1454            // the nav bar will take care of these
1455            if (mNavigationBarView != null) mNavigationBarView.setDisabledFlags(state);
1456
1457            if ((state & StatusBarManager.DISABLE_RECENT) != 0) {
1458                // close recents if it's visible
1459                mHandler.removeMessages(MSG_HIDE_RECENT_APPS);
1460                mHandler.sendEmptyMessage(MSG_HIDE_RECENT_APPS);
1461            }
1462        }
1463
1464        if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
1465            if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
1466                if (mTicking) {
1467                    haltTicker();
1468                }
1469
1470                mNotificationIcons.animate()
1471                    .alpha(0f)
1472                    .translationY(mNaturalBarHeight*0.5f)
1473                    .setDuration(175)
1474                    .setInterpolator(new DecelerateInterpolator(1.5f))
1475                    .setListener(mMakeIconsInvisible)
1476                    .start();
1477            } else {
1478                mNotificationIcons.setVisibility(View.VISIBLE);
1479                mNotificationIcons.animate()
1480                    .alpha(1f)
1481                    .translationY(0)
1482                    .setStartDelay(0)
1483                    .setInterpolator(new DecelerateInterpolator(1.5f))
1484                    .setDuration(175)
1485                    .start();
1486            }
1487        }
1488    }
1489
1490    @Override
1491    protected BaseStatusBar.H createHandler() {
1492        return new PhoneStatusBar.H();
1493    }
1494
1495    @Override
1496    public void startActivity(Intent intent) {
1497        startActivityDismissingKeyguard(intent, false);
1498    }
1499
1500    /**
1501     * All changes to the status bar and notifications funnel through here and are batched.
1502     */
1503    private class H extends BaseStatusBar.H {
1504        public void handleMessage(Message m) {
1505            super.handleMessage(m);
1506            switch (m.what) {
1507                case MSG_OPEN_NOTIFICATION_PANEL:
1508                    animateExpandNotificationsPanel();
1509                    break;
1510                case MSG_OPEN_SETTINGS_PANEL:
1511                    animateExpandSettingsPanel();
1512                    break;
1513                case MSG_CLOSE_PANELS:
1514                    animateCollapsePanels();
1515                    break;
1516                case MSG_SHOW_HEADS_UP:
1517                    setHeadsUpVisibility(true);
1518                    break;
1519                case MSG_HIDE_HEADS_UP:
1520                    setHeadsUpVisibility(false);
1521                    break;
1522                case MSG_ESCALATE_HEADS_UP:
1523                    escalateHeadsUp();
1524                    setHeadsUpVisibility(false);
1525                    break;
1526            }
1527        }
1528    }
1529
1530    /**  if the interrupting notification had a fullscreen intent, fire it now.  */
1531    private void escalateHeadsUp() {
1532        if (mInterruptingNotificationEntry != null) {
1533            final StatusBarNotification sbn = mInterruptingNotificationEntry.notification;
1534            final Notification notification = sbn.getNotification();
1535            if (notification.fullScreenIntent != null) {
1536                if (DEBUG)
1537                    Log.d(TAG, "converting a heads up to fullScreen");
1538                try {
1539                    notification.fullScreenIntent.send();
1540                } catch (PendingIntent.CanceledException e) {
1541                }
1542            }
1543        }
1544    }
1545
1546    private Handler getHandler() {
1547        return mHandler;
1548    }
1549
1550    View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() {
1551        public void onFocusChange(View v, boolean hasFocus) {
1552            // Because 'v' is a ViewGroup, all its children will be (un)selected
1553            // too, which allows marqueeing to work.
1554            v.setSelected(hasFocus);
1555        }
1556    };
1557
1558    boolean panelsEnabled() {
1559        return (mDisabled & StatusBarManager.DISABLE_EXPAND) == 0;
1560    }
1561
1562    void makeExpandedVisible(boolean force) {
1563        if (SPEW) Log.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
1564        if (!force && (mExpandedVisible || !panelsEnabled())) {
1565            return;
1566        }
1567
1568        mExpandedVisible = true;
1569        if (mNavigationBarView != null)
1570            mNavigationBarView.setSlippery(true);
1571
1572        updateCarrierLabelVisibility(true);
1573
1574        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
1575
1576        // Expand the window to encompass the full screen in anticipation of the drag.
1577        // This is only possible to do atomically because the status bar is at the top of the screen!
1578        mStatusBarWindowManager.setStatusBarExpanded(true);
1579
1580        visibilityChanged(true);
1581
1582        setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true);
1583    }
1584
1585    public void animateCollapsePanels() {
1586        animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
1587    }
1588
1589    private final Runnable mAnimateCollapsePanels = new Runnable() {
1590        @Override
1591        public void run() {
1592            animateCollapsePanels();
1593        }
1594    };
1595
1596    public void postAnimateCollapsePanels() {
1597        mHandler.post(mAnimateCollapsePanels);
1598    }
1599
1600    public void animateCollapsePanels(int flags) {
1601        if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
1602            return;
1603        }
1604        if (SPEW) {
1605            Log.d(TAG, "animateCollapse():"
1606                    + " mExpandedVisible=" + mExpandedVisible
1607                    + " flags=" + flags);
1608        }
1609
1610        if ((flags & CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL) == 0) {
1611            mHandler.removeMessages(MSG_HIDE_RECENT_APPS);
1612            mHandler.sendEmptyMessage(MSG_HIDE_RECENT_APPS);
1613        }
1614
1615        if ((flags & CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL) == 0) {
1616            mHandler.removeMessages(MSG_CLOSE_SEARCH_PANEL);
1617            mHandler.sendEmptyMessage(MSG_CLOSE_SEARCH_PANEL);
1618        }
1619
1620        if (mStatusBarWindow != null) {
1621
1622            // release focus immediately to kick off focus change transition
1623            mStatusBarWindowManager.setStatusBarFocusable(false);
1624
1625            mStatusBarWindow.cancelExpandHelper();
1626            mStatusBarView.collapseAllPanels(true);
1627            if (isFlippedToSettings()) {
1628                flipToNotifications(true /*animate*/);
1629            }
1630        }
1631    }
1632
1633    public ViewPropertyAnimator setVisibilityWhenDone(
1634            final ViewPropertyAnimator a, final View v, final int vis) {
1635        a.setListener(new AnimatorListenerAdapter() {
1636            @Override
1637            public void onAnimationEnd(Animator animation) {
1638                v.setVisibility(vis);
1639                a.setListener(null); // oneshot
1640            }
1641        });
1642        return a;
1643    }
1644
1645    public Animator setVisibilityWhenDone(
1646            final Animator a, final View v, final int vis) {
1647        a.addListener(new AnimatorListenerAdapter() {
1648            @Override
1649            public void onAnimationEnd(Animator animation) {
1650                v.setVisibility(vis);
1651            }
1652        });
1653        return a;
1654    }
1655
1656    public Animator interpolator(TimeInterpolator ti, Animator a) {
1657        a.setInterpolator(ti);
1658        return a;
1659    }
1660
1661    public Animator startDelay(int d, Animator a) {
1662        a.setStartDelay(d);
1663        return a;
1664    }
1665
1666    public Animator start(Animator a) {
1667        a.start();
1668        return a;
1669    }
1670
1671    final TimeInterpolator mAccelerateInterpolator = new AccelerateInterpolator();
1672    final TimeInterpolator mDecelerateInterpolator = new DecelerateInterpolator();
1673    final int FLIP_DURATION_OUT = 125;
1674    final int FLIP_DURATION_IN = 225;
1675    final int FLIP_DURATION = (FLIP_DURATION_IN + FLIP_DURATION_OUT);
1676
1677    Animator mScrollViewAnim, mClearButtonAnim;
1678
1679    @Override
1680    public void animateExpandNotificationsPanel() {
1681        if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible);
1682        if (!panelsEnabled()) {
1683            return ;
1684        }
1685
1686        mNotificationPanel.expand();
1687        if (mStackScroller.getVisibility() != View.VISIBLE) {
1688            flipToNotifications(true /*animate*/);
1689        }
1690
1691        if (false) postStartTracing();
1692    }
1693
1694    private static void cancelAnim(Animator anim) {
1695        if (anim != null) {
1696            anim.cancel();
1697        }
1698    }
1699
1700    public void flipToNotifications(boolean animate) {
1701        // TODO: Animation
1702        mNotificationPanel.closeQs();
1703    }
1704
1705    @Override
1706    public void animateExpandSettingsPanel() {
1707        if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible);
1708        if (!panelsEnabled()) {
1709            return;
1710        }
1711
1712        // Settings are not available in setup
1713        if (!mUserSetup) return;
1714
1715        mNotificationPanel.expand();
1716        mNotificationPanel.openQs();
1717
1718        if (false) postStartTracing();
1719    }
1720
1721    public boolean isFlippedToSettings() {
1722        if (mNotificationPanel != null) {
1723            return mNotificationPanel.isQsExpanded();
1724        }
1725        return false;
1726    }
1727
1728    public void animateCollapseQuickSettings() {
1729        mStatusBarView.collapseAllPanels(true);
1730    }
1731
1732    void makeExpandedInvisibleSoon() {
1733        mHandler.postDelayed(new Runnable() { public void run() { makeExpandedInvisible(); }}, 50);
1734    }
1735
1736    void makeExpandedInvisible() {
1737        if (SPEW) Log.d(TAG, "makeExpandedInvisible: mExpandedVisible=" + mExpandedVisible
1738                + " mExpandedVisible=" + mExpandedVisible);
1739
1740        if (!mExpandedVisible || mStatusBarWindow == null) {
1741            return;
1742        }
1743
1744        // Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868)
1745        mStatusBarView.collapseAllPanels(/*animate=*/ false);
1746
1747        // reset things to their proper state
1748        if (mScrollViewAnim != null) mScrollViewAnim.cancel();
1749        if (mClearButtonAnim != null) mClearButtonAnim.cancel();
1750
1751        mStackScroller.setVisibility(View.VISIBLE);
1752        mNotificationPanel.setVisibility(View.GONE);
1753
1754        setAreThereNotifications(); // show the clear button
1755
1756        mNotificationPanel.closeQs();
1757
1758        mExpandedVisible = false;
1759        if (mNavigationBarView != null)
1760            mNavigationBarView.setSlippery(false);
1761        visibilityChanged(false);
1762
1763        // Shrink the window to the size of the status bar only
1764        mStatusBarWindowManager.setStatusBarExpanded(false);
1765
1766        if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) {
1767            setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
1768        }
1769
1770        // Close any "App info" popups that might have snuck on-screen
1771        dismissPopups();
1772
1773        if (mPostCollapseCleanup != null) {
1774            mPostCollapseCleanup.run();
1775            mPostCollapseCleanup = null;
1776        }
1777
1778        setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
1779
1780        showBouncer();
1781    }
1782
1783    public boolean interceptTouchEvent(MotionEvent event) {
1784        if (DEBUG_GESTURES) {
1785            if (event.getActionMasked() != MotionEvent.ACTION_MOVE) {
1786                EventLog.writeEvent(EventLogTags.SYSUI_STATUSBAR_TOUCH,
1787                        event.getActionMasked(), (int) event.getX(), (int) event.getY(), mDisabled);
1788            }
1789
1790        }
1791
1792        if (SPEW) {
1793            Log.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled="
1794                + mDisabled + " mTracking=" + mTracking);
1795        } else if (CHATTY) {
1796            if (event.getAction() != MotionEvent.ACTION_MOVE) {
1797                Log.d(TAG, String.format(
1798                            "panel: %s at (%f, %f) mDisabled=0x%08x",
1799                            MotionEvent.actionToString(event.getAction()),
1800                            event.getRawX(), event.getRawY(), mDisabled));
1801            }
1802        }
1803
1804        if (DEBUG_GESTURES) {
1805            mGestureRec.add(event);
1806        }
1807
1808        if (mStatusBarWindowState == WINDOW_STATE_SHOWING) {
1809            final boolean upOrCancel =
1810                    event.getAction() == MotionEvent.ACTION_UP ||
1811                    event.getAction() == MotionEvent.ACTION_CANCEL;
1812            if (upOrCancel && !mExpandedVisible) {
1813                setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
1814            } else {
1815                setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true);
1816            }
1817        }
1818        return false;
1819    }
1820
1821    public GestureRecorder getGestureRecorder() {
1822        return mGestureRec;
1823    }
1824
1825    private void setNavigationIconHints(int hints) {
1826        if (hints == mNavigationIconHints) return;
1827
1828        mNavigationIconHints = hints;
1829
1830        if (mNavigationBarView != null) {
1831            mNavigationBarView.setNavigationIconHints(hints);
1832        }
1833        checkBarModes();
1834    }
1835
1836    @Override // CommandQueue
1837    public void setWindowState(int window, int state) {
1838        boolean showing = state == WINDOW_STATE_SHOWING;
1839        if (mStatusBarWindow != null
1840                && window == StatusBarManager.WINDOW_STATUS_BAR
1841                && mStatusBarWindowState != state) {
1842            mStatusBarWindowState = state;
1843            if (DEBUG_WINDOW_STATE) Log.d(TAG, "Status bar " + windowStateToString(state));
1844            if (!showing) {
1845                mStatusBarView.collapseAllPanels(false);
1846            }
1847        }
1848        if (mNavigationBarView != null
1849                && window == StatusBarManager.WINDOW_NAVIGATION_BAR
1850                && mNavigationBarWindowState != state) {
1851            mNavigationBarWindowState = state;
1852            if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state));
1853        }
1854    }
1855
1856    @Override // CommandQueue
1857    public void setSystemUiVisibility(int vis, int mask) {
1858        final int oldVal = mSystemUiVisibility;
1859        final int newVal = (oldVal&~mask) | (vis&mask);
1860        final int diff = newVal ^ oldVal;
1861        if (DEBUG) Log.d(TAG, String.format(
1862                "setSystemUiVisibility vis=%s mask=%s oldVal=%s newVal=%s diff=%s",
1863                Integer.toHexString(vis), Integer.toHexString(mask),
1864                Integer.toHexString(oldVal), Integer.toHexString(newVal),
1865                Integer.toHexString(diff)));
1866        if (diff != 0) {
1867            mSystemUiVisibility = newVal;
1868
1869            // update low profile
1870            if ((diff & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) {
1871                final boolean lightsOut = (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0;
1872                if (lightsOut) {
1873                    animateCollapsePanels();
1874                    if (mTicking) {
1875                        haltTicker();
1876                    }
1877                }
1878
1879                setAreThereNotifications();
1880            }
1881
1882            // update status bar mode
1883            final int sbMode = computeBarMode(oldVal, newVal, mStatusBarView.getBarTransitions(),
1884                    View.STATUS_BAR_TRANSIENT, View.STATUS_BAR_TRANSLUCENT);
1885
1886            // update navigation bar mode
1887            final int nbMode = mNavigationBarView == null ? -1 : computeBarMode(
1888                    oldVal, newVal, mNavigationBarView.getBarTransitions(),
1889                    View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_TRANSLUCENT);
1890            final boolean sbModeChanged = sbMode != -1;
1891            final boolean nbModeChanged = nbMode != -1;
1892            boolean checkBarModes = false;
1893            if (sbModeChanged && sbMode != mStatusBarMode) {
1894                mStatusBarMode = sbMode;
1895                checkBarModes = true;
1896            }
1897            if (nbModeChanged && nbMode != mNavigationBarMode) {
1898                mNavigationBarMode = nbMode;
1899                checkBarModes = true;
1900            }
1901            if (checkBarModes) {
1902                checkBarModes();
1903            }
1904            if (sbModeChanged || nbModeChanged) {
1905                // update transient bar autohide
1906                if (mStatusBarMode == MODE_SEMI_TRANSPARENT || mNavigationBarMode == MODE_SEMI_TRANSPARENT) {
1907                    scheduleAutohide();
1908                } else {
1909                    cancelAutohide();
1910                }
1911            }
1912
1913            // ready to unhide
1914            if ((vis & View.STATUS_BAR_UNHIDE) != 0) {
1915                mSystemUiVisibility &= ~View.STATUS_BAR_UNHIDE;
1916            }
1917            if ((vis & View.NAVIGATION_BAR_UNHIDE) != 0) {
1918                mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE;
1919            }
1920
1921            // send updated sysui visibility to window manager
1922            notifyUiVisibilityChanged(mSystemUiVisibility);
1923        }
1924    }
1925
1926    private int computeBarMode(int oldVis, int newVis, BarTransitions transitions,
1927            int transientFlag, int translucentFlag) {
1928        final int oldMode = barMode(oldVis, transientFlag, translucentFlag);
1929        final int newMode = barMode(newVis, transientFlag, translucentFlag);
1930        if (oldMode == newMode) {
1931            return -1; // no mode change
1932        }
1933        return newMode;
1934    }
1935
1936    private int barMode(int vis, int transientFlag, int translucentFlag) {
1937        return (vis & transientFlag) != 0 ? MODE_SEMI_TRANSPARENT
1938                : (vis & translucentFlag) != 0 ? MODE_TRANSLUCENT
1939                : (vis & View.SYSTEM_UI_TRANSPARENT) != 0 ? MODE_TRANSPARENT
1940                : (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0 ? MODE_LIGHTS_OUT
1941                : MODE_OPAQUE;
1942    }
1943
1944    private void checkBarModes() {
1945        if (mDemoMode) return;
1946        checkBarMode(mStatusBarMode, mStatusBarWindowState, mStatusBarView.getBarTransitions());
1947        if (mNavigationBarView != null) {
1948            checkBarMode(mNavigationBarMode,
1949                    mNavigationBarWindowState, mNavigationBarView.getBarTransitions());
1950        }
1951    }
1952
1953    private void checkBarMode(int mode, int windowState, BarTransitions transitions) {
1954        final boolean anim = (mScreenOn == null || mScreenOn) && windowState != WINDOW_STATE_HIDDEN;
1955        transitions.transitionTo(mode, anim);
1956    }
1957
1958    private void finishBarAnimations() {
1959        mStatusBarView.getBarTransitions().finishAnimations();
1960        if (mNavigationBarView != null) {
1961            mNavigationBarView.getBarTransitions().finishAnimations();
1962        }
1963    }
1964
1965    private final Runnable mCheckBarModes = new Runnable() {
1966        @Override
1967        public void run() {
1968            checkBarModes();
1969        }};
1970
1971    @Override
1972    public void setInteracting(int barWindow, boolean interacting) {
1973        mInteractingWindows = interacting
1974                ? (mInteractingWindows | barWindow)
1975                : (mInteractingWindows & ~barWindow);
1976        if (mInteractingWindows != 0) {
1977            suspendAutohide();
1978        } else {
1979            resumeSuspendedAutohide();
1980        }
1981        checkBarModes();
1982    }
1983
1984    private void resumeSuspendedAutohide() {
1985        if (mAutohideSuspended) {
1986            scheduleAutohide();
1987            mHandler.postDelayed(mCheckBarModes, 500); // longer than home -> launcher
1988        }
1989    }
1990
1991    private void suspendAutohide() {
1992        mHandler.removeCallbacks(mAutohide);
1993        mHandler.removeCallbacks(mCheckBarModes);
1994        mAutohideSuspended = (mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0;
1995    }
1996
1997    private void cancelAutohide() {
1998        mAutohideSuspended = false;
1999        mHandler.removeCallbacks(mAutohide);
2000    }
2001
2002    private void scheduleAutohide() {
2003        cancelAutohide();
2004        mHandler.postDelayed(mAutohide, AUTOHIDE_TIMEOUT_MS);
2005    }
2006
2007    private void checkUserAutohide(View v, MotionEvent event) {
2008        if ((mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0  // a transient bar is revealed
2009                && event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar
2010                && event.getX() == 0 && event.getY() == 0  // a touch outside both bars
2011                ) {
2012            userAutohide();
2013        }
2014    }
2015
2016    private void userAutohide() {
2017        cancelAutohide();
2018        mHandler.postDelayed(mAutohide, 350); // longer than app gesture -> flag clear
2019    }
2020
2021    private boolean areLightsOn() {
2022        return 0 == (mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE);
2023    }
2024
2025    public void setLightsOn(boolean on) {
2026        Log.v(TAG, "setLightsOn(" + on + ")");
2027        if (on) {
2028            setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE);
2029        } else {
2030            setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE, View.SYSTEM_UI_FLAG_LOW_PROFILE);
2031        }
2032    }
2033
2034    private void notifyUiVisibilityChanged(int vis) {
2035        try {
2036            mWindowManagerService.statusBarVisibilityChanged(vis);
2037        } catch (RemoteException ex) {
2038        }
2039    }
2040
2041    public void topAppWindowChanged(boolean showMenu) {
2042        if (DEBUG) {
2043            Log.d(TAG, (showMenu?"showing":"hiding") + " the MENU button");
2044        }
2045        if (mNavigationBarView != null) {
2046            mNavigationBarView.setMenuVisibility(showMenu);
2047        }
2048
2049        // See above re: lights-out policy for legacy apps.
2050        if (showMenu) setLightsOn(true);
2051    }
2052
2053    @Override
2054    public void setImeWindowStatus(IBinder token, int vis, int backDisposition,
2055            boolean showImeSwitcher) {
2056        boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0;
2057        int flags = mNavigationIconHints;
2058        if ((backDisposition == InputMethodService.BACK_DISPOSITION_WILL_DISMISS) || imeShown) {
2059            flags |= NAVIGATION_HINT_BACK_ALT;
2060        } else {
2061            flags &= ~NAVIGATION_HINT_BACK_ALT;
2062        }
2063        if (showImeSwitcher) {
2064            flags |= NAVIGATION_HINT_IME_SHOWN;
2065        } else {
2066            flags &= ~NAVIGATION_HINT_IME_SHOWN;
2067        }
2068
2069        setNavigationIconHints(flags);
2070    }
2071
2072    @Override
2073    public void setHardKeyboardStatus(boolean available, boolean enabled) {}
2074
2075    @Override
2076    protected void tick(StatusBarNotification n, boolean firstTime) {
2077        // no ticking in lights-out mode
2078        if (!areLightsOn()) return;
2079
2080        // no ticking in Setup
2081        if (!isDeviceProvisioned()) return;
2082
2083        // not for you
2084        if (!notificationIsForCurrentProfiles(n)) return;
2085
2086        // Show the ticker if one is requested. Also don't do this
2087        // until status bar window is attached to the window manager,
2088        // because...  well, what's the point otherwise?  And trying to
2089        // run a ticker without being attached will crash!
2090        if (n.getNotification().tickerText != null && mStatusBarWindow != null
2091                && mStatusBarWindow.getWindowToken() != null) {
2092            if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS
2093                            | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
2094                mTicker.addEntry(n);
2095            }
2096        }
2097    }
2098
2099    private class MyTicker extends Ticker {
2100        MyTicker(Context context, View sb) {
2101            super(context, sb);
2102        }
2103
2104        @Override
2105        public void tickerStarting() {
2106            mTicking = true;
2107            mStatusBarContents.setVisibility(View.GONE);
2108            mTickerView.setVisibility(View.VISIBLE);
2109            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null));
2110            mStatusBarContents.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null));
2111        }
2112
2113        @Override
2114        public void tickerDone() {
2115            mStatusBarContents.setVisibility(View.VISIBLE);
2116            mTickerView.setVisibility(View.GONE);
2117            mStatusBarContents.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null));
2118            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out,
2119                        mTickingDoneListener));
2120        }
2121
2122        public void tickerHalting() {
2123            if (mStatusBarContents.getVisibility() != View.VISIBLE) {
2124                mStatusBarContents.setVisibility(View.VISIBLE);
2125                mStatusBarContents
2126                        .startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
2127            }
2128            mTickerView.setVisibility(View.GONE);
2129            // we do not animate the ticker away at this point, just get rid of it (b/6992707)
2130        }
2131    }
2132
2133    Animation.AnimationListener mTickingDoneListener = new Animation.AnimationListener() {;
2134        public void onAnimationEnd(Animation animation) {
2135            mTicking = false;
2136        }
2137        public void onAnimationRepeat(Animation animation) {
2138        }
2139        public void onAnimationStart(Animation animation) {
2140        }
2141    };
2142
2143    private Animation loadAnim(int id, Animation.AnimationListener listener) {
2144        Animation anim = AnimationUtils.loadAnimation(mContext, id);
2145        if (listener != null) {
2146            anim.setAnimationListener(listener);
2147        }
2148        return anim;
2149    }
2150
2151    public static String viewInfo(View v) {
2152        return "[(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom()
2153                + ") " + v.getWidth() + "x" + v.getHeight() + "]";
2154    }
2155
2156    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2157        synchronized (mQueueLock) {
2158            pw.println("Current Status Bar state:");
2159            pw.println("  mExpandedVisible=" + mExpandedVisible
2160                    + ", mTrackingPosition=" + mTrackingPosition);
2161            pw.println("  mTicking=" + mTicking);
2162            pw.println("  mTracking=" + mTracking);
2163            pw.println("  mDisplayMetrics=" + mDisplayMetrics);
2164            pw.println("  mStackScroller: " + viewInfo(mStackScroller));
2165            pw.println("  mTickerView: " + viewInfo(mTickerView));
2166            pw.println("  mStackScroller: " + viewInfo(mStackScroller)
2167                    + " scroll " + mStackScroller.getScrollX()
2168                    + "," + mStackScroller.getScrollY());
2169        }
2170
2171        pw.print("  mInteractingWindows="); pw.println(mInteractingWindows);
2172        pw.print("  mStatusBarWindowState=");
2173        pw.println(windowStateToString(mStatusBarWindowState));
2174        pw.print("  mStatusBarMode=");
2175        pw.println(BarTransitions.modeToString(mStatusBarMode));
2176        pw.print("  mZenMode=");
2177        pw.println(Settings.Global.zenModeToString(mZenMode));
2178        pw.print("  mUseHeadsUp=");
2179        pw.println(mUseHeadsUp);
2180        pw.print("  interrupting package: ");
2181        pw.println(hunStateToString(mInterruptingNotificationEntry));
2182        dumpBarTransitions(pw, "mStatusBarView", mStatusBarView.getBarTransitions());
2183        if (mNavigationBarView != null) {
2184            pw.print("  mNavigationBarWindowState=");
2185            pw.println(windowStateToString(mNavigationBarWindowState));
2186            pw.print("  mNavigationBarMode=");
2187            pw.println(BarTransitions.modeToString(mNavigationBarMode));
2188            dumpBarTransitions(pw, "mNavigationBarView", mNavigationBarView.getBarTransitions());
2189        }
2190
2191        pw.print("  mNavigationBarView=");
2192        if (mNavigationBarView == null) {
2193            pw.println("null");
2194        } else {
2195            mNavigationBarView.dump(fd, pw, args);
2196        }
2197
2198        pw.println("  Panels: ");
2199        if (mNotificationPanel != null) {
2200            pw.println("    mNotificationPanel=" +
2201                mNotificationPanel + " params=" + mNotificationPanel.getLayoutParams().debug(""));
2202            pw.print  ("      ");
2203            mNotificationPanel.dump(fd, pw, args);
2204        }
2205
2206        if (DUMPTRUCK) {
2207            synchronized (mNotificationData) {
2208                int N = mNotificationData.size();
2209                pw.println("  notification icons: " + N);
2210                for (int i=0; i<N; i++) {
2211                    NotificationData.Entry e = mNotificationData.get(i);
2212                    pw.println("    [" + i + "] key=" + e.key + " icon=" + e.icon);
2213                    StatusBarNotification n = e.notification;
2214                    pw.println("         pkg=" + n.getPackageName() + " id=" + n.getId() + " score=" + n.getScore());
2215                    pw.println("         notification=" + n.getNotification());
2216                    pw.println("         tickerText=\"" + n.getNotification().tickerText + "\"");
2217                }
2218            }
2219
2220            int N = mStatusIcons.getChildCount();
2221            pw.println("  system icons: " + N);
2222            for (int i=0; i<N; i++) {
2223                StatusBarIconView ic = (StatusBarIconView) mStatusIcons.getChildAt(i);
2224                pw.println("    [" + i + "] icon=" + ic);
2225            }
2226
2227            if (false) {
2228                pw.println("see the logcat for a dump of the views we have created.");
2229                // must happen on ui thread
2230                mHandler.post(new Runnable() {
2231                        public void run() {
2232                            mStatusBarView.getLocationOnScreen(mAbsPos);
2233                            Log.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
2234                                    + ") " + mStatusBarView.getWidth() + "x"
2235                                    + getStatusBarHeight());
2236                            mStatusBarView.debug();
2237                        }
2238                    });
2239            }
2240        }
2241
2242        if (DEBUG_GESTURES) {
2243            pw.print("  status bar gestures: ");
2244            mGestureRec.dump(fd, pw, args);
2245        }
2246
2247        mNetworkController.dump(fd, pw, args);
2248    }
2249
2250    private String hunStateToString(Entry entry) {
2251        if (entry == null) return "null";
2252        if (entry.notification == null) return "corrupt";
2253        return entry.notification.getPackageName();
2254    }
2255
2256    private static void dumpBarTransitions(PrintWriter pw, String var, BarTransitions transitions) {
2257        pw.print("  "); pw.print(var); pw.print(".BarTransitions.mMode=");
2258        pw.println(BarTransitions.modeToString(transitions.getMode()));
2259    }
2260
2261    @Override
2262    public void createAndAddWindows() {
2263        addStatusBarWindow();
2264    }
2265
2266    private void addStatusBarWindow() {
2267        makeStatusBarView();
2268        mStatusBarWindowManager = new StatusBarWindowManager(mContext);
2269        mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
2270    }
2271
2272    void setNotificationIconVisibility(boolean visible, int anim) {
2273        int old = mNotificationIcons.getVisibility();
2274        int v = visible ? View.VISIBLE : View.INVISIBLE;
2275        if (old != v) {
2276            mNotificationIcons.setVisibility(v);
2277            mNotificationIcons.startAnimation(loadAnim(anim, null));
2278        }
2279    }
2280
2281    static final float saturate(float a) {
2282        return a < 0f ? 0f : (a > 1f ? 1f : a);
2283    }
2284
2285    @Override
2286    public void updateExpandedViewPos(int thingy) {
2287        if (SPEW) Log.v(TAG, "updateExpandedViewPos");
2288
2289        // on larger devices, the notification panel is propped open a bit
2290        mNotificationPanel.setMinimumHeight(
2291                (int)(mNotificationPanelMinHeightFrac * mCurrentDisplaySize.y));
2292
2293        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mNotificationPanel.getLayoutParams();
2294        lp.gravity = mNotificationPanelGravity;
2295        mNotificationPanel.setLayoutParams(lp);
2296
2297        updateCarrierLabelVisibility(false);
2298    }
2299
2300    // called by makeStatusbar and also by PhoneStatusBarView
2301    void updateDisplaySize() {
2302        mDisplay.getMetrics(mDisplayMetrics);
2303        mDisplay.getSize(mCurrentDisplaySize);
2304        if (DEBUG_GESTURES) {
2305            mGestureRec.tag("display",
2306                    String.format("%dx%d", mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
2307        }
2308    }
2309
2310    private View.OnClickListener mClearButtonListener = new View.OnClickListener() {
2311        public void onClick(View v) {
2312            synchronized (mNotificationData) {
2313                mPostCollapseCleanup = new Runnable() {
2314                    @Override
2315                    public void run() {
2316                        if (DEBUG) {
2317                            Log.v(TAG, "running post-collapse cleanup");
2318                        }
2319                        try {
2320                            mBarService.onClearAllNotifications(mCurrentUserId);
2321                        } catch (Exception ex) { }
2322                    }
2323                };
2324
2325                animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
2326                return;
2327                // TODO: Handle this better with notification stack scroller
2328            }
2329        }
2330    };
2331
2332    public void startActivityDismissingKeyguard(Intent intent, boolean onlyProvisioned) {
2333        if (onlyProvisioned && !isDeviceProvisioned()) return;
2334        try {
2335            // Dismiss the lock screen when Settings starts.
2336            ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
2337        } catch (RemoteException e) {
2338        }
2339        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
2340        mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
2341        animateCollapsePanels();
2342    }
2343
2344    private View.OnClickListener mClockClickListener = new View.OnClickListener() {
2345        public void onClick(View v) {
2346            startActivityDismissingKeyguard(
2347                    new Intent(Intent.ACTION_QUICK_CLOCK), true); // have fun, everyone
2348        }
2349    };
2350
2351    private View.OnClickListener mNotificationButtonListener = new View.OnClickListener() {
2352        public void onClick(View v) {
2353            animateExpandNotificationsPanel();
2354        }
2355    };
2356
2357    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
2358        public void onReceive(Context context, Intent intent) {
2359            if (DEBUG) Log.v(TAG, "onReceive: " + intent);
2360            String action = intent.getAction();
2361            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
2362                int flags = CommandQueue.FLAG_EXCLUDE_NONE;
2363                if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
2364                    String reason = intent.getStringExtra("reason");
2365                    if (reason != null && reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) {
2366                        flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL;
2367                    }
2368                }
2369                animateCollapsePanels(flags);
2370            }
2371            else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
2372                mScreenOn = false;
2373                notifyNavigationBarScreenOn(false);
2374                notifyHeadsUpScreenOn(false);
2375                finishBarAnimations();
2376                stopNotificationLogging();
2377            }
2378            else if (Intent.ACTION_SCREEN_ON.equals(action)) {
2379                mScreenOn = true;
2380                // work around problem where mDisplay.getRotation() is not stable while screen is off (bug 7086018)
2381                repositionNavigationBar();
2382                notifyNavigationBarScreenOn(true);
2383                startNotificationLoggingIfScreenOnAndVisible();
2384            }
2385            else if (ACTION_DEMO.equals(action)) {
2386                Bundle bundle = intent.getExtras();
2387                if (bundle != null) {
2388                    String command = bundle.getString("command", "").trim().toLowerCase();
2389                    if (command.length() > 0) {
2390                        try {
2391                            dispatchDemoCommand(command, bundle);
2392                        } catch (Throwable t) {
2393                            Log.w(TAG, "Error running demo command, intent=" + intent, t);
2394                        }
2395                    }
2396                }
2397            }
2398        }
2399    };
2400
2401    @Override
2402    protected void startNotificationActivity(OnDismissAction action) {
2403        if (mStatusBarKeyguardViewManager.isShowing()) {
2404            mStatusBarKeyguardViewManager.dismissWithAction(action);
2405        } else {
2406            action.onDismiss();
2407        }
2408    }
2409
2410    // SystemUIService notifies SystemBars of configuration changes, which then calls down here
2411    @Override
2412    protected void onConfigurationChanged(Configuration newConfig) {
2413        super.onConfigurationChanged(newConfig); // calls refreshLayout
2414
2415        if (DEBUG) {
2416            Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration());
2417        }
2418        updateDisplaySize(); // populates mDisplayMetrics
2419
2420        updateResources();
2421        repositionNavigationBar();
2422        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
2423        updateShowSearchHoldoff();
2424    }
2425
2426    @Override
2427    public void userSwitched(int newUserId) {
2428        if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId);
2429        animateCollapsePanels();
2430        updateNotifications();
2431        resetUserSetupObserver();
2432    }
2433
2434    private void resetUserSetupObserver() {
2435        mContext.getContentResolver().unregisterContentObserver(mUserSetupObserver);
2436        mUserSetupObserver.onChange(false);
2437        mContext.getContentResolver().registerContentObserver(
2438                Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), true,
2439                mUserSetupObserver,
2440                mCurrentUserId);
2441    }
2442
2443    private void setHeadsUpVisibility(boolean vis) {
2444        if (!ENABLE_HEADS_UP) return;
2445        if (DEBUG) Log.v(TAG, (vis ? "showing" : "hiding") + " heads up window");
2446        mHeadsUpNotificationView.setVisibility(vis ? View.VISIBLE : View.GONE);
2447        if (!vis) {
2448            if (DEBUG) Log.d(TAG, "setting heads up entry to null");
2449            mInterruptingNotificationEntry = null;
2450        }
2451    }
2452
2453    public void onHeadsUpDismissed() {
2454        if (mInterruptingNotificationEntry == null) return;
2455        mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
2456        if (mHeadsUpNotificationView.isClearable()) {
2457            try {
2458                mBarService.onNotificationClear(
2459                        mInterruptingNotificationEntry.notification.getPackageName(),
2460                        mInterruptingNotificationEntry.notification.getTag(),
2461                        mInterruptingNotificationEntry.notification.getId(),
2462                        mInterruptingNotificationEntry.notification.getUserId());
2463            } catch (android.os.RemoteException ex) {
2464                // oh well
2465            }
2466        }
2467    }
2468
2469    /**
2470     * Reload some of our resources when the configuration changes.
2471     *
2472     * We don't reload everything when the configuration changes -- we probably
2473     * should, but getting that smooth is tough.  Someday we'll fix that.  In the
2474     * meantime, just update the things that we know change.
2475     */
2476    void updateResources() {
2477        // Update the quick setting tiles
2478        if (mQSPanel != null) mQSPanel.updateResources();
2479
2480        loadDimens();
2481    }
2482
2483    protected void loadDimens() {
2484        final Resources res = mContext.getResources();
2485
2486        mNaturalBarHeight = res.getDimensionPixelSize(
2487                com.android.internal.R.dimen.status_bar_height);
2488
2489        int newIconSize = res.getDimensionPixelSize(
2490            com.android.internal.R.dimen.status_bar_icon_size);
2491        int newIconHPadding = res.getDimensionPixelSize(
2492            R.dimen.status_bar_icon_padding);
2493
2494        if (newIconHPadding != mIconHPadding || newIconSize != mIconSize) {
2495//            Log.d(TAG, "size=" + newIconSize + " padding=" + newIconHPadding);
2496            mIconHPadding = newIconHPadding;
2497            mIconSize = newIconSize;
2498            //reloadAllNotificationIcons(); // reload the tray
2499        }
2500
2501        mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
2502
2503        mSelfExpandVelocityPx = res.getDimension(R.dimen.self_expand_velocity);
2504        mSelfCollapseVelocityPx = res.getDimension(R.dimen.self_collapse_velocity);
2505        mFlingExpandMinVelocityPx = res.getDimension(R.dimen.fling_expand_min_velocity);
2506        mFlingCollapseMinVelocityPx = res.getDimension(R.dimen.fling_collapse_min_velocity);
2507
2508        mCollapseMinDisplayFraction = res.getFraction(R.dimen.collapse_min_display_fraction, 1, 1);
2509        mExpandMinDisplayFraction = res.getFraction(R.dimen.expand_min_display_fraction, 1, 1);
2510
2511        mExpandAccelPx = res.getDimension(R.dimen.expand_accel);
2512        mCollapseAccelPx = res.getDimension(R.dimen.collapse_accel);
2513
2514        mFlingGestureMaxXVelocityPx = res.getDimension(R.dimen.fling_gesture_max_x_velocity);
2515
2516        mFlingGestureMaxOutputVelocityPx = res.getDimension(R.dimen.fling_gesture_max_output_velocity);
2517
2518        mNotificationPanelGravity = res.getInteger(R.integer.notification_panel_layout_gravity);
2519        if (mNotificationPanelGravity <= 0) {
2520            mNotificationPanelGravity = Gravity.START | Gravity.TOP;
2521        }
2522
2523        mCarrierLabelHeight = res.getDimensionPixelSize(R.dimen.carrier_label_height);
2524        mStatusBarHeaderHeight = res.getDimensionPixelSize(R.dimen.status_bar_header_height);
2525
2526        mNotificationPanelMinHeightFrac = res.getFraction(R.dimen.notification_panel_min_height_frac, 1, 1);
2527        if (mNotificationPanelMinHeightFrac < 0f || mNotificationPanelMinHeightFrac > 1f) {
2528            mNotificationPanelMinHeightFrac = 0f;
2529        }
2530
2531        mHeadsUpNotificationDecay = res.getInteger(R.integer.heads_up_notification_decay);
2532        mRowMinHeight =  res.getDimensionPixelSize(R.dimen.notification_min_height);
2533        mRowMaxHeight =  res.getDimensionPixelSize(R.dimen.notification_max_height);
2534
2535        mKeyguardMaxNotificationCount = res.getInteger(R.integer.keyguard_max_notification_count);
2536
2537        if (false) Log.v(TAG, "updateResources");
2538    }
2539
2540    // Visibility reporting
2541
2542    @Override
2543    protected void visibilityChanged(boolean visible) {
2544        mVisible = visible;
2545        if (visible) {
2546            startNotificationLoggingIfScreenOnAndVisible();
2547        } else {
2548            stopNotificationLogging();
2549        }
2550        super.visibilityChanged(visible);
2551    }
2552
2553    private void stopNotificationLogging() {
2554        // Report all notifications as invisible and turn down the
2555        // reporter.
2556        if (!mCurrentlyVisibleNotifications.isEmpty()) {
2557            logNotificationVisibilityChanges(
2558                    Collections.<String>emptyList(), mCurrentlyVisibleNotifications);
2559            mCurrentlyVisibleNotifications.clear();
2560        }
2561        mHandler.removeCallbacks(mVisibilityReporter);
2562        mStackScroller.setChildLocationsChangedListener(null);
2563    }
2564
2565    private void startNotificationLoggingIfScreenOnAndVisible() {
2566        if (mVisible && mScreenOn) {
2567            mStackScroller.setChildLocationsChangedListener(mNotificationLocationsChangedListener);
2568            // Some transitions like mScreenOn=false -> mScreenOn=true don't
2569            // cause the scroller to emit child location events. Hence generate
2570            // one ourselves to guarantee that we're reporting visible
2571            // notifications.
2572            // (Note that in cases where the scroller does emit events, this
2573            // additional event doesn't break anything.)
2574            mNotificationLocationsChangedListener.onChildLocationsChanged(mStackScroller);
2575        }
2576    }
2577
2578    private void logNotificationVisibilityChanges(
2579            Collection<String> newlyVisible, Collection<String> noLongerVisible) {
2580        if (newlyVisible.isEmpty() && noLongerVisible.isEmpty()) {
2581            return;
2582        }
2583
2584        String[] newlyVisibleAr = newlyVisible.toArray(new String[newlyVisible.size()]);
2585        String[] noLongerVisibleAr = noLongerVisible.toArray(new String[noLongerVisible.size()]);
2586        try {
2587            mBarService.onNotificationVisibilityChanged(newlyVisibleAr, noLongerVisibleAr);
2588        } catch (RemoteException e) {
2589            // Ignore.
2590        }
2591    }
2592
2593    //
2594    // tracing
2595    //
2596
2597    void postStartTracing() {
2598        mHandler.postDelayed(mStartTracing, 3000);
2599    }
2600
2601    void vibrate() {
2602        android.os.Vibrator vib = (android.os.Vibrator)mContext.getSystemService(
2603                Context.VIBRATOR_SERVICE);
2604        vib.vibrate(250, AudioManager.STREAM_SYSTEM);
2605    }
2606
2607    Runnable mStartTracing = new Runnable() {
2608        public void run() {
2609            vibrate();
2610            SystemClock.sleep(250);
2611            Log.d(TAG, "startTracing");
2612            android.os.Debug.startMethodTracing("/data/statusbar-traces/trace");
2613            mHandler.postDelayed(mStopTracing, 10000);
2614        }
2615    };
2616
2617    Runnable mStopTracing = new Runnable() {
2618        public void run() {
2619            android.os.Debug.stopMethodTracing();
2620            Log.d(TAG, "stopTracing");
2621            vibrate();
2622        }
2623    };
2624
2625    @Override
2626    protected void haltTicker() {
2627        mTicker.halt();
2628    }
2629
2630    @Override
2631    protected boolean shouldDisableNavbarGestures() {
2632        return !isDeviceProvisioned()
2633                || mExpandedVisible
2634                || (mDisabled & StatusBarManager.DISABLE_SEARCH) != 0;
2635    }
2636
2637    public void postStartSettingsActivity(final Intent intent, int delay) {
2638        mHandler.postDelayed(new Runnable() {
2639            @Override
2640            public void run() {
2641                handleStartSettingsActivity(intent, true /*onlyProvisioned*/);
2642            }
2643        }, delay);
2644    }
2645
2646    private void handleStartSettingsActivity(Intent intent, boolean onlyProvisioned) {
2647        if (onlyProvisioned && !isDeviceProvisioned()) return;
2648        try {
2649            // Dismiss the lock screen when Settings starts.
2650            ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
2651        } catch (RemoteException e) {
2652        }
2653        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
2654        mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
2655        animateCollapsePanels();
2656    }
2657
2658    public void startSettingsActivity(String action) {
2659        postStartSettingsActivity(new Intent(action), 0);
2660    }
2661
2662    private static class FastColorDrawable extends Drawable {
2663        private final int mColor;
2664
2665        public FastColorDrawable(int color) {
2666            mColor = 0xff000000 | color;
2667        }
2668
2669        @Override
2670        public void draw(Canvas canvas) {
2671            canvas.drawColor(mColor, PorterDuff.Mode.SRC);
2672        }
2673
2674        @Override
2675        public void setAlpha(int alpha) {
2676        }
2677
2678        @Override
2679        public void setColorFilter(ColorFilter cf) {
2680        }
2681
2682        @Override
2683        public int getOpacity() {
2684            return PixelFormat.OPAQUE;
2685        }
2686
2687        @Override
2688        public void setBounds(int left, int top, int right, int bottom) {
2689        }
2690
2691        @Override
2692        public void setBounds(Rect bounds) {
2693        }
2694    }
2695
2696    @Override
2697    public void destroy() {
2698        super.destroy();
2699        if (mStatusBarWindow != null) {
2700            mWindowManager.removeViewImmediate(mStatusBarWindow);
2701            mStatusBarWindow = null;
2702        }
2703        if (mNavigationBarView != null) {
2704            mWindowManager.removeViewImmediate(mNavigationBarView);
2705            mNavigationBarView = null;
2706        }
2707        mContext.unregisterReceiver(mBroadcastReceiver);
2708    }
2709
2710    private boolean mDemoModeAllowed;
2711    private boolean mDemoMode;
2712    private DemoStatusIcons mDemoStatusIcons;
2713
2714    @Override
2715    public void dispatchDemoCommand(String command, Bundle args) {
2716        if (!mDemoModeAllowed) {
2717            mDemoModeAllowed = Settings.Global.getInt(mContext.getContentResolver(),
2718                    "sysui_demo_allowed", 0) != 0;
2719        }
2720        if (!mDemoModeAllowed) return;
2721        if (command.equals(COMMAND_ENTER)) {
2722            mDemoMode = true;
2723        } else if (command.equals(COMMAND_EXIT)) {
2724            mDemoMode = false;
2725            checkBarModes();
2726        } else if (!mDemoMode) {
2727            // automatically enter demo mode on first demo command
2728            dispatchDemoCommand(COMMAND_ENTER, new Bundle());
2729        }
2730        boolean modeChange = command.equals(COMMAND_ENTER) || command.equals(COMMAND_EXIT);
2731        if (modeChange || command.equals(COMMAND_CLOCK)) {
2732            dispatchDemoCommandToView(command, args, R.id.clock);
2733        }
2734        if (modeChange || command.equals(COMMAND_BATTERY)) {
2735            dispatchDemoCommandToView(command, args, R.id.battery);
2736        }
2737        if (modeChange || command.equals(COMMAND_STATUS)) {
2738            if (mDemoStatusIcons == null) {
2739                mDemoStatusIcons = new DemoStatusIcons(mStatusIcons, mIconSize);
2740            }
2741            mDemoStatusIcons.dispatchDemoCommand(command, args);
2742        }
2743        if (mNetworkController != null && (modeChange || command.equals(COMMAND_NETWORK))) {
2744            mNetworkController.dispatchDemoCommand(command, args);
2745        }
2746        if (modeChange || command.equals(COMMAND_NOTIFICATIONS)) {
2747            View notifications = mStatusBarView == null ? null
2748                    : mStatusBarView.findViewById(R.id.notification_icon_area);
2749            if (notifications != null) {
2750                String visible = args.getString("visible");
2751                int vis = mDemoMode && "false".equals(visible) ? View.INVISIBLE : View.VISIBLE;
2752                notifications.setVisibility(vis);
2753            }
2754        }
2755        if (command.equals(COMMAND_BARS)) {
2756            String mode = args.getString("mode");
2757            int barMode = "opaque".equals(mode) ? MODE_OPAQUE :
2758                    "translucent".equals(mode) ? MODE_TRANSLUCENT :
2759                    "semi-transparent".equals(mode) ? MODE_SEMI_TRANSPARENT :
2760                    -1;
2761            if (barMode != -1) {
2762                boolean animate = true;
2763                if (mStatusBarView != null) {
2764                    mStatusBarView.getBarTransitions().transitionTo(barMode, animate);
2765                }
2766                if (mNavigationBarView != null) {
2767                    mNavigationBarView.getBarTransitions().transitionTo(barMode, animate);
2768                }
2769            }
2770        }
2771    }
2772
2773    private void dispatchDemoCommandToView(String command, Bundle args, int id) {
2774        if (mStatusBarView == null) return;
2775        View v = mStatusBarView.findViewById(id);
2776        if (v instanceof DemoMode) {
2777            ((DemoMode)v).dispatchDemoCommand(command, args);
2778        }
2779    }
2780
2781    /**
2782     * @return The {@link StatusBarState} the status bar is in.
2783     */
2784    public int getBarState() {
2785        return mState;
2786    }
2787
2788    public void showKeyguard() {
2789        setBarState(StatusBarState.KEYGUARD);
2790        updateKeyguardState();
2791        instantExpandNotificationsPanel();
2792        mLeaveOpenOnKeyguardHide = false;
2793    }
2794
2795    public void hideKeyguard() {
2796        setBarState(StatusBarState.SHADE);
2797        if (mLeaveOpenOnKeyguardHide) {
2798            mLeaveOpenOnKeyguardHide = false;
2799            mNotificationPanel.animateToFullShade();
2800        } else {
2801            instantCollapseNotificationPanel();
2802        }
2803        updateKeyguardState();
2804    }
2805
2806    private void updatePublicMode() {
2807        setLockscreenPublicMode(mState == StatusBarState.KEYGUARD
2808                && mStatusBarKeyguardViewManager.isSecure());
2809    }
2810
2811    private void updateKeyguardState() {
2812        if (mState == StatusBarState.KEYGUARD) {
2813            mKeyguardStatusView.setVisibility(View.VISIBLE);
2814            mKeyguardIndicationTextView.setVisibility(View.VISIBLE);
2815            mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase);
2816            mNotificationPanel.closeQs();
2817        } else {
2818            mKeyguardStatusView.setVisibility(View.GONE);
2819            mKeyguardIndicationTextView.setVisibility(View.GONE);
2820        }
2821        if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
2822            mKeyguardBottomArea.setVisibility(View.VISIBLE);
2823            mHeader.setKeyguardShowing(true);
2824        } else {
2825            mKeyguardBottomArea.setVisibility(View.GONE);
2826            mHeader.setKeyguardShowing(false);
2827        }
2828
2829        updateStackScrollerState();
2830        updatePublicMode();
2831        updateNotifications();
2832        checkBarModes();
2833        updateCarrierLabelVisibility(false);
2834    }
2835
2836    public void updateStackScrollerState() {
2837        mStackScroller.setDimmed(mState == StatusBarState.KEYGUARD, false /* animate */);
2838    }
2839
2840    public void userActivity() {
2841        mHandler.removeCallbacks(mUserActivity);
2842        mHandler.post(mUserActivity);
2843    }
2844
2845    public boolean interceptMediaKey(KeyEvent event) {
2846        return mState == StatusBarState.KEYGUARD
2847                && mStatusBarKeyguardViewManager.interceptMediaKey(event);
2848    }
2849
2850    public boolean onMenuPressed() {
2851        return mState == StatusBarState.KEYGUARD && mStatusBarKeyguardViewManager.onMenuPressed();
2852    }
2853
2854    public boolean onBackPressed() {
2855        if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
2856            return mStatusBarKeyguardViewManager.onBackPressed();
2857        } else {
2858            animateCollapsePanels();
2859            return true;
2860        }
2861    }
2862
2863    private void showBouncer() {
2864        if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
2865            mStatusBarKeyguardViewManager.dismiss();
2866        }
2867    }
2868
2869    private void instantExpandNotificationsPanel() {
2870
2871        // Make our window larger and the panel visible.
2872        makeExpandedVisible(true);
2873        mNotificationPanel.setVisibility(View.VISIBLE);
2874
2875        // Wait for window manager to pickup the change, so we know the maximum height of the panel
2876        // then.
2877        mNotificationPanel.getViewTreeObserver().addOnGlobalLayoutListener(
2878                new ViewTreeObserver.OnGlobalLayoutListener() {
2879            @Override
2880            public void onGlobalLayout() {
2881                if (mStatusBarWindow.getHeight() != getStatusBarHeight()) {
2882                    mNotificationPanel.getViewTreeObserver().removeOnGlobalLayoutListener(this);
2883                    mNotificationPanel.setExpandedFraction(1);
2884                }
2885            }
2886        });
2887    }
2888
2889    private void instantCollapseNotificationPanel() {
2890        mNotificationPanel.setExpandedFraction(0);
2891    }
2892
2893    @Override
2894    public void onActivated(View view) {
2895        userActivity();
2896        mKeyguardIndicationTextView.switchIndication(R.string.notification_tap_again);
2897        mStackScroller.setActivatedChild(view);
2898    }
2899
2900    /**
2901     * @param state The {@link StatusBarState} to set.
2902     */
2903    public void setBarState(int state) {
2904        mState = state;
2905        mStatusBarWindowManager.setStatusBarState(state);
2906    }
2907
2908    @Override
2909    public void onActivationReset(View view) {
2910        if (view == mStackScroller.getActivatedChild()) {
2911            mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase);
2912            mStackScroller.setActivatedChild(null);
2913        }
2914    }
2915
2916    public void onTrackingStarted() {
2917        if (mState == StatusBarState.KEYGUARD) {
2918            mKeyguardIndicationTextView.switchIndication(R.string.keyguard_unlock);
2919        }
2920    }
2921
2922    public void onTrackingStopped() {
2923        if (mState == StatusBarState.KEYGUARD) {
2924            mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase);
2925        }
2926    }
2927
2928    @Override
2929    protected int getMaxKeyguardNotifications() {
2930        return mKeyguardMaxNotificationCount;
2931    }
2932
2933    public NavigationBarView getNavigationBarView() {
2934        return mNavigationBarView;
2935    }
2936
2937    // ---------------------- DragDownHelper.OnDragDownListener ------------------------------------
2938
2939    @Override
2940    public void onDraggedDown(View startingChild) {
2941        goToLockedShade(startingChild);
2942    }
2943
2944    @Override
2945    public void onDragDownReset() {
2946        mStackScroller.setDimmed(true /* dimmed */, true /* animated */);
2947    }
2948
2949    public void onThresholdReached() {
2950        mStackScroller.setDimmed(false /* dimmed */, true /* animate */);
2951    }
2952
2953    /**
2954     * If secure with redaction: Show bouncer, go to unlocked shade.
2955     *
2956     * <p>If secure without redaction or no security: Go to {@link StatusBarState#SHADE_LOCKED}.</p>
2957     *
2958     * @param expandView The view to expand after going to the shade.
2959     */
2960    public void goToLockedShade(View expandView) {
2961        if (expandView instanceof ExpandableNotificationRow) {
2962            ExpandableNotificationRow row = (ExpandableNotificationRow) expandView;
2963            row.setUserExpanded(true);
2964        }
2965        if (isLockscreenPublicMode() && !userAllowsPrivateNotificationsInPublic(mCurrentUserId)) {
2966            mLeaveOpenOnKeyguardHide = true;
2967            showBouncer();
2968        } else {
2969            mNotificationPanel.animateToFullShade();
2970            setBarState(StatusBarState.SHADE_LOCKED);
2971            updateKeyguardState();
2972        }
2973    }
2974
2975    /**
2976     * @return a ViewGroup that spans the entire panel which contains the quick settings
2977     */
2978    public ViewGroup getQuickSettingsOverlayParent() {
2979        return mNotificationPanel;
2980    }
2981
2982    public LinearLayout getSystemIcons() {
2983        return mSystemIcons;
2984    }
2985
2986    /**
2987     * Reattaches the system icons to its normal parent in collapsed status bar.
2988     */
2989    public void reattachSystemIcons() {
2990        mSystemIconArea.addView(mSystemIcons, 0);
2991    }
2992
2993    public void onScreenTurnedOff() {
2994        mStackScroller.setAnimationsEnabled(false);
2995    }
2996
2997    public void onScreenTurnedOn() {
2998        mStackScroller.setAnimationsEnabled(true);
2999    }
3000
3001    private final Runnable mUserActivity = new Runnable() {
3002        @Override
3003        public void run() {
3004            if (mState == StatusBarState.KEYGUARD) {
3005                mKeyguardViewMediatorCallback.userActivity();
3006            }
3007        }
3008    };
3009}
3010