PhoneStatusBar.java revision fd5494b8ef91e8a909622c3bf45dac7537254b41
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
19import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.animation.AnimatorSet;
22import android.animation.ObjectAnimator;
23import android.app.ActivityManager;
24import android.app.ActivityManagerNative;
25import android.app.Dialog;
26import android.app.KeyguardManager;
27import android.app.Notification;
28import android.app.PendingIntent;
29import android.app.StatusBarManager;
30import android.content.BroadcastReceiver;
31import android.content.Context;
32import android.content.Intent;
33import android.content.IntentFilter;
34import android.content.res.Configuration;
35import android.content.res.Resources;
36import android.graphics.Canvas;
37import android.graphics.ColorFilter;
38import android.graphics.PixelFormat;
39import android.graphics.PorterDuff;
40import android.graphics.Rect;
41import android.graphics.drawable.Drawable;
42import android.graphics.drawable.NinePatchDrawable;
43import android.inputmethodservice.InputMethodService;
44import android.os.IBinder;
45import android.os.Message;
46import android.os.RemoteException;
47import android.os.ServiceManager;
48import android.os.SystemClock;
49import android.os.UserHandle;
50import android.provider.Settings;
51import android.service.dreams.IDreamManager;
52import android.util.DisplayMetrics;
53import android.util.Log;
54import android.util.Slog;
55import android.view.Display;
56import android.view.Gravity;
57import android.view.IWindowManager;
58import android.view.KeyEvent;
59import android.view.MotionEvent;
60import android.view.VelocityTracker;
61import android.view.View;
62import android.view.ViewGroup;
63import android.view.WindowManagerGlobal;
64import android.view.ViewGroup.LayoutParams;
65import android.view.WindowManager;
66import android.view.animation.AccelerateInterpolator;
67import android.view.animation.Animation;
68import android.view.animation.AnimationUtils;
69import android.view.animation.DecelerateInterpolator;
70import android.widget.FrameLayout;
71import android.widget.ImageView;
72import android.widget.LinearLayout;
73import android.widget.ScrollView;
74import android.widget.TextView;
75
76import com.android.internal.statusbar.StatusBarIcon;
77import com.android.internal.statusbar.StatusBarNotification;
78import com.android.systemui.R;
79import com.android.systemui.recent.RecentTasksLoader;
80import com.android.systemui.statusbar.BaseStatusBar;
81import com.android.systemui.statusbar.CommandQueue;
82import com.android.systemui.statusbar.GestureRecorder;
83import com.android.systemui.statusbar.NotificationData;
84import com.android.systemui.statusbar.NotificationData.Entry;
85import com.android.systemui.statusbar.RotationToggle;
86import com.android.systemui.statusbar.SignalClusterView;
87import com.android.systemui.statusbar.StatusBarIconView;
88import com.android.systemui.statusbar.policy.BatteryController;
89import com.android.systemui.statusbar.policy.DateView;
90import com.android.systemui.statusbar.policy.IntruderAlertView;
91import com.android.systemui.statusbar.policy.LocationController;
92import com.android.systemui.statusbar.policy.NetworkController;
93import com.android.systemui.statusbar.policy.NotificationRowLayout;
94import com.android.systemui.statusbar.policy.OnSizeChangedListener;
95
96import java.io.FileDescriptor;
97import java.io.PrintWriter;
98import java.util.ArrayList;
99
100public class PhoneStatusBar extends BaseStatusBar {
101    static final String TAG = "PhoneStatusBar";
102    public static final boolean DEBUG = false;
103    public static final boolean SPEW = DEBUG;
104    public static final boolean DUMPTRUCK = true; // extra dumpsys info
105
106    // additional instrumentation for testing purposes; intended to be left on during development
107    public static final boolean CHATTY = DEBUG;
108
109    public static final String ACTION_STATUSBAR_START
110            = "com.android.internal.policy.statusbar.START";
111
112    private static final int MSG_OPEN_NOTIFICATION_PANEL = 1000;
113    private static final int MSG_CLOSE_NOTIFICATION_PANEL = 1001;
114    // 1020-1030 reserved for BaseStatusBar
115
116    // will likely move to a resource or other tunable param at some point
117    private static final int INTRUDER_ALERT_DECAY_MS = 0; // disabled, was 10000;
118
119    private static final boolean CLOSE_PANEL_WHEN_EMPTIED = true;
120
121    private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10; // see NotificationManagerService
122    private static final int HIDE_ICONS_BELOW_SCORE = Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER;
123
124    // fling gesture tuning parameters, scaled to display density
125    private float mSelfExpandVelocityPx; // classic value: 2000px/s
126    private float mSelfCollapseVelocityPx; // classic value: 2000px/s (will be negated to collapse "up")
127    private float mFlingExpandMinVelocityPx; // classic value: 200px/s
128    private float mFlingCollapseMinVelocityPx; // classic value: 200px/s
129    private float mCollapseMinDisplayFraction; // classic value: 0.08 (25px/min(320px,480px) on G1)
130    private float mExpandMinDisplayFraction; // classic value: 0.5 (drag open halfway to expand)
131    private float mFlingGestureMaxXVelocityPx; // classic value: 150px/s
132
133    private float mExpandAccelPx; // classic value: 2000px/s/s
134    private float mCollapseAccelPx; // classic value: 2000px/s/s (will be negated to collapse "up")
135
136    private float mFlingGestureMaxOutputVelocityPx; // how fast can it really go? (should be a little
137                                                    // faster than mSelfCollapseVelocityPx)
138
139    PhoneStatusBarPolicy mIconPolicy;
140
141    // These are no longer handled by the policy, because we need custom strategies for them
142    BatteryController mBatteryController;
143    LocationController mLocationController;
144    NetworkController mNetworkController;
145
146    int mNaturalBarHeight = -1;
147    int mIconSize = -1;
148    int mIconHPadding = -1;
149    Display mDisplay;
150
151    IDreamManager mDreamManager;
152
153    StatusBarWindowView mStatusBarWindow;
154    PhoneStatusBarView mStatusBarView;
155
156    int mPixelFormat;
157    Object mQueueLock = new Object();
158
159    // icons
160    LinearLayout mIcons;
161    IconMerger mNotificationIcons;
162    View mMoreIcon;
163    LinearLayout mStatusIcons;
164
165    // expanded notifications
166    PanelView mNotificationPanel; // the sliding/resizing panel within the notification window
167    ScrollView mScrollView;
168    View mExpandedContents;
169    final Rect mNotificationPanelBackgroundPadding = new Rect();
170    int mNotificationPanelGravity;
171    int mNotificationPanelMarginBottomPx, mNotificationPanelMarginPx;
172    int mNotificationPanelMinHeight;
173    boolean mNotificationPanelIsFullScreenWidth;
174    TextView mNotificationPanelDebugText;
175
176    // settings
177    PanelView mSettingsPanel;
178    int mSettingsPanelGravity;
179
180    // top bar
181    View mClearButton;
182    View mSettingsButton;
183    RotationToggle mRotationButton;
184
185    // carrier/wifi label
186    private TextView mCarrierLabel;
187    private boolean mCarrierLabelVisible = false;
188    private int mCarrierLabelHeight;
189    private TextView mEmergencyCallLabel;
190    private int mNotificationHeaderHeight;
191
192    private boolean mShowCarrierInPanel = false;
193
194    // position
195    int[] mPositionTmp = new int[2];
196    boolean mExpandedVisible;
197
198    // the date view
199    DateView mDateView;
200
201    // for immersive activities
202    private IntruderAlertView mIntruderAlertView;
203
204    // on-screen navigation buttons
205    private NavigationBarView mNavigationBarView = null;
206
207    // the tracker view
208    int mTrackingPosition; // the position of the top of the tracking view.
209
210    // ticker
211    private Ticker mTicker;
212    private View mTickerView;
213    private boolean mTicking;
214
215    // Tracking finger for opening/closing.
216    int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore
217    boolean mTracking;
218    VelocityTracker mVelocityTracker;
219
220    boolean mAnimating;
221    boolean mClosing; // only valid when mAnimating; indicates the initial acceleration
222    float mAnimY;
223    float mAnimVel;
224    float mAnimAccel;
225    long mAnimLastTimeNanos;
226    boolean mAnimatingReveal = false;
227    int mViewDelta;
228    float mFlingVelocity;
229    int mFlingY;
230    int[] mAbsPos = new int[2];
231    Runnable mPostCollapseCleanup = null;
232
233    private AnimatorSet mLightsOutAnimation;
234    private AnimatorSet mLightsOnAnimation;
235
236    // for disabling the status bar
237    int mDisabled = 0;
238
239    // tracking calls to View.setSystemUiVisibility()
240    int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE;
241
242    DisplayMetrics mDisplayMetrics = new DisplayMetrics();
243
244    // XXX: gesture research
245    private GestureRecorder mGestureRec = new GestureRecorder("/sdcard/statusbar_gestures.dat");
246
247    private int mNavigationIconHints = 0;
248    private final Animator.AnimatorListener mMakeIconsInvisible = new AnimatorListenerAdapter() {
249        @Override
250        public void onAnimationEnd(Animator animation) {
251            // double-check to avoid races
252            if (mIcons.getAlpha() == 0) {
253                Slog.d(TAG, "makeIconsInvisible");
254                mIcons.setVisibility(View.INVISIBLE);
255            }
256        }
257    };
258
259    @Override
260    public void start() {
261        mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
262                .getDefaultDisplay();
263
264        mDreamManager = IDreamManager.Stub.asInterface(
265                ServiceManager.checkService("dreams"));
266
267        super.start(); // calls createAndAddWindows()
268
269        addNavigationBar();
270
271        if (ENABLE_INTRUDERS) addIntruderView();
272
273        // Lastly, call to the icon policy to install/update all the icons.
274        mIconPolicy = new PhoneStatusBarPolicy(mContext);
275    }
276
277    // ================================================================================
278    // Constructing the view
279    // ================================================================================
280    protected PhoneStatusBarView makeStatusBarView() {
281        final Context context = mContext;
282
283        Resources res = context.getResources();
284
285        updateDisplaySize(); // populates mDisplayMetrics
286        loadDimens();
287
288        mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
289
290        mStatusBarWindow = (StatusBarWindowView) View.inflate(context,
291                R.layout.super_status_bar, null);
292        if (DEBUG) {
293            mStatusBarWindow.setBackgroundColor(0x6000FF80);
294        }
295        mStatusBarWindow.mService = this;
296        mStatusBarWindow.setOnTouchListener(new View.OnTouchListener() {
297            @Override
298            public boolean onTouch(View v, MotionEvent event) {
299                if (event.getAction() == MotionEvent.ACTION_DOWN) {
300                    if (mExpandedVisible && !mAnimating) {
301                        animateCollapse();
302                    }
303                }
304                return mStatusBarWindow.onTouchEvent(event);
305            }});
306
307        mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar);
308        mStatusBarView.setBar(this);
309
310        PanelHolder holder = (PanelHolder) mStatusBarWindow.findViewById(R.id.panel_holder);
311        mStatusBarView.setPanelHolder(holder);
312
313        mNotificationPanel = (PanelView) mStatusBarWindow.findViewById(R.id.notification_panel);
314        mNotificationPanelIsFullScreenWidth =
315            (mNotificationPanel.getLayoutParams().width == ViewGroup.LayoutParams.MATCH_PARENT);
316        mNotificationPanel.setSystemUiVisibility(
317                  View.STATUS_BAR_DISABLE_NOTIFICATION_TICKER
318                | (mNotificationPanelIsFullScreenWidth ? 0 : View.STATUS_BAR_DISABLE_SYSTEM_INFO));
319
320        // quick settings (WIP)
321        mSettingsPanel = (PanelView) mStatusBarWindow.findViewById(R.id.settings_panel);
322
323        if (!ActivityManager.isHighEndGfx()) {
324            mStatusBarWindow.setBackground(null);
325            mNotificationPanel.setBackground(new FastColorDrawable(context.getResources().getColor(
326                    R.color.notification_panel_solid_background)));
327            mSettingsPanel.setBackground(new FastColorDrawable(context.getResources().getColor(
328                    R.color.notification_panel_solid_background)));
329        }
330        if (ENABLE_INTRUDERS) {
331            mIntruderAlertView = (IntruderAlertView) View.inflate(context, R.layout.intruder_alert, null);
332            mIntruderAlertView.setVisibility(View.GONE);
333            mIntruderAlertView.setBar(this);
334        }
335        if (MULTIUSER_DEBUG) {
336            mNotificationPanelDebugText = (TextView) mNotificationPanel.findViewById(R.id.header_debug_info);
337            mNotificationPanelDebugText.setVisibility(View.VISIBLE);
338        }
339
340        updateShowSearchHoldoff();
341
342        try {
343            boolean showNav = mWindowManagerService.hasNavigationBar();
344            if (DEBUG) Slog.v(TAG, "hasNavigationBar=" + showNav);
345            if (showNav) {
346                mNavigationBarView =
347                    (NavigationBarView) View.inflate(context, R.layout.navigation_bar, null);
348
349                mNavigationBarView.setDisabledFlags(mDisabled);
350                mNavigationBarView.setBar(this);
351            }
352        } catch (RemoteException ex) {
353            // no window manager? good luck with that
354        }
355
356        // figure out which pixel-format to use for the status bar.
357        mPixelFormat = PixelFormat.OPAQUE;
358        mStatusIcons = (LinearLayout)mStatusBarView.findViewById(R.id.statusIcons);
359        mNotificationIcons = (IconMerger)mStatusBarView.findViewById(R.id.notificationIcons);
360        mNotificationIcons.setOverflowIndicator(mMoreIcon);
361        mIcons = (LinearLayout)mStatusBarView.findViewById(R.id.icons);
362        mTickerView = mStatusBarView.findViewById(R.id.ticker);
363
364        mPile = (NotificationRowLayout)mStatusBarWindow.findViewById(R.id.latestItems);
365        mPile.setLayoutTransitionsEnabled(false);
366        mPile.setLongPressListener(getNotificationLongClicker());
367        mExpandedContents = mPile; // was: expanded.findViewById(R.id.notificationLinearLayout);
368
369        mClearButton = mStatusBarWindow.findViewById(R.id.clear_all_button);
370        mClearButton.setOnClickListener(mClearButtonListener);
371        mClearButton.setAlpha(0f);
372        mClearButton.setVisibility(View.INVISIBLE);
373        mClearButton.setEnabled(false);
374        mDateView = (DateView)mStatusBarWindow.findViewById(R.id.date);
375        mSettingsButton = mStatusBarWindow.findViewById(R.id.settings_button);
376        mSettingsButton.setOnClickListener(mSettingsButtonListener);
377        mRotationButton = (RotationToggle) mStatusBarWindow.findViewById(R.id.rotation_lock_button);
378
379        mScrollView = (ScrollView)mStatusBarWindow.findViewById(R.id.scroll);
380        mScrollView.setVerticalScrollBarEnabled(false); // less drawing during pulldowns
381
382        mTicker = new MyTicker(context, mStatusBarView);
383
384        TickerView tickerView = (TickerView)mStatusBarView.findViewById(R.id.tickerText);
385        tickerView.mTicker = mTicker;
386
387        mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
388
389        // set the inital view visibility
390        setAreThereNotifications();
391
392        // Other icons
393        mLocationController = new LocationController(mContext); // will post a notification
394        mBatteryController = new BatteryController(mContext);
395        mBatteryController.addIconView((ImageView)mStatusBarView.findViewById(R.id.battery));
396        mNetworkController = new NetworkController(mContext);
397        final SignalClusterView signalCluster =
398                (SignalClusterView)mStatusBarView.findViewById(R.id.signal_cluster);
399
400        mNetworkController.addSignalCluster(signalCluster);
401        signalCluster.setNetworkController(mNetworkController);
402
403        mEmergencyCallLabel = (TextView)mStatusBarWindow.findViewById(R.id.emergency_calls_only);
404        if (mEmergencyCallLabel != null) {
405            mNetworkController.addEmergencyLabelView(mEmergencyCallLabel);
406            mEmergencyCallLabel.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
407                @Override
408                public void onLayoutChange(View v, int left, int top, int right, int bottom,
409                        int oldLeft, int oldTop, int oldRight, int oldBottom) {
410                    updateCarrierLabelVisibility(false);
411                }});
412        }
413
414        mCarrierLabel = (TextView)mStatusBarWindow.findViewById(R.id.carrier_label);
415        mShowCarrierInPanel = (mCarrierLabel != null);
416        Slog.v(TAG, "carrierlabel=" + mCarrierLabel + " show=" + mShowCarrierInPanel);
417        if (mShowCarrierInPanel) {
418            mCarrierLabel.setVisibility(mCarrierLabelVisible ? View.VISIBLE : View.INVISIBLE);
419
420            // for mobile devices, we always show mobile connection info here (SPN/PLMN)
421            // for other devices, we show whatever network is connected
422            if (mNetworkController.hasMobileDataFeature()) {
423                mNetworkController.addMobileLabelView(mCarrierLabel);
424            } else {
425                mNetworkController.addCombinedLabelView(mCarrierLabel);
426            }
427
428            // set up the dynamic hide/show of the label
429            mPile.setOnSizeChangedListener(new OnSizeChangedListener() {
430                @Override
431                public void onSizeChanged(View view, int w, int h, int oldw, int oldh) {
432                    updateCarrierLabelVisibility(false);
433                }
434            });
435        }
436
437//        final ImageView wimaxRSSI =
438//                (ImageView)sb.findViewById(R.id.wimax_signal);
439//        if (wimaxRSSI != null) {
440//            mNetworkController.addWimaxIconView(wimaxRSSI);
441//        }
442
443        // receive broadcasts
444        IntentFilter filter = new IntentFilter();
445        filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
446        filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
447        filter.addAction(Intent.ACTION_SCREEN_OFF);
448        filter.addAction(Intent.ACTION_SCREEN_ON);
449        context.registerReceiver(mBroadcastReceiver, filter);
450
451        return mStatusBarView;
452    }
453
454    @Override
455    protected View getStatusBarView() {
456        return mStatusBarView;
457    }
458
459    @Override
460    protected WindowManager.LayoutParams getRecentsLayoutParams(LayoutParams layoutParams) {
461        boolean opaque = false;
462        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
463                layoutParams.width,
464                layoutParams.height,
465                WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
466                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
467                | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
468                | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
469                (opaque ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT));
470        if (ActivityManager.isHighEndGfx()) {
471            lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
472        } else {
473            lp.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;
474            lp.dimAmount = 0.75f;
475        }
476        lp.gravity = Gravity.BOTTOM | Gravity.LEFT;
477        lp.setTitle("RecentsPanel");
478        lp.windowAnimations = com.android.internal.R.style.Animation_RecentApplications;
479        lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
480        | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
481        return lp;
482    }
483
484    @Override
485    protected WindowManager.LayoutParams getSearchLayoutParams(LayoutParams layoutParams) {
486        boolean opaque = false;
487        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
488                LayoutParams.MATCH_PARENT,
489                LayoutParams.MATCH_PARENT,
490                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
491                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
492                | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
493                | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
494                (opaque ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT));
495        if (ActivityManager.isHighEndGfx()) {
496            lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
497        }
498        lp.gravity = Gravity.BOTTOM | Gravity.LEFT;
499        lp.setTitle("SearchPanel");
500        // TODO: Define custom animation for Search panel
501        lp.windowAnimations = com.android.internal.R.style.Animation_RecentApplications;
502        lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
503        | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
504        return lp;
505    }
506
507    @Override
508    protected void updateSearchPanel() {
509        super.updateSearchPanel();
510        mSearchPanelView.setStatusBarView(mNavigationBarView);
511        mNavigationBarView.setDelegateView(mSearchPanelView);
512    }
513
514    @Override
515    public void showSearchPanel() {
516        super.showSearchPanel();
517        WindowManager.LayoutParams lp =
518            (android.view.WindowManager.LayoutParams) mNavigationBarView.getLayoutParams();
519        lp.flags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
520        mWindowManager.updateViewLayout(mNavigationBarView, lp);
521    }
522
523    @Override
524    public void hideSearchPanel() {
525        super.hideSearchPanel();
526        WindowManager.LayoutParams lp =
527            (android.view.WindowManager.LayoutParams) mNavigationBarView.getLayoutParams();
528        lp.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
529        mWindowManager.updateViewLayout(mNavigationBarView, lp);
530    }
531
532    protected int getStatusBarGravity() {
533        return Gravity.TOP | Gravity.FILL_HORIZONTAL;
534    }
535
536    public int getStatusBarHeight() {
537        if (mNaturalBarHeight < 0) {
538            final Resources res = mContext.getResources();
539            mNaturalBarHeight =
540                    res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
541        }
542        return mNaturalBarHeight;
543    }
544
545    private View.OnClickListener mRecentsClickListener = new View.OnClickListener() {
546        public void onClick(View v) {
547            toggleRecentApps();
548        }
549    };
550
551    private int mShowSearchHoldoff = 0;
552    private Runnable mShowSearchPanel = new Runnable() {
553        public void run() {
554            showSearchPanel();
555            awakenDreams();
556        }
557    };
558
559    View.OnTouchListener mHomeSearchActionListener = new View.OnTouchListener() {
560        public boolean onTouch(View v, MotionEvent event) {
561            switch(event.getAction()) {
562            case MotionEvent.ACTION_DOWN:
563                if (!shouldDisableNavbarGestures() && !inKeyguardRestrictedInputMode()) {
564                    mHandler.removeCallbacks(mShowSearchPanel);
565                    mHandler.postDelayed(mShowSearchPanel, mShowSearchHoldoff);
566                }
567            break;
568
569            case MotionEvent.ACTION_UP:
570            case MotionEvent.ACTION_CANCEL:
571                mHandler.removeCallbacks(mShowSearchPanel);
572                awakenDreams();
573            break;
574        }
575        return false;
576        }
577    };
578
579    private void awakenDreams() {
580        if (mDreamManager != null) {
581            try {
582                mDreamManager.awaken();
583            } catch (RemoteException e) {
584                // fine, stay asleep then
585            }
586        }
587    }
588
589    private void prepareNavigationBarView() {
590        mNavigationBarView.reorient();
591
592        mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
593        mNavigationBarView.getRecentsButton().setOnTouchListener(getRecentTasksLoader());
594        mNavigationBarView.getHomeButton().setOnTouchListener(mHomeSearchActionListener);
595        updateSearchPanel();
596    }
597
598    // For small-screen devices (read: phones) that lack hardware navigation buttons
599    private void addNavigationBar() {
600        if (DEBUG) Slog.v(TAG, "addNavigationBar: about to add " + mNavigationBarView);
601        if (mNavigationBarView == null) return;
602
603        prepareNavigationBarView();
604
605        mWindowManager.addView(mNavigationBarView, getNavigationBarLayoutParams());
606    }
607
608    private void repositionNavigationBar() {
609        if (mNavigationBarView == null) return;
610
611        prepareNavigationBarView();
612
613        mWindowManager.updateViewLayout(mNavigationBarView, getNavigationBarLayoutParams());
614    }
615
616    private WindowManager.LayoutParams getNavigationBarLayoutParams() {
617        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
618                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
619                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
620                    0
621                    | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
622                    | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
623                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
624                    | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
625                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
626                PixelFormat.OPAQUE);
627        // this will allow the navbar to run in an overlay on devices that support this
628        if (ActivityManager.isHighEndGfx()) {
629            lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
630        }
631
632        lp.setTitle("NavigationBar");
633        lp.windowAnimations = 0;
634        return lp;
635    }
636
637    private void addIntruderView() {
638        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
639                ViewGroup.LayoutParams.MATCH_PARENT,
640                ViewGroup.LayoutParams.WRAP_CONTENT,
641                WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, // above the status bar!
642                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
643                    | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
644                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
645                    | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
646                    | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
647                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
648                PixelFormat.TRANSLUCENT);
649        lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
650        //lp.y += height * 1.5; // FIXME
651        lp.setTitle("IntruderAlert");
652        lp.packageName = mContext.getPackageName();
653        lp.windowAnimations = R.style.Animation_StatusBar_IntruderAlert;
654
655        mWindowManager.addView(mIntruderAlertView, lp);
656    }
657
658    public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
659        if (SPEW) Slog.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
660                + " icon=" + icon);
661        StatusBarIconView view = new StatusBarIconView(mContext, slot, null);
662        view.set(icon);
663        mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(mIconSize, mIconSize));
664    }
665
666    public void updateIcon(String slot, int index, int viewIndex,
667            StatusBarIcon old, StatusBarIcon icon) {
668        if (SPEW) Slog.d(TAG, "updateIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
669                + " old=" + old + " icon=" + icon);
670        StatusBarIconView view = (StatusBarIconView)mStatusIcons.getChildAt(viewIndex);
671        view.set(icon);
672    }
673
674    public void removeIcon(String slot, int index, int viewIndex) {
675        if (SPEW) Slog.d(TAG, "removeIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex);
676        mStatusIcons.removeViewAt(viewIndex);
677    }
678
679    public void addNotification(IBinder key, StatusBarNotification notification) {
680        /* if (DEBUG) */ Slog.d(TAG, "addNotification score=" + notification.score);
681        StatusBarIconView iconView = addNotificationViews(key, notification);
682        if (iconView == null) return;
683
684        boolean immersive = false;
685        try {
686            immersive = ActivityManagerNative.getDefault().isTopActivityImmersive();
687            if (DEBUG) {
688                Slog.d(TAG, "Top activity is " + (immersive?"immersive":"not immersive"));
689            }
690        } catch (RemoteException ex) {
691        }
692
693        /*
694         * DISABLED due to missing API
695        if (ENABLE_INTRUDERS && (
696                   // TODO(dsandler): Only if the screen is on
697                notification.notification.intruderView != null)) {
698            Slog.d(TAG, "Presenting high-priority notification");
699            // special new transient ticker mode
700            // 1. Populate mIntruderAlertView
701
702            if (notification.notification.intruderView == null) {
703                Slog.e(TAG, notification.notification.toString() + " wanted to intrude but intruderView was null");
704                return;
705            }
706
707            // bind the click event to the content area
708            PendingIntent contentIntent = notification.notification.contentIntent;
709            final View.OnClickListener listener = (contentIntent != null)
710                    ? new NotificationClicker(contentIntent,
711                            notification.pkg, notification.tag, notification.id)
712                    : null;
713
714            mIntruderAlertView.applyIntruderContent(notification.notification.intruderView, listener);
715
716            mCurrentlyIntrudingNotification = notification;
717
718            // 2. Animate mIntruderAlertView in
719            mHandler.sendEmptyMessage(MSG_SHOW_INTRUDER);
720
721            // 3. Set alarm to age the notification off (TODO)
722            mHandler.removeMessages(MSG_HIDE_INTRUDER);
723            if (INTRUDER_ALERT_DECAY_MS > 0) {
724                mHandler.sendEmptyMessageDelayed(MSG_HIDE_INTRUDER, INTRUDER_ALERT_DECAY_MS);
725            }
726        } else
727         */
728
729        if (notification.notification.fullScreenIntent != null) {
730            // Stop screensaver if the notification has a full-screen intent.
731            // (like an incoming phone call)
732            awakenDreams();
733
734            // not immersive & a full-screen alert should be shown
735            Slog.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
736            try {
737                notification.notification.fullScreenIntent.send();
738            } catch (PendingIntent.CanceledException e) {
739            }
740        } else {
741            // usual case: status bar visible & not immersive
742
743            // show the ticker if there isn't an intruder too
744            if (mCurrentlyIntrudingNotification == null) {
745                tick(null, notification, true);
746            }
747        }
748
749        // Recalculate the position of the sliding windows and the titles.
750        setAreThereNotifications();
751        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
752    }
753
754    public void removeNotification(IBinder key) {
755        StatusBarNotification old = removeNotificationViews(key);
756        if (SPEW) Slog.d(TAG, "removeNotification key=" + key + " old=" + old);
757
758        if (old != null) {
759            // Cancel the ticker if it's still running
760            mTicker.removeEntry(old);
761
762            // Recalculate the position of the sliding windows and the titles.
763            updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
764
765            if (ENABLE_INTRUDERS && old == mCurrentlyIntrudingNotification) {
766                mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER);
767            }
768
769            if (CLOSE_PANEL_WHEN_EMPTIED && mNotificationData.size() == 0 && !mAnimating) {
770                animateCollapse();
771            }
772        }
773
774        setAreThereNotifications();
775    }
776
777    private void updateShowSearchHoldoff() {
778        mShowSearchHoldoff = mContext.getResources().getInteger(
779            R.integer.config_show_search_delay);
780    }
781
782    private void loadNotificationShade() {
783        if (mPile == null) return;
784
785        int N = mNotificationData.size();
786
787        ArrayList<View> toShow = new ArrayList<View>();
788
789        final boolean provisioned = isDeviceProvisioned();
790        // If the device hasn't been through Setup, we only show system notifications
791        for (int i=0; i<N; i++) {
792            Entry ent = mNotificationData.get(N-i-1);
793            if (!(provisioned || showNotificationEvenIfUnprovisioned(ent.notification))) continue;
794            if (!notificationIsForCurrentUser(ent.notification)) continue;
795            toShow.add(ent.row);
796        }
797
798        ArrayList<View> toRemove = new ArrayList<View>();
799        for (int i=0; i<mPile.getChildCount(); i++) {
800            View child = mPile.getChildAt(i);
801            if (!toShow.contains(child)) {
802                toRemove.add(child);
803            }
804        }
805
806        for (View remove : toRemove) {
807            mPile.removeView(remove);
808        }
809
810        for (int i=0; i<toShow.size(); i++) {
811            View v = toShow.get(i);
812            if (v.getParent() == null) {
813                mPile.addView(v, i);
814            }
815        }
816
817        mSettingsButton.setEnabled(isDeviceProvisioned());
818    }
819
820    @Override
821    protected void updateNotificationIcons() {
822        if (mNotificationIcons == null) return;
823
824        loadNotificationShade();
825
826        final LinearLayout.LayoutParams params
827            = new LinearLayout.LayoutParams(mIconSize + 2*mIconHPadding, mNaturalBarHeight);
828
829        int N = mNotificationData.size();
830
831        if (DEBUG) {
832            Slog.d(TAG, "refreshing icons: " + N + " notifications, mNotificationIcons=" + mNotificationIcons);
833        }
834
835        ArrayList<View> toShow = new ArrayList<View>();
836
837        final boolean provisioned = isDeviceProvisioned();
838        // If the device hasn't been through Setup, we only show system notifications
839        for (int i=0; i<N; i++) {
840            Entry ent = mNotificationData.get(N-i-1);
841            if (!((provisioned && ent.notification.score >= HIDE_ICONS_BELOW_SCORE)
842                    || showNotificationEvenIfUnprovisioned(ent.notification))) continue;
843            if (!notificationIsForCurrentUser(ent.notification)) continue;
844            toShow.add(ent.icon);
845        }
846
847        ArrayList<View> toRemove = new ArrayList<View>();
848        for (int i=0; i<mNotificationIcons.getChildCount(); i++) {
849            View child = mNotificationIcons.getChildAt(i);
850            if (!toShow.contains(child)) {
851                toRemove.add(child);
852            }
853        }
854
855        for (View remove : toRemove) {
856            mNotificationIcons.removeView(remove);
857        }
858
859        for (int i=0; i<toShow.size(); i++) {
860            View v = toShow.get(i);
861            if (v.getParent() == null) {
862                mNotificationIcons.addView(v, i, params);
863            }
864        }
865    }
866
867    protected void updateCarrierLabelVisibility(boolean force) {
868        if (!mShowCarrierInPanel) return;
869        // The idea here is to only show the carrier label when there is enough room to see it,
870        // i.e. when there aren't enough notifications to fill the panel.
871        if (DEBUG) {
872            Slog.d(TAG, String.format("pileh=%d scrollh=%d carrierh=%d",
873                    mPile.getHeight(), mScrollView.getHeight(), mCarrierLabelHeight));
874        }
875
876        final boolean emergencyCallsShownElsewhere = mEmergencyCallLabel != null;
877        final boolean makeVisible =
878            !(emergencyCallsShownElsewhere && mNetworkController.isEmergencyOnly())
879            && mPile.getHeight() < (mNotificationPanel.getHeight() - mCarrierLabelHeight - mNotificationHeaderHeight);
880
881        if (force || mCarrierLabelVisible != makeVisible) {
882            mCarrierLabelVisible = makeVisible;
883            if (DEBUG) {
884                Slog.d(TAG, "making carrier label " + (makeVisible?"visible":"invisible"));
885            }
886            mCarrierLabel.animate().cancel();
887            if (makeVisible) {
888                mCarrierLabel.setVisibility(View.VISIBLE);
889            }
890            mCarrierLabel.animate()
891                .alpha(makeVisible ? 1f : 0f)
892                //.setStartDelay(makeVisible ? 500 : 0)
893                //.setDuration(makeVisible ? 750 : 100)
894                .setDuration(150)
895                .setListener(makeVisible ? null : new AnimatorListenerAdapter() {
896                    @Override
897                    public void onAnimationEnd(Animator animation) {
898                        if (!mCarrierLabelVisible) { // race
899                            mCarrierLabel.setVisibility(View.INVISIBLE);
900                            mCarrierLabel.setAlpha(0f);
901                        }
902                    }
903                })
904                .start();
905        }
906    }
907
908    @Override
909    protected void setAreThereNotifications() {
910        final boolean any = mNotificationData.size() > 0;
911
912        final boolean clearable = any && mNotificationData.hasClearableItems();
913
914        if (DEBUG) {
915            Slog.d(TAG, "setAreThereNotifications: N=" + mNotificationData.size()
916                    + " any=" + any + " clearable=" + clearable);
917        }
918
919        if (mClearButton.isShown()) {
920            if (clearable != (mClearButton.getAlpha() == 1.0f)) {
921                ObjectAnimator clearAnimation = ObjectAnimator.ofFloat(
922                        mClearButton, "alpha", clearable ? 1.0f : 0.0f).setDuration(250);
923                clearAnimation.addListener(new AnimatorListenerAdapter() {
924                    @Override
925                    public void onAnimationEnd(Animator animation) {
926                        if (mClearButton.getAlpha() <= 0.0f) {
927                            mClearButton.setVisibility(View.INVISIBLE);
928                        }
929                    }
930
931                    @Override
932                    public void onAnimationStart(Animator animation) {
933                        if (mClearButton.getAlpha() <= 0.0f) {
934                            mClearButton.setVisibility(View.VISIBLE);
935                        }
936                    }
937                });
938                clearAnimation.start();
939            }
940        } else {
941            mClearButton.setAlpha(clearable ? 1.0f : 0.0f);
942            mClearButton.setVisibility(clearable ? View.VISIBLE : View.INVISIBLE);
943        }
944        mClearButton.setEnabled(clearable);
945
946        final View nlo = mStatusBarView.findViewById(R.id.notification_lights_out);
947        final boolean showDot = (any&&!areLightsOn());
948        if (showDot != (nlo.getAlpha() == 1.0f)) {
949            if (showDot) {
950                nlo.setAlpha(0f);
951                nlo.setVisibility(View.VISIBLE);
952            }
953            nlo.animate()
954                .alpha(showDot?1:0)
955                .setDuration(showDot?750:250)
956                .setInterpolator(new AccelerateInterpolator(2.0f))
957                .setListener(showDot ? null : new AnimatorListenerAdapter() {
958                    @Override
959                    public void onAnimationEnd(Animator _a) {
960                        nlo.setVisibility(View.GONE);
961                    }
962                })
963                .start();
964        }
965
966        updateCarrierLabelVisibility(false);
967    }
968
969    public void showClock(boolean show) {
970        if (mStatusBarView == null) return;
971        View clock = mStatusBarView.findViewById(R.id.clock);
972        if (clock != null) {
973            clock.setVisibility(show ? View.VISIBLE : View.GONE);
974        }
975    }
976
977    /**
978     * State is one or more of the DISABLE constants from StatusBarManager.
979     */
980    public void disable(int state) {
981        final int old = mDisabled;
982        final int diff = state ^ old;
983        mDisabled = state;
984
985        if (DEBUG) {
986            Slog.d(TAG, String.format("disable: 0x%08x -> 0x%08x (diff: 0x%08x)",
987                old, state, diff));
988        }
989
990        StringBuilder flagdbg = new StringBuilder();
991        flagdbg.append("disable: < ");
992        flagdbg.append(((state & StatusBarManager.DISABLE_EXPAND) != 0) ? "EXPAND" : "expand");
993        flagdbg.append(((diff  & StatusBarManager.DISABLE_EXPAND) != 0) ? "* " : " ");
994        flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "ICONS" : "icons");
995        flagdbg.append(((diff  & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "* " : " ");
996        flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "ALERTS" : "alerts");
997        flagdbg.append(((diff  & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "* " : " ");
998        flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) ? "TICKER" : "ticker");
999        flagdbg.append(((diff  & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) ? "* " : " ");
1000        flagdbg.append(((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "SYSTEM_INFO" : "system_info");
1001        flagdbg.append(((diff  & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "* " : " ");
1002        flagdbg.append(((state & StatusBarManager.DISABLE_BACK) != 0) ? "BACK" : "back");
1003        flagdbg.append(((diff  & StatusBarManager.DISABLE_BACK) != 0) ? "* " : " ");
1004        flagdbg.append(((state & StatusBarManager.DISABLE_HOME) != 0) ? "HOME" : "home");
1005        flagdbg.append(((diff  & StatusBarManager.DISABLE_HOME) != 0) ? "* " : " ");
1006        flagdbg.append(((state & StatusBarManager.DISABLE_RECENT) != 0) ? "RECENT" : "recent");
1007        flagdbg.append(((diff  & StatusBarManager.DISABLE_RECENT) != 0) ? "* " : " ");
1008        flagdbg.append(((state & StatusBarManager.DISABLE_CLOCK) != 0) ? "CLOCK" : "clock");
1009        flagdbg.append(((diff  & StatusBarManager.DISABLE_CLOCK) != 0) ? "* " : " ");
1010        flagdbg.append(">");
1011        Slog.d(TAG, flagdbg.toString());
1012
1013        if ((diff & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
1014            mIcons.animate().cancel();
1015            if ((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
1016                if (mTicking) {
1017                    mTicker.halt();
1018                }
1019                mIcons.animate()
1020                    .alpha(0f)
1021                    .translationY(mNaturalBarHeight*0.5f)
1022                    //.setStartDelay(100)
1023                    .setDuration(175)
1024                    .setInterpolator(new DecelerateInterpolator(1.5f))
1025                    .setListener(mMakeIconsInvisible)
1026                    .start();
1027            } else {
1028                mIcons.setVisibility(View.VISIBLE);
1029                mIcons.animate()
1030                    .alpha(1f)
1031                    .translationY(0)
1032                    .setStartDelay(0)
1033                    .setInterpolator(new DecelerateInterpolator(1.5f))
1034                    .setDuration(175)
1035                    .start();
1036            }
1037        }
1038
1039        if ((diff & StatusBarManager.DISABLE_CLOCK) != 0) {
1040            boolean show = (state & StatusBarManager.DISABLE_CLOCK) == 0;
1041            showClock(show);
1042        }
1043        if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
1044            if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {
1045                animateCollapse();
1046            }
1047        }
1048
1049        if ((diff & (StatusBarManager.DISABLE_HOME
1050                        | StatusBarManager.DISABLE_RECENT
1051                        | StatusBarManager.DISABLE_BACK)) != 0) {
1052            // the nav bar will take care of these
1053            if (mNavigationBarView != null) mNavigationBarView.setDisabledFlags(state);
1054
1055            if ((state & StatusBarManager.DISABLE_RECENT) != 0) {
1056                // close recents if it's visible
1057                mHandler.removeMessages(MSG_CLOSE_RECENTS_PANEL);
1058                mHandler.sendEmptyMessage(MSG_CLOSE_RECENTS_PANEL);
1059            }
1060        }
1061
1062        if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
1063            if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
1064                if (mTicking) {
1065                    mTicker.halt();
1066                } else {
1067                    setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
1068                }
1069            } else {
1070                if (!mExpandedVisible) {
1071                    setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
1072                }
1073            }
1074        } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
1075            if (mTicking && (state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
1076                mTicker.halt();
1077            }
1078        }
1079    }
1080
1081    @Override
1082    protected BaseStatusBar.H createHandler() {
1083        return new PhoneStatusBar.H();
1084    }
1085
1086    /**
1087     * All changes to the status bar and notifications funnel through here and are batched.
1088     */
1089    private class H extends BaseStatusBar.H {
1090        public void handleMessage(Message m) {
1091            super.handleMessage(m);
1092            switch (m.what) {
1093                case MSG_OPEN_NOTIFICATION_PANEL:
1094                    animateExpand();
1095                    break;
1096                case MSG_CLOSE_NOTIFICATION_PANEL:
1097                    animateCollapse();
1098                    break;
1099                case MSG_SHOW_INTRUDER:
1100                    setIntruderAlertVisibility(true);
1101                    break;
1102                case MSG_HIDE_INTRUDER:
1103                    setIntruderAlertVisibility(false);
1104                    mCurrentlyIntrudingNotification = null;
1105                    break;
1106            }
1107        }
1108    }
1109
1110    View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() {
1111        public void onFocusChange(View v, boolean hasFocus) {
1112            // Because 'v' is a ViewGroup, all its children will be (un)selected
1113            // too, which allows marqueeing to work.
1114            v.setSelected(hasFocus);
1115        }
1116    };
1117
1118    void makeExpandedVisible(boolean revealAfterDraw) {
1119        if (SPEW) Slog.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
1120        if (mExpandedVisible) {
1121            return;
1122        }
1123
1124        mExpandedVisible = true;
1125        mPile.setLayoutTransitionsEnabled(true);
1126        if (mNavigationBarView != null)
1127            mNavigationBarView.setSlippery(true);
1128
1129        updateCarrierLabelVisibility(true);
1130
1131        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
1132
1133        // Expand the window to encompass the full screen in anticipation of the drag.
1134        // This is only possible to do atomically because the status bar is at the top of the screen!
1135        WindowManager.LayoutParams lp = (WindowManager.LayoutParams) mStatusBarWindow.getLayoutParams();
1136        lp.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
1137        lp.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
1138        lp.height = ViewGroup.LayoutParams.MATCH_PARENT;
1139        mWindowManager.updateViewLayout(mStatusBarWindow, lp);
1140
1141        // Updating the window layout will force an expensive traversal/redraw.
1142        // Kick off the reveal animation after this is complete to avoid animation latency.
1143        if (revealAfterDraw) {
1144//            mHandler.post(mStartRevealAnimation);
1145        }
1146
1147        visibilityChanged(true);
1148    }
1149
1150    public void animateCollapse() {
1151        animateCollapse(CommandQueue.FLAG_EXCLUDE_NONE);
1152    }
1153
1154    public void animateCollapse(int flags) {
1155        if (SPEW) {
1156            Slog.d(TAG, "animateCollapse(): "
1157                    + " mExpandedVisible=" + mExpandedVisible
1158                    + " mAnimating=" + mAnimating
1159                    + " mAnimatingReveal=" + mAnimatingReveal
1160                    + " mAnimY=" + mAnimY
1161                    + " mAnimVel=" + mAnimVel
1162                    + " flags=" + flags);
1163        }
1164
1165        if ((flags & CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL) == 0) {
1166            mHandler.removeMessages(MSG_CLOSE_RECENTS_PANEL);
1167            mHandler.sendEmptyMessage(MSG_CLOSE_RECENTS_PANEL);
1168        }
1169
1170        if ((flags & CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL) == 0) {
1171            mHandler.removeMessages(MSG_CLOSE_SEARCH_PANEL);
1172            mHandler.sendEmptyMessage(MSG_CLOSE_SEARCH_PANEL);
1173        }
1174
1175        mStatusBarView.collapseAllPanels(true);
1176    }
1177
1178    @Override
1179    public void animateExpand() {
1180        if (SPEW) Slog.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible);
1181        if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
1182            return ;
1183        }
1184
1185        mNotificationPanel.expand();
1186
1187        if (false) postStartTracing();
1188    }
1189
1190    void makeExpandedInvisible() {
1191        if (SPEW) Slog.d(TAG, "makeExpandedInvisible: mExpandedVisible=" + mExpandedVisible
1192                + " mExpandedVisible=" + mExpandedVisible);
1193
1194        if (!mExpandedVisible) {
1195            return;
1196        }
1197
1198        // Ensure the panel is fully collapsed (just in case; bug 6765842)
1199        mStatusBarView.collapseAllPanels(/*animate=*/ false);
1200
1201        mExpandedVisible = false;
1202        mPile.setLayoutTransitionsEnabled(false);
1203        if (mNavigationBarView != null)
1204            mNavigationBarView.setSlippery(false);
1205        visibilityChanged(false);
1206
1207        // Shrink the window to the size of the status bar only
1208        WindowManager.LayoutParams lp = (WindowManager.LayoutParams) mStatusBarWindow.getLayoutParams();
1209        lp.height = getStatusBarHeight();
1210        lp.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
1211        lp.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
1212        mWindowManager.updateViewLayout(mStatusBarWindow, lp);
1213
1214        if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) {
1215            setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
1216        }
1217
1218        // Close any "App info" popups that might have snuck on-screen
1219        dismissPopups();
1220
1221        if (mPostCollapseCleanup != null) {
1222            mPostCollapseCleanup.run();
1223            mPostCollapseCleanup = null;
1224        }
1225    }
1226
1227    /**
1228     * Enables or disables layers on the children of the notifications pile.
1229     *
1230     * When layers are enabled, this method attempts to enable layers for the minimal
1231     * number of children. Only children visible when the notification area is fully
1232     * expanded will receive a layer. The technique used in this method might cause
1233     * more children than necessary to get a layer (at most one extra child with the
1234     * current UI.)
1235     *
1236     * @param layerType {@link View#LAYER_TYPE_NONE} or {@link View#LAYER_TYPE_HARDWARE}
1237     */
1238    private void setPileLayers(int layerType) {
1239        final int count = mPile.getChildCount();
1240
1241        switch (layerType) {
1242            case View.LAYER_TYPE_NONE:
1243                for (int i = 0; i < count; i++) {
1244                    mPile.getChildAt(i).setLayerType(layerType, null);
1245                }
1246                break;
1247            case View.LAYER_TYPE_HARDWARE:
1248                final int[] location = new int[2];
1249                mNotificationPanel.getLocationInWindow(location);
1250
1251                final int left = location[0];
1252                final int top = location[1];
1253                final int right = left + mNotificationPanel.getWidth();
1254                final int bottom = top + getExpandedViewMaxHeight();
1255
1256                final Rect childBounds = new Rect();
1257
1258                for (int i = 0; i < count; i++) {
1259                    final View view = mPile.getChildAt(i);
1260                    view.getLocationInWindow(location);
1261
1262                    childBounds.set(location[0], location[1],
1263                            location[0] + view.getWidth(), location[1] + view.getHeight());
1264
1265                    if (childBounds.intersects(left, top, right, bottom)) {
1266                        view.setLayerType(layerType, null);
1267                    }
1268                }
1269
1270                break;
1271        }
1272    }
1273
1274    boolean interceptTouchEvent(MotionEvent event) {
1275        if (SPEW) {
1276            Slog.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled="
1277                + mDisabled + " mTracking=" + mTracking);
1278        } else if (CHATTY) {
1279            if (event.getAction() != MotionEvent.ACTION_MOVE) {
1280                Slog.d(TAG, String.format(
1281                            "panel: %s at (%f, %f) mDisabled=0x%08x",
1282                            MotionEvent.actionToString(event.getAction()),
1283                            event.getRawX(), event.getRawY(), mDisabled));
1284            }
1285        }
1286
1287        mGestureRec.add(event);
1288
1289        return false;
1290    }
1291
1292    public GestureRecorder getGestureRecorder() {
1293        return mGestureRec;
1294    }
1295
1296    @Override // CommandQueue
1297    public void setNavigationIconHints(int hints) {
1298        if (hints == mNavigationIconHints) return;
1299
1300        mNavigationIconHints = hints;
1301
1302        if (mNavigationBarView != null) {
1303            mNavigationBarView.setNavigationIconHints(hints);
1304        }
1305    }
1306
1307    @Override // CommandQueue
1308    public void setSystemUiVisibility(int vis, int mask) {
1309        final int oldVal = mSystemUiVisibility;
1310        final int newVal = (oldVal&~mask) | (vis&mask);
1311        final int diff = newVal ^ oldVal;
1312
1313        if (diff != 0) {
1314            mSystemUiVisibility = newVal;
1315
1316            if (0 != (diff & View.SYSTEM_UI_FLAG_LOW_PROFILE)) {
1317                final boolean lightsOut = (0 != (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE));
1318                if (lightsOut) {
1319                    animateCollapse();
1320                    if (mTicking) {
1321                        mTicker.halt();
1322                    }
1323                }
1324
1325                if (mNavigationBarView != null) {
1326                    mNavigationBarView.setLowProfile(lightsOut);
1327                }
1328
1329                setStatusBarLowProfile(lightsOut);
1330            }
1331
1332            notifyUiVisibilityChanged();
1333        }
1334    }
1335
1336    private void setStatusBarLowProfile(boolean lightsOut) {
1337        if (mLightsOutAnimation == null) {
1338            final View notifications = mStatusBarView.findViewById(R.id.notification_icon_area);
1339            final View systemIcons = mStatusBarView.findViewById(R.id.statusIcons);
1340            final View signal = mStatusBarView.findViewById(R.id.signal_cluster);
1341            final View battery = mStatusBarView.findViewById(R.id.battery);
1342            final View clock = mStatusBarView.findViewById(R.id.clock);
1343
1344            mLightsOutAnimation = new AnimatorSet();
1345            mLightsOutAnimation.playTogether(
1346                    ObjectAnimator.ofFloat(notifications, View.ALPHA, 0),
1347                    ObjectAnimator.ofFloat(systemIcons, View.ALPHA, 0),
1348                    ObjectAnimator.ofFloat(signal, View.ALPHA, 0),
1349                    ObjectAnimator.ofFloat(battery, View.ALPHA, 0.5f),
1350                    ObjectAnimator.ofFloat(clock, View.ALPHA, 0.5f)
1351                );
1352            mLightsOutAnimation.setDuration(750);
1353
1354            mLightsOnAnimation = new AnimatorSet();
1355            mLightsOnAnimation.playTogether(
1356                    ObjectAnimator.ofFloat(notifications, View.ALPHA, 1),
1357                    ObjectAnimator.ofFloat(systemIcons, View.ALPHA, 1),
1358                    ObjectAnimator.ofFloat(signal, View.ALPHA, 1),
1359                    ObjectAnimator.ofFloat(battery, View.ALPHA, 1),
1360                    ObjectAnimator.ofFloat(clock, View.ALPHA, 1)
1361                );
1362            mLightsOnAnimation.setDuration(250);
1363        }
1364
1365        mLightsOutAnimation.cancel();
1366        mLightsOnAnimation.cancel();
1367
1368        final Animator a = lightsOut ? mLightsOutAnimation : mLightsOnAnimation;
1369        a.start();
1370
1371        setAreThereNotifications();
1372    }
1373
1374    private boolean areLightsOn() {
1375        return 0 == (mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE);
1376    }
1377
1378    public void setLightsOn(boolean on) {
1379        Log.v(TAG, "setLightsOn(" + on + ")");
1380        if (on) {
1381            setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE);
1382        } else {
1383            setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE, View.SYSTEM_UI_FLAG_LOW_PROFILE);
1384        }
1385    }
1386
1387    private void notifyUiVisibilityChanged() {
1388        try {
1389            mWindowManagerService.statusBarVisibilityChanged(mSystemUiVisibility);
1390        } catch (RemoteException ex) {
1391        }
1392    }
1393
1394    public void topAppWindowChanged(boolean showMenu) {
1395        if (DEBUG) {
1396            Slog.d(TAG, (showMenu?"showing":"hiding") + " the MENU button");
1397        }
1398        if (mNavigationBarView != null) {
1399            mNavigationBarView.setMenuVisibility(showMenu);
1400        }
1401
1402        // See above re: lights-out policy for legacy apps.
1403        if (showMenu) setLightsOn(true);
1404    }
1405
1406    @Override
1407    public void setImeWindowStatus(IBinder token, int vis, int backDisposition) {
1408        boolean altBack = (backDisposition == InputMethodService.BACK_DISPOSITION_WILL_DISMISS)
1409            || ((vis & InputMethodService.IME_VISIBLE) != 0);
1410
1411        mCommandQueue.setNavigationIconHints(
1412                altBack ? (mNavigationIconHints | StatusBarManager.NAVIGATION_HINT_BACK_ALT)
1413                        : (mNavigationIconHints & ~StatusBarManager.NAVIGATION_HINT_BACK_ALT));
1414    }
1415
1416    @Override
1417    public void setHardKeyboardStatus(boolean available, boolean enabled) { }
1418
1419    @Override
1420    protected void tick(IBinder key, StatusBarNotification n, boolean firstTime) {
1421        // no ticking in lights-out mode
1422        if (!areLightsOn()) return;
1423
1424        // no ticking in Setup
1425        if (!isDeviceProvisioned()) return;
1426
1427        // Show the ticker if one is requested. Also don't do this
1428        // until status bar window is attached to the window manager,
1429        // because...  well, what's the point otherwise?  And trying to
1430        // run a ticker without being attached will crash!
1431        if (n.notification.tickerText != null && mStatusBarWindow.getWindowToken() != null) {
1432            if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS
1433                            | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
1434                mTicker.addEntry(n);
1435            }
1436        }
1437    }
1438
1439    private class MyTicker extends Ticker {
1440        MyTicker(Context context, View sb) {
1441            super(context, sb);
1442        }
1443
1444        @Override
1445        public void tickerStarting() {
1446            mTicking = true;
1447            mIcons.setVisibility(View.GONE);
1448            mTickerView.setVisibility(View.VISIBLE);
1449            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null));
1450            mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null));
1451        }
1452
1453        @Override
1454        public void tickerDone() {
1455            mIcons.setVisibility(View.VISIBLE);
1456            mTickerView.setVisibility(View.GONE);
1457            mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null));
1458            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out,
1459                        mTickingDoneListener));
1460        }
1461
1462        public void tickerHalting() {
1463            mIcons.setVisibility(View.VISIBLE);
1464            mTickerView.setVisibility(View.GONE);
1465            mIcons.startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
1466            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.fade_out,
1467                        mTickingDoneListener));
1468        }
1469    }
1470
1471    Animation.AnimationListener mTickingDoneListener = new Animation.AnimationListener() {;
1472        public void onAnimationEnd(Animation animation) {
1473            mTicking = false;
1474        }
1475        public void onAnimationRepeat(Animation animation) {
1476        }
1477        public void onAnimationStart(Animation animation) {
1478        }
1479    };
1480
1481    private Animation loadAnim(int id, Animation.AnimationListener listener) {
1482        Animation anim = AnimationUtils.loadAnimation(mContext, id);
1483        if (listener != null) {
1484            anim.setAnimationListener(listener);
1485        }
1486        return anim;
1487    }
1488
1489    public static String viewInfo(View v) {
1490        return "[(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom()
1491                + ") " + v.getWidth() + "x" + v.getHeight() + "]";
1492    }
1493
1494    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1495        synchronized (mQueueLock) {
1496            pw.println("Current Status Bar state:");
1497            pw.println("  mExpandedVisible=" + mExpandedVisible
1498                    + ", mTrackingPosition=" + mTrackingPosition);
1499            pw.println("  mTicking=" + mTicking);
1500            pw.println("  mTracking=" + mTracking);
1501            pw.println("  mNotificationPanel=" +
1502                    ((mNotificationPanel == null)
1503                            ? "null"
1504                            : (mNotificationPanel + " params=" + mNotificationPanel.getLayoutParams().debug(""))));
1505            pw.println("  mAnimating=" + mAnimating
1506                    + ", mAnimY=" + mAnimY + ", mAnimVel=" + mAnimVel
1507                    + ", mAnimAccel=" + mAnimAccel);
1508            pw.println("  mAnimLastTimeNanos=" + mAnimLastTimeNanos);
1509            pw.println("  mAnimatingReveal=" + mAnimatingReveal
1510                    + " mViewDelta=" + mViewDelta);
1511            pw.println("  mDisplayMetrics=" + mDisplayMetrics);
1512            pw.println("  mPile: " + viewInfo(mPile));
1513            pw.println("  mTickerView: " + viewInfo(mTickerView));
1514            pw.println("  mScrollView: " + viewInfo(mScrollView)
1515                    + " scroll " + mScrollView.getScrollX() + "," + mScrollView.getScrollY());
1516        }
1517
1518        pw.print("  mNavigationBarView=");
1519        if (mNavigationBarView == null) {
1520            pw.println("null");
1521        } else {
1522            mNavigationBarView.dump(fd, pw, args);
1523        }
1524
1525        if (DUMPTRUCK) {
1526            synchronized (mNotificationData) {
1527                int N = mNotificationData.size();
1528                pw.println("  notification icons: " + N);
1529                for (int i=0; i<N; i++) {
1530                    NotificationData.Entry e = mNotificationData.get(i);
1531                    pw.println("    [" + i + "] key=" + e.key + " icon=" + e.icon);
1532                    StatusBarNotification n = e.notification;
1533                    pw.println("         pkg=" + n.pkg + " id=" + n.id + " score=" + n.score);
1534                    pw.println("         notification=" + n.notification);
1535                    pw.println("         tickerText=\"" + n.notification.tickerText + "\"");
1536                }
1537            }
1538
1539            int N = mStatusIcons.getChildCount();
1540            pw.println("  system icons: " + N);
1541            for (int i=0; i<N; i++) {
1542                StatusBarIconView ic = (StatusBarIconView) mStatusIcons.getChildAt(i);
1543                pw.println("    [" + i + "] icon=" + ic);
1544            }
1545
1546            if (false) {
1547                pw.println("see the logcat for a dump of the views we have created.");
1548                // must happen on ui thread
1549                mHandler.post(new Runnable() {
1550                        public void run() {
1551                            mStatusBarView.getLocationOnScreen(mAbsPos);
1552                            Slog.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
1553                                    + ") " + mStatusBarView.getWidth() + "x"
1554                                    + getStatusBarHeight());
1555                            mStatusBarView.debug();
1556                        }
1557                    });
1558            }
1559        }
1560
1561        pw.print("  status bar gestures: ");
1562        mGestureRec.dump(fd, pw, args);
1563
1564        mNetworkController.dump(fd, pw, args);
1565    }
1566
1567    @Override
1568    public void createAndAddWindows() {
1569        addStatusBarWindow();
1570    }
1571
1572    private void addStatusBarWindow() {
1573        // Put up the view
1574        final int height = getStatusBarHeight();
1575
1576        // Now that the status bar window encompasses the sliding panel and its
1577        // translucent backdrop, the entire thing is made TRANSLUCENT and is
1578        // hardware-accelerated.
1579        final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
1580                ViewGroup.LayoutParams.MATCH_PARENT,
1581                height,
1582                WindowManager.LayoutParams.TYPE_STATUS_BAR,
1583                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
1584                    | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
1585                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
1586                PixelFormat.TRANSLUCENT);
1587
1588        lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
1589
1590        lp.gravity = getStatusBarGravity();
1591        lp.setTitle("StatusBar");
1592        lp.packageName = mContext.getPackageName();
1593
1594        makeStatusBarView();
1595        mWindowManager.addView(mStatusBarWindow, lp);
1596    }
1597
1598    void setNotificationIconVisibility(boolean visible, int anim) {
1599        int old = mNotificationIcons.getVisibility();
1600        int v = visible ? View.VISIBLE : View.INVISIBLE;
1601        if (old != v) {
1602            mNotificationIcons.setVisibility(v);
1603            mNotificationIcons.startAnimation(loadAnim(anim, null));
1604        }
1605    }
1606
1607    void updateExpandedInvisiblePosition() {
1608        mTrackingPosition = -mDisplayMetrics.heightPixels;
1609    }
1610
1611    static final float saturate(float a) {
1612        return a < 0f ? 0f : (a > 1f ? 1f : a);
1613    }
1614
1615    @Override
1616    protected int getExpandedViewMaxHeight() {
1617        return mDisplayMetrics.heightPixels - mNotificationPanelMarginBottomPx;
1618    }
1619
1620    @Override
1621    public void updateExpandedViewPos(int thingy) {
1622        // TODO
1623        if (DEBUG) Slog.v(TAG, "updateExpandedViewPos");
1624        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mNotificationPanel.getLayoutParams();
1625        lp.gravity = mNotificationPanelGravity;
1626        lp.leftMargin = mNotificationPanelMarginPx;
1627        mNotificationPanel.setLayoutParams(lp);
1628        lp = (FrameLayout.LayoutParams) mSettingsPanel.getLayoutParams();
1629        lp.gravity = mSettingsPanelGravity;
1630        lp.rightMargin = mNotificationPanelMarginPx;
1631        mSettingsPanel.setLayoutParams(lp);
1632
1633        updateCarrierLabelVisibility(false);
1634    }
1635
1636    // called by makeStatusbar and also by PhoneStatusBarView
1637    void updateDisplaySize() {
1638        mDisplay.getMetrics(mDisplayMetrics);
1639        mGestureRec.tag("display",
1640                String.format("%dx%d", mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
1641    }
1642
1643    void performDisableActions(int net) {
1644        int old = mDisabled;
1645        int diff = net ^ old;
1646        mDisabled = net;
1647
1648        // act accordingly
1649        if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
1650            if ((net & StatusBarManager.DISABLE_EXPAND) != 0) {
1651                Slog.d(TAG, "DISABLE_EXPAND: yes");
1652                animateCollapse();
1653            }
1654        }
1655        if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
1656            if ((net & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
1657                Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
1658                if (mTicking) {
1659                    mNotificationIcons.setVisibility(View.INVISIBLE);
1660                    mTicker.halt();
1661                } else {
1662                    setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
1663                }
1664            } else {
1665                Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
1666                if (!mExpandedVisible) {
1667                    setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
1668                }
1669            }
1670        } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
1671            if (mTicking && (net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
1672                mTicker.halt();
1673            }
1674        }
1675    }
1676
1677    private View.OnClickListener mClearButtonListener = new View.OnClickListener() {
1678        public void onClick(View v) {
1679            synchronized (mNotificationData) {
1680                // animate-swipe all dismissable notifications, then animate the shade closed
1681                int numChildren = mPile.getChildCount();
1682
1683                int scrollTop = mScrollView.getScrollY();
1684                int scrollBottom = scrollTop + mScrollView.getHeight();
1685                final ArrayList<View> snapshot = new ArrayList<View>(numChildren);
1686                for (int i=0; i<numChildren; i++) {
1687                    final View child = mPile.getChildAt(i);
1688                    if (mPile.canChildBeDismissed(child) && child.getBottom() > scrollTop &&
1689                            child.getTop() < scrollBottom) {
1690                        snapshot.add(child);
1691                    }
1692                }
1693                if (snapshot.isEmpty()) {
1694                    animateCollapse(CommandQueue.FLAG_EXCLUDE_NONE);
1695                    return;
1696                }
1697                new Thread(new Runnable() {
1698                    @Override
1699                    public void run() {
1700                        // Decrease the delay for every row we animate to give the sense of
1701                        // accelerating the swipes
1702                        final int ROW_DELAY_DECREMENT = 10;
1703                        int currentDelay = 140;
1704                        int totalDelay = 0;
1705
1706                        // Set the shade-animating state to avoid doing other work during
1707                        // all of these animations. In particular, avoid layout and
1708                        // redrawing when collapsing the shade.
1709                        mPile.setViewRemoval(false);
1710
1711                        mPostCollapseCleanup = new Runnable() {
1712                            @Override
1713                            public void run() {
1714                                if (DEBUG) {
1715                                    Slog.v(TAG, "running post-collapse cleanup");
1716                                }
1717                                try {
1718                                    mPile.setViewRemoval(true);
1719                                    mBarService.onClearAllNotifications();
1720                                } catch (Exception ex) { }
1721                            }
1722                        };
1723
1724                        View sampleView = snapshot.get(0);
1725                        int width = sampleView.getWidth();
1726                        final int velocity = width * 8; // 1000/8 = 125 ms duration
1727                        for (final View _v : snapshot) {
1728                            mHandler.postDelayed(new Runnable() {
1729                                @Override
1730                                public void run() {
1731                                    mPile.dismissRowAnimated(_v, velocity);
1732                                }
1733                            }, totalDelay);
1734                            currentDelay = Math.max(50, currentDelay - ROW_DELAY_DECREMENT);
1735                            totalDelay += currentDelay;
1736                        }
1737                        // Delay the collapse animation until after all swipe animations have
1738                        // finished. Provide some buffer because there may be some extra delay
1739                        // before actually starting each swipe animation. Ideally, we'd
1740                        // synchronize the end of those animations with the start of the collaps
1741                        // exactly.
1742                        mHandler.postDelayed(new Runnable() {
1743                            @Override
1744                            public void run() {
1745                                animateCollapse(CommandQueue.FLAG_EXCLUDE_NONE);
1746                            }
1747                        }, totalDelay + 225);
1748                    }
1749                }).start();
1750            }
1751        }
1752    };
1753
1754    private View.OnClickListener mSettingsButtonListener = new View.OnClickListener() {
1755        public void onClick(View v) {
1756            // We take this as a good indicator that Setup is running and we shouldn't
1757            // allow you to go somewhere else
1758            if (!isDeviceProvisioned()) return;
1759            try {
1760                // Dismiss the lock screen when Settings starts.
1761                ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
1762            } catch (RemoteException e) {
1763            }
1764            v.getContext().startActivityAsUser(new Intent(Settings.ACTION_SETTINGS)
1765                    .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
1766                    new UserHandle(UserHandle.USER_CURRENT));
1767            animateCollapse();
1768        }
1769    };
1770
1771    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1772        public void onReceive(Context context, Intent intent) {
1773            Slog.v(TAG, "onReceive: " + intent);
1774            String action = intent.getAction();
1775            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
1776                int flags = CommandQueue.FLAG_EXCLUDE_NONE;
1777                if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
1778                    String reason = intent.getStringExtra("reason");
1779                    if (reason != null && reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) {
1780                        flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL;
1781                    }
1782                }
1783                animateCollapse(flags);
1784            }
1785            else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
1786                // no waiting!
1787                makeExpandedInvisible();
1788            }
1789            else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
1790                if (DEBUG) {
1791                    Slog.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration());
1792                }
1793                updateResources();
1794                repositionNavigationBar();
1795                updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
1796                updateShowSearchHoldoff();
1797            }
1798            else if (Intent.ACTION_SCREEN_ON.equals(action)) {
1799                // work around problem where mDisplay.getRotation() is not stable while screen is off (bug 7086018)
1800                repositionNavigationBar();
1801            }
1802        }
1803    };
1804
1805    @Override
1806    public void userSwitched(int newUserId) {
1807        if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId);
1808        animateCollapse();
1809        updateNotificationIcons();
1810    }
1811
1812    private void setIntruderAlertVisibility(boolean vis) {
1813        if (!ENABLE_INTRUDERS) return;
1814        if (DEBUG) {
1815            Slog.v(TAG, (vis ? "showing" : "hiding") + " intruder alert window");
1816        }
1817        mIntruderAlertView.setVisibility(vis ? View.VISIBLE : View.GONE);
1818    }
1819
1820    public void dismissIntruder() {
1821        if (mCurrentlyIntrudingNotification == null) return;
1822
1823        try {
1824            mBarService.onNotificationClear(
1825                    mCurrentlyIntrudingNotification.pkg,
1826                    mCurrentlyIntrudingNotification.tag,
1827                    mCurrentlyIntrudingNotification.id);
1828        } catch (android.os.RemoteException ex) {
1829            // oh well
1830        }
1831    }
1832
1833    /**
1834     * Reload some of our resources when the configuration changes.
1835     *
1836     * We don't reload everything when the configuration changes -- we probably
1837     * should, but getting that smooth is tough.  Someday we'll fix that.  In the
1838     * meantime, just update the things that we know change.
1839     */
1840    void updateResources() {
1841        final Context context = mContext;
1842        final Resources res = context.getResources();
1843
1844        if (mClearButton instanceof TextView) {
1845            ((TextView)mClearButton).setText(context.getText(R.string.status_bar_clear_all_button));
1846        }
1847        loadDimens();
1848    }
1849
1850    protected void loadDimens() {
1851        final Resources res = mContext.getResources();
1852
1853        mNaturalBarHeight = res.getDimensionPixelSize(
1854                com.android.internal.R.dimen.status_bar_height);
1855
1856        int newIconSize = res.getDimensionPixelSize(
1857            com.android.internal.R.dimen.status_bar_icon_size);
1858        int newIconHPadding = res.getDimensionPixelSize(
1859            R.dimen.status_bar_icon_padding);
1860
1861        if (newIconHPadding != mIconHPadding || newIconSize != mIconSize) {
1862//            Slog.d(TAG, "size=" + newIconSize + " padding=" + newIconHPadding);
1863            mIconHPadding = newIconHPadding;
1864            mIconSize = newIconSize;
1865            //reloadAllNotificationIcons(); // reload the tray
1866        }
1867
1868        mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
1869
1870        mSelfExpandVelocityPx = res.getDimension(R.dimen.self_expand_velocity);
1871        mSelfCollapseVelocityPx = res.getDimension(R.dimen.self_collapse_velocity);
1872        mFlingExpandMinVelocityPx = res.getDimension(R.dimen.fling_expand_min_velocity);
1873        mFlingCollapseMinVelocityPx = res.getDimension(R.dimen.fling_collapse_min_velocity);
1874
1875        mCollapseMinDisplayFraction = res.getFraction(R.dimen.collapse_min_display_fraction, 1, 1);
1876        mExpandMinDisplayFraction = res.getFraction(R.dimen.expand_min_display_fraction, 1, 1);
1877
1878        mExpandAccelPx = res.getDimension(R.dimen.expand_accel);
1879        mCollapseAccelPx = res.getDimension(R.dimen.collapse_accel);
1880
1881        mFlingGestureMaxXVelocityPx = res.getDimension(R.dimen.fling_gesture_max_x_velocity);
1882
1883        mFlingGestureMaxOutputVelocityPx = res.getDimension(R.dimen.fling_gesture_max_output_velocity);
1884
1885        mNotificationPanelMarginBottomPx
1886            = (int) res.getDimension(R.dimen.notification_panel_margin_bottom);
1887        mNotificationPanelMarginPx
1888            = (int) res.getDimension(R.dimen.notification_panel_margin_left);
1889        mNotificationPanelGravity = res.getInteger(R.integer.notification_panel_layout_gravity);
1890        if (mNotificationPanelGravity <= 0) {
1891            mNotificationPanelGravity = Gravity.LEFT | Gravity.TOP;
1892        }
1893        mSettingsPanelGravity = res.getInteger(R.integer.settings_panel_layout_gravity);
1894        if (mSettingsPanelGravity <= 0) {
1895            mSettingsPanelGravity = Gravity.RIGHT | Gravity.TOP;
1896        }
1897        getNinePatchPadding(res.getDrawable(R.drawable.notification_panel_bg), mNotificationPanelBackgroundPadding);
1898        final int notificationPanelDecorationHeight =
1899              res.getDimensionPixelSize(R.dimen.notification_panel_padding_top)
1900            + res.getDimensionPixelSize(R.dimen.notification_panel_header_height)
1901            + mNotificationPanelBackgroundPadding.top
1902            + mNotificationPanelBackgroundPadding.bottom;
1903        mNotificationPanelMinHeight =
1904              notificationPanelDecorationHeight
1905            + res.getDimensionPixelSize(R.dimen.close_handle_underlap);
1906
1907        mCarrierLabelHeight = res.getDimensionPixelSize(R.dimen.carrier_label_height);
1908        mNotificationHeaderHeight = res.getDimensionPixelSize(R.dimen.notification_panel_header_height);
1909
1910        if (false) Slog.v(TAG, "updateResources");
1911    }
1912
1913    private static void getNinePatchPadding(Drawable d, Rect outPadding) {
1914        if (d instanceof NinePatchDrawable) {
1915            NinePatchDrawable ninePatch = (NinePatchDrawable) d;
1916            ninePatch.getPadding(outPadding);
1917        }
1918    }
1919
1920    //
1921    // tracing
1922    //
1923
1924    void postStartTracing() {
1925        mHandler.postDelayed(mStartTracing, 3000);
1926    }
1927
1928    void vibrate() {
1929        android.os.Vibrator vib = (android.os.Vibrator)mContext.getSystemService(
1930                Context.VIBRATOR_SERVICE);
1931        vib.vibrate(250);
1932    }
1933
1934    Runnable mStartTracing = new Runnable() {
1935        public void run() {
1936            vibrate();
1937            SystemClock.sleep(250);
1938            Slog.d(TAG, "startTracing");
1939            android.os.Debug.startMethodTracing("/data/statusbar-traces/trace");
1940            mHandler.postDelayed(mStopTracing, 10000);
1941        }
1942    };
1943
1944    Runnable mStopTracing = new Runnable() {
1945        public void run() {
1946            android.os.Debug.stopMethodTracing();
1947            Slog.d(TAG, "stopTracing");
1948            vibrate();
1949        }
1950    };
1951
1952    @Override
1953    protected void haltTicker() {
1954        mTicker.halt();
1955    }
1956
1957    @Override
1958    protected boolean shouldDisableNavbarGestures() {
1959        return mExpandedVisible || (mDisabled & StatusBarManager.DISABLE_HOME) != 0;
1960    }
1961
1962    private static class FastColorDrawable extends Drawable {
1963        private final int mColor;
1964
1965        public FastColorDrawable(int color) {
1966            mColor = 0xff000000 | color;
1967        }
1968
1969        @Override
1970        public void draw(Canvas canvas) {
1971            canvas.drawColor(mColor, PorterDuff.Mode.SRC);
1972        }
1973
1974        @Override
1975        public void setAlpha(int alpha) {
1976        }
1977
1978        @Override
1979        public void setColorFilter(ColorFilter cf) {
1980        }
1981
1982        @Override
1983        public int getOpacity() {
1984            return PixelFormat.OPAQUE;
1985        }
1986
1987        @Override
1988        public void setBounds(int left, int top, int right, int bottom) {
1989        }
1990
1991        @Override
1992        public void setBounds(Rect bounds) {
1993        }
1994    }
1995}
1996