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