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