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