PhoneStatusBar.java revision 5a426f6c4ac67a52768c56b97d35808dc11905c8
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            // not immersive & a full-screen alert should be shown
731            Slog.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
732            try {
733                notification.notification.fullScreenIntent.send();
734            } catch (PendingIntent.CanceledException e) {
735            }
736        } else {
737            // usual case: status bar visible & not immersive
738
739            // show the ticker if there isn't an intruder too
740            if (mCurrentlyIntrudingNotification == null) {
741                tick(null, notification, true);
742            }
743        }
744
745        // Recalculate the position of the sliding windows and the titles.
746        setAreThereNotifications();
747        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
748    }
749
750    public void removeNotification(IBinder key) {
751        StatusBarNotification old = removeNotificationViews(key);
752        if (SPEW) Slog.d(TAG, "removeNotification key=" + key + " old=" + old);
753
754        if (old != null) {
755            // Cancel the ticker if it's still running
756            mTicker.removeEntry(old);
757
758            // Recalculate the position of the sliding windows and the titles.
759            updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
760
761            if (ENABLE_INTRUDERS && old == mCurrentlyIntrudingNotification) {
762                mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER);
763            }
764
765            if (CLOSE_PANEL_WHEN_EMPTIED && mNotificationData.size() == 0 && !mAnimating) {
766                animateCollapse();
767            }
768        }
769
770        setAreThereNotifications();
771    }
772
773    private void updateShowSearchHoldoff() {
774        mShowSearchHoldoff = mContext.getResources().getInteger(
775            R.integer.config_show_search_delay);
776    }
777
778    private void loadNotificationShade() {
779        if (mPile == null) return;
780
781        int N = mNotificationData.size();
782
783        ArrayList<View> toShow = new ArrayList<View>();
784
785        final boolean provisioned = isDeviceProvisioned();
786        // If the device hasn't been through Setup, we only show system notifications
787        for (int i=0; i<N; i++) {
788            Entry ent = mNotificationData.get(N-i-1);
789            if (!(provisioned || showNotificationEvenIfUnprovisioned(ent.notification))) continue;
790            if (!notificationIsForCurrentUser(ent.notification)) continue;
791            toShow.add(ent.row);
792        }
793
794        ArrayList<View> toRemove = new ArrayList<View>();
795        for (int i=0; i<mPile.getChildCount(); i++) {
796            View child = mPile.getChildAt(i);
797            if (!toShow.contains(child)) {
798                toRemove.add(child);
799            }
800        }
801
802        for (View remove : toRemove) {
803            mPile.removeView(remove);
804        }
805
806        for (int i=0; i<toShow.size(); i++) {
807            View v = toShow.get(i);
808            if (v.getParent() == null) {
809                mPile.addView(v, i);
810            }
811        }
812
813        mSettingsButton.setEnabled(isDeviceProvisioned());
814    }
815
816    @Override
817    protected void updateNotificationIcons() {
818        if (mNotificationIcons == null) return;
819
820        loadNotificationShade();
821
822        final LinearLayout.LayoutParams params
823            = new LinearLayout.LayoutParams(mIconSize + 2*mIconHPadding, mNaturalBarHeight);
824
825        int N = mNotificationData.size();
826
827        if (DEBUG) {
828            Slog.d(TAG, "refreshing icons: " + N + " notifications, mNotificationIcons=" + mNotificationIcons);
829        }
830
831        ArrayList<View> toShow = new ArrayList<View>();
832
833        final boolean provisioned = isDeviceProvisioned();
834        // If the device hasn't been through Setup, we only show system notifications
835        for (int i=0; i<N; i++) {
836            Entry ent = mNotificationData.get(N-i-1);
837            if (!((provisioned && ent.notification.score >= HIDE_ICONS_BELOW_SCORE)
838                    || showNotificationEvenIfUnprovisioned(ent.notification))) continue;
839            if (!notificationIsForCurrentUser(ent.notification)) continue;
840            toShow.add(ent.icon);
841        }
842
843        ArrayList<View> toRemove = new ArrayList<View>();
844        for (int i=0; i<mNotificationIcons.getChildCount(); i++) {
845            View child = mNotificationIcons.getChildAt(i);
846            if (!toShow.contains(child)) {
847                toRemove.add(child);
848            }
849        }
850
851        for (View remove : toRemove) {
852            mNotificationIcons.removeView(remove);
853        }
854
855        for (int i=0; i<toShow.size(); i++) {
856            View v = toShow.get(i);
857            if (v.getParent() == null) {
858                mNotificationIcons.addView(v, i, params);
859            }
860        }
861    }
862
863    protected void updateCarrierLabelVisibility(boolean force) {
864        if (!mShowCarrierInPanel) return;
865        // The idea here is to only show the carrier label when there is enough room to see it,
866        // i.e. when there aren't enough notifications to fill the panel.
867        if (DEBUG) {
868            Slog.d(TAG, String.format("pileh=%d scrollh=%d carrierh=%d",
869                    mPile.getHeight(), mScrollView.getHeight(), mCarrierLabelHeight));
870        }
871
872        final boolean emergencyCallsShownElsewhere = mEmergencyCallLabel != null;
873        final boolean makeVisible =
874            !(emergencyCallsShownElsewhere && mNetworkController.isEmergencyOnly())
875            && mPile.getHeight() < (mNotificationPanel.getHeight() - mCarrierLabelHeight - mNotificationHeaderHeight);
876
877        if (force || mCarrierLabelVisible != makeVisible) {
878            mCarrierLabelVisible = makeVisible;
879            if (DEBUG) {
880                Slog.d(TAG, "making carrier label " + (makeVisible?"visible":"invisible"));
881            }
882            mCarrierLabel.animate().cancel();
883            if (makeVisible) {
884                mCarrierLabel.setVisibility(View.VISIBLE);
885            }
886            mCarrierLabel.animate()
887                .alpha(makeVisible ? 1f : 0f)
888                //.setStartDelay(makeVisible ? 500 : 0)
889                //.setDuration(makeVisible ? 750 : 100)
890                .setDuration(150)
891                .setListener(makeVisible ? null : new AnimatorListenerAdapter() {
892                    @Override
893                    public void onAnimationEnd(Animator animation) {
894                        if (!mCarrierLabelVisible) { // race
895                            mCarrierLabel.setVisibility(View.INVISIBLE);
896                            mCarrierLabel.setAlpha(0f);
897                        }
898                    }
899                })
900                .start();
901        }
902    }
903
904    @Override
905    protected void setAreThereNotifications() {
906        final boolean any = mNotificationData.size() > 0;
907
908        final boolean clearable = any && mNotificationData.hasClearableItems();
909
910        if (DEBUG) {
911            Slog.d(TAG, "setAreThereNotifications: N=" + mNotificationData.size()
912                    + " any=" + any + " clearable=" + clearable);
913        }
914
915        if (mClearButton.isShown()) {
916            if (clearable != (mClearButton.getAlpha() == 1.0f)) {
917                ObjectAnimator clearAnimation = ObjectAnimator.ofFloat(
918                        mClearButton, "alpha", clearable ? 1.0f : 0.0f).setDuration(250);
919                clearAnimation.addListener(new AnimatorListenerAdapter() {
920                    @Override
921                    public void onAnimationEnd(Animator animation) {
922                        if (mClearButton.getAlpha() <= 0.0f) {
923                            mClearButton.setVisibility(View.INVISIBLE);
924                        }
925                    }
926
927                    @Override
928                    public void onAnimationStart(Animator animation) {
929                        if (mClearButton.getAlpha() <= 0.0f) {
930                            mClearButton.setVisibility(View.VISIBLE);
931                        }
932                    }
933                });
934                clearAnimation.start();
935            }
936        } else {
937            mClearButton.setAlpha(clearable ? 1.0f : 0.0f);
938            mClearButton.setVisibility(clearable ? View.VISIBLE : View.INVISIBLE);
939        }
940        mClearButton.setEnabled(clearable);
941
942        final View nlo = mStatusBarView.findViewById(R.id.notification_lights_out);
943        final boolean showDot = (any&&!areLightsOn());
944        if (showDot != (nlo.getAlpha() == 1.0f)) {
945            if (showDot) {
946                nlo.setAlpha(0f);
947                nlo.setVisibility(View.VISIBLE);
948            }
949            nlo.animate()
950                .alpha(showDot?1:0)
951                .setDuration(showDot?750:250)
952                .setInterpolator(new AccelerateInterpolator(2.0f))
953                .setListener(showDot ? null : new AnimatorListenerAdapter() {
954                    @Override
955                    public void onAnimationEnd(Animator _a) {
956                        nlo.setVisibility(View.GONE);
957                    }
958                })
959                .start();
960        }
961
962        updateCarrierLabelVisibility(false);
963    }
964
965    public void showClock(boolean show) {
966        if (mStatusBarView == null) return;
967        View clock = mStatusBarView.findViewById(R.id.clock);
968        if (clock != null) {
969            clock.setVisibility(show ? View.VISIBLE : View.GONE);
970        }
971    }
972
973    /**
974     * State is one or more of the DISABLE constants from StatusBarManager.
975     */
976    public void disable(int state) {
977        final int old = mDisabled;
978        final int diff = state ^ old;
979        mDisabled = state;
980
981        if (DEBUG) {
982            Slog.d(TAG, String.format("disable: 0x%08x -> 0x%08x (diff: 0x%08x)",
983                old, state, diff));
984        }
985
986        StringBuilder flagdbg = new StringBuilder();
987        flagdbg.append("disable: < ");
988        flagdbg.append(((state & StatusBarManager.DISABLE_EXPAND) != 0) ? "EXPAND" : "expand");
989        flagdbg.append(((diff  & StatusBarManager.DISABLE_EXPAND) != 0) ? "* " : " ");
990        flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "ICONS" : "icons");
991        flagdbg.append(((diff  & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "* " : " ");
992        flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "ALERTS" : "alerts");
993        flagdbg.append(((diff  & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "* " : " ");
994        flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) ? "TICKER" : "ticker");
995        flagdbg.append(((diff  & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) ? "* " : " ");
996        flagdbg.append(((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "SYSTEM_INFO" : "system_info");
997        flagdbg.append(((diff  & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "* " : " ");
998        flagdbg.append(((state & StatusBarManager.DISABLE_BACK) != 0) ? "BACK" : "back");
999        flagdbg.append(((diff  & StatusBarManager.DISABLE_BACK) != 0) ? "* " : " ");
1000        flagdbg.append(((state & StatusBarManager.DISABLE_HOME) != 0) ? "HOME" : "home");
1001        flagdbg.append(((diff  & StatusBarManager.DISABLE_HOME) != 0) ? "* " : " ");
1002        flagdbg.append(((state & StatusBarManager.DISABLE_RECENT) != 0) ? "RECENT" : "recent");
1003        flagdbg.append(((diff  & StatusBarManager.DISABLE_RECENT) != 0) ? "* " : " ");
1004        flagdbg.append(((state & StatusBarManager.DISABLE_CLOCK) != 0) ? "CLOCK" : "clock");
1005        flagdbg.append(((diff  & StatusBarManager.DISABLE_CLOCK) != 0) ? "* " : " ");
1006        flagdbg.append(">");
1007        Slog.d(TAG, flagdbg.toString());
1008
1009        if ((diff & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
1010            mIcons.animate().cancel();
1011            if ((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
1012                if (mTicking) {
1013                    mTicker.halt();
1014                }
1015                mIcons.animate()
1016                    .alpha(0f)
1017                    .translationY(mNaturalBarHeight*0.5f)
1018                    //.setStartDelay(100)
1019                    .setDuration(175)
1020                    .setInterpolator(new DecelerateInterpolator(1.5f))
1021                    .setListener(mMakeIconsInvisible)
1022                    .start();
1023            } else {
1024                mIcons.setVisibility(View.VISIBLE);
1025                mIcons.animate()
1026                    .alpha(1f)
1027                    .translationY(0)
1028                    .setStartDelay(0)
1029                    .setInterpolator(new DecelerateInterpolator(1.5f))
1030                    .setDuration(175)
1031                    .start();
1032            }
1033        }
1034
1035        if ((diff & StatusBarManager.DISABLE_CLOCK) != 0) {
1036            boolean show = (state & StatusBarManager.DISABLE_CLOCK) == 0;
1037            showClock(show);
1038        }
1039        if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
1040            if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {
1041                animateCollapse();
1042            }
1043        }
1044
1045        if ((diff & (StatusBarManager.DISABLE_HOME
1046                        | StatusBarManager.DISABLE_RECENT
1047                        | StatusBarManager.DISABLE_BACK)) != 0) {
1048            // the nav bar will take care of these
1049            if (mNavigationBarView != null) mNavigationBarView.setDisabledFlags(state);
1050
1051            if ((state & StatusBarManager.DISABLE_RECENT) != 0) {
1052                // close recents if it's visible
1053                mHandler.removeMessages(MSG_CLOSE_RECENTS_PANEL);
1054                mHandler.sendEmptyMessage(MSG_CLOSE_RECENTS_PANEL);
1055            }
1056        }
1057
1058        if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
1059            if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
1060                if (mTicking) {
1061                    mTicker.halt();
1062                } else {
1063                    setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
1064                }
1065            } else {
1066                if (!mExpandedVisible) {
1067                    setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
1068                }
1069            }
1070        } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
1071            if (mTicking && (state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
1072                mTicker.halt();
1073            }
1074        }
1075    }
1076
1077    @Override
1078    protected BaseStatusBar.H createHandler() {
1079        return new PhoneStatusBar.H();
1080    }
1081
1082    /**
1083     * All changes to the status bar and notifications funnel through here and are batched.
1084     */
1085    private class H extends BaseStatusBar.H {
1086        public void handleMessage(Message m) {
1087            super.handleMessage(m);
1088            switch (m.what) {
1089                case MSG_OPEN_NOTIFICATION_PANEL:
1090                    animateExpand();
1091                    break;
1092                case MSG_CLOSE_NOTIFICATION_PANEL:
1093                    animateCollapse();
1094                    break;
1095                case MSG_SHOW_INTRUDER:
1096                    setIntruderAlertVisibility(true);
1097                    break;
1098                case MSG_HIDE_INTRUDER:
1099                    setIntruderAlertVisibility(false);
1100                    mCurrentlyIntrudingNotification = null;
1101                    break;
1102            }
1103        }
1104    }
1105
1106    View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() {
1107        public void onFocusChange(View v, boolean hasFocus) {
1108            // Because 'v' is a ViewGroup, all its children will be (un)selected
1109            // too, which allows marqueeing to work.
1110            v.setSelected(hasFocus);
1111        }
1112    };
1113
1114    void makeExpandedVisible(boolean revealAfterDraw) {
1115        if (SPEW) Slog.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
1116        if (mExpandedVisible) {
1117            return;
1118        }
1119
1120        mExpandedVisible = true;
1121        mPile.setLayoutTransitionsEnabled(true);
1122        if (mNavigationBarView != null)
1123            mNavigationBarView.setSlippery(true);
1124
1125        updateCarrierLabelVisibility(true);
1126
1127        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
1128
1129        // Expand the window to encompass the full screen in anticipation of the drag.
1130        // This is only possible to do atomically because the status bar is at the top of the screen!
1131        WindowManager.LayoutParams lp = (WindowManager.LayoutParams) mStatusBarWindow.getLayoutParams();
1132        lp.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
1133        lp.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
1134        lp.height = ViewGroup.LayoutParams.MATCH_PARENT;
1135        mWindowManager.updateViewLayout(mStatusBarWindow, lp);
1136
1137        // Updating the window layout will force an expensive traversal/redraw.
1138        // Kick off the reveal animation after this is complete to avoid animation latency.
1139        if (revealAfterDraw) {
1140//            mHandler.post(mStartRevealAnimation);
1141        }
1142
1143        visibilityChanged(true);
1144    }
1145
1146    public void animateCollapse() {
1147        animateCollapse(CommandQueue.FLAG_EXCLUDE_NONE);
1148    }
1149
1150    public void animateCollapse(int flags) {
1151        if (SPEW) {
1152            Slog.d(TAG, "animateCollapse(): "
1153                    + " mExpandedVisible=" + mExpandedVisible
1154                    + " mAnimating=" + mAnimating
1155                    + " mAnimatingReveal=" + mAnimatingReveal
1156                    + " mAnimY=" + mAnimY
1157                    + " mAnimVel=" + mAnimVel
1158                    + " flags=" + flags);
1159        }
1160
1161        if ((flags & CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL) == 0) {
1162            mHandler.removeMessages(MSG_CLOSE_RECENTS_PANEL);
1163            mHandler.sendEmptyMessage(MSG_CLOSE_RECENTS_PANEL);
1164        }
1165
1166        if ((flags & CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL) == 0) {
1167            mHandler.removeMessages(MSG_CLOSE_SEARCH_PANEL);
1168            mHandler.sendEmptyMessage(MSG_CLOSE_SEARCH_PANEL);
1169        }
1170
1171        mStatusBarView.collapseAllPanels(true);
1172    }
1173
1174    @Override
1175    public void animateExpand() {
1176        if (SPEW) Slog.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible);
1177        if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
1178            return ;
1179        }
1180
1181        mNotificationPanel.expand();
1182
1183        if (false) postStartTracing();
1184    }
1185
1186    void makeExpandedInvisible() {
1187        if (SPEW) Slog.d(TAG, "makeExpandedInvisible: mExpandedVisible=" + mExpandedVisible
1188                + " mExpandedVisible=" + mExpandedVisible);
1189
1190        if (!mExpandedVisible) {
1191            return;
1192        }
1193
1194        // Ensure the panel is fully collapsed (just in case; bug 6765842)
1195        mStatusBarView.collapseAllPanels(/*animate=*/ false);
1196
1197        mExpandedVisible = false;
1198        mPile.setLayoutTransitionsEnabled(false);
1199        if (mNavigationBarView != null)
1200            mNavigationBarView.setSlippery(false);
1201        visibilityChanged(false);
1202
1203        // Shrink the window to the size of the status bar only
1204        WindowManager.LayoutParams lp = (WindowManager.LayoutParams) mStatusBarWindow.getLayoutParams();
1205        lp.height = getStatusBarHeight();
1206        lp.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
1207        lp.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
1208        mWindowManager.updateViewLayout(mStatusBarWindow, lp);
1209
1210        if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) {
1211            setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
1212        }
1213
1214        // Close any "App info" popups that might have snuck on-screen
1215        dismissPopups();
1216
1217        if (mPostCollapseCleanup != null) {
1218            mPostCollapseCleanup.run();
1219            mPostCollapseCleanup = null;
1220        }
1221    }
1222
1223    /**
1224     * Enables or disables layers on the children of the notifications pile.
1225     *
1226     * When layers are enabled, this method attempts to enable layers for the minimal
1227     * number of children. Only children visible when the notification area is fully
1228     * expanded will receive a layer. The technique used in this method might cause
1229     * more children than necessary to get a layer (at most one extra child with the
1230     * current UI.)
1231     *
1232     * @param layerType {@link View#LAYER_TYPE_NONE} or {@link View#LAYER_TYPE_HARDWARE}
1233     */
1234    private void setPileLayers(int layerType) {
1235        final int count = mPile.getChildCount();
1236
1237        switch (layerType) {
1238            case View.LAYER_TYPE_NONE:
1239                for (int i = 0; i < count; i++) {
1240                    mPile.getChildAt(i).setLayerType(layerType, null);
1241                }
1242                break;
1243            case View.LAYER_TYPE_HARDWARE:
1244                final int[] location = new int[2];
1245                mNotificationPanel.getLocationInWindow(location);
1246
1247                final int left = location[0];
1248                final int top = location[1];
1249                final int right = left + mNotificationPanel.getWidth();
1250                final int bottom = top + getExpandedViewMaxHeight();
1251
1252                final Rect childBounds = new Rect();
1253
1254                for (int i = 0; i < count; i++) {
1255                    final View view = mPile.getChildAt(i);
1256                    view.getLocationInWindow(location);
1257
1258                    childBounds.set(location[0], location[1],
1259                            location[0] + view.getWidth(), location[1] + view.getHeight());
1260
1261                    if (childBounds.intersects(left, top, right, bottom)) {
1262                        view.setLayerType(layerType, null);
1263                    }
1264                }
1265
1266                break;
1267        }
1268    }
1269
1270    boolean interceptTouchEvent(MotionEvent event) {
1271        if (SPEW) {
1272            Slog.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled="
1273                + mDisabled + " mTracking=" + mTracking);
1274        } else if (CHATTY) {
1275            if (event.getAction() != MotionEvent.ACTION_MOVE) {
1276                Slog.d(TAG, String.format(
1277                            "panel: %s at (%f, %f) mDisabled=0x%08x",
1278                            MotionEvent.actionToString(event.getAction()),
1279                            event.getRawX(), event.getRawY(), mDisabled));
1280            }
1281        }
1282
1283        mGestureRec.add(event);
1284
1285        return false;
1286    }
1287
1288    public GestureRecorder getGestureRecorder() {
1289        return mGestureRec;
1290    }
1291
1292    @Override // CommandQueue
1293    public void setNavigationIconHints(int hints) {
1294        if (hints == mNavigationIconHints) return;
1295
1296        mNavigationIconHints = hints;
1297
1298        if (mNavigationBarView != null) {
1299            mNavigationBarView.setNavigationIconHints(hints);
1300        }
1301    }
1302
1303    @Override // CommandQueue
1304    public void setSystemUiVisibility(int vis, int mask) {
1305        final int oldVal = mSystemUiVisibility;
1306        final int newVal = (oldVal&~mask) | (vis&mask);
1307        final int diff = newVal ^ oldVal;
1308
1309        if (diff != 0) {
1310            mSystemUiVisibility = newVal;
1311
1312            if (0 != (diff & View.SYSTEM_UI_FLAG_LOW_PROFILE)) {
1313                final boolean lightsOut = (0 != (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE));
1314                if (lightsOut) {
1315                    animateCollapse();
1316                    if (mTicking) {
1317                        mTicker.halt();
1318                    }
1319                }
1320
1321                if (mNavigationBarView != null) {
1322                    mNavigationBarView.setLowProfile(lightsOut);
1323                }
1324
1325                setStatusBarLowProfile(lightsOut);
1326            }
1327
1328            notifyUiVisibilityChanged();
1329        }
1330    }
1331
1332    private void setStatusBarLowProfile(boolean lightsOut) {
1333        if (mLightsOutAnimation == null) {
1334            final View notifications = mStatusBarView.findViewById(R.id.notification_icon_area);
1335            final View systemIcons = mStatusBarView.findViewById(R.id.statusIcons);
1336            final View signal = mStatusBarView.findViewById(R.id.signal_cluster);
1337            final View battery = mStatusBarView.findViewById(R.id.battery);
1338            final View clock = mStatusBarView.findViewById(R.id.clock);
1339
1340            mLightsOutAnimation = new AnimatorSet();
1341            mLightsOutAnimation.playTogether(
1342                    ObjectAnimator.ofFloat(notifications, View.ALPHA, 0),
1343                    ObjectAnimator.ofFloat(systemIcons, View.ALPHA, 0),
1344                    ObjectAnimator.ofFloat(signal, View.ALPHA, 0),
1345                    ObjectAnimator.ofFloat(battery, View.ALPHA, 0.5f),
1346                    ObjectAnimator.ofFloat(clock, View.ALPHA, 0.5f)
1347                );
1348            mLightsOutAnimation.setDuration(750);
1349
1350            mLightsOnAnimation = new AnimatorSet();
1351            mLightsOnAnimation.playTogether(
1352                    ObjectAnimator.ofFloat(notifications, View.ALPHA, 1),
1353                    ObjectAnimator.ofFloat(systemIcons, View.ALPHA, 1),
1354                    ObjectAnimator.ofFloat(signal, View.ALPHA, 1),
1355                    ObjectAnimator.ofFloat(battery, View.ALPHA, 1),
1356                    ObjectAnimator.ofFloat(clock, View.ALPHA, 1)
1357                );
1358            mLightsOnAnimation.setDuration(250);
1359        }
1360
1361        mLightsOutAnimation.cancel();
1362        mLightsOnAnimation.cancel();
1363
1364        final Animator a = lightsOut ? mLightsOutAnimation : mLightsOnAnimation;
1365        a.start();
1366
1367        setAreThereNotifications();
1368    }
1369
1370    private boolean areLightsOn() {
1371        return 0 == (mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE);
1372    }
1373
1374    public void setLightsOn(boolean on) {
1375        Log.v(TAG, "setLightsOn(" + on + ")");
1376        if (on) {
1377            setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE);
1378        } else {
1379            setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE, View.SYSTEM_UI_FLAG_LOW_PROFILE);
1380        }
1381    }
1382
1383    private void notifyUiVisibilityChanged() {
1384        try {
1385            mWindowManagerService.statusBarVisibilityChanged(mSystemUiVisibility);
1386        } catch (RemoteException ex) {
1387        }
1388    }
1389
1390    public void topAppWindowChanged(boolean showMenu) {
1391        if (DEBUG) {
1392            Slog.d(TAG, (showMenu?"showing":"hiding") + " the MENU button");
1393        }
1394        if (mNavigationBarView != null) {
1395            mNavigationBarView.setMenuVisibility(showMenu);
1396        }
1397
1398        // See above re: lights-out policy for legacy apps.
1399        if (showMenu) setLightsOn(true);
1400    }
1401
1402    @Override
1403    public void setImeWindowStatus(IBinder token, int vis, int backDisposition) {
1404        boolean altBack = (backDisposition == InputMethodService.BACK_DISPOSITION_WILL_DISMISS)
1405            || ((vis & InputMethodService.IME_VISIBLE) != 0);
1406
1407        mCommandQueue.setNavigationIconHints(
1408                altBack ? (mNavigationIconHints | StatusBarManager.NAVIGATION_HINT_BACK_ALT)
1409                        : (mNavigationIconHints & ~StatusBarManager.NAVIGATION_HINT_BACK_ALT));
1410    }
1411
1412    @Override
1413    public void setHardKeyboardStatus(boolean available, boolean enabled) { }
1414
1415    @Override
1416    protected void tick(IBinder key, StatusBarNotification n, boolean firstTime) {
1417        // no ticking in lights-out mode
1418        if (!areLightsOn()) return;
1419
1420        // no ticking in Setup
1421        if (!isDeviceProvisioned()) return;
1422
1423        // Show the ticker if one is requested. Also don't do this
1424        // until status bar window is attached to the window manager,
1425        // because...  well, what's the point otherwise?  And trying to
1426        // run a ticker without being attached will crash!
1427        if (n.notification.tickerText != null && mStatusBarWindow.getWindowToken() != null) {
1428            if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS
1429                            | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
1430                mTicker.addEntry(n);
1431            }
1432        }
1433    }
1434
1435    private class MyTicker extends Ticker {
1436        MyTicker(Context context, View sb) {
1437            super(context, sb);
1438        }
1439
1440        @Override
1441        public void tickerStarting() {
1442            mTicking = true;
1443            mIcons.setVisibility(View.GONE);
1444            mTickerView.setVisibility(View.VISIBLE);
1445            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null));
1446            mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null));
1447        }
1448
1449        @Override
1450        public void tickerDone() {
1451            mIcons.setVisibility(View.VISIBLE);
1452            mTickerView.setVisibility(View.GONE);
1453            mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null));
1454            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out,
1455                        mTickingDoneListener));
1456        }
1457
1458        public void tickerHalting() {
1459            mIcons.setVisibility(View.VISIBLE);
1460            mTickerView.setVisibility(View.GONE);
1461            mIcons.startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
1462            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.fade_out,
1463                        mTickingDoneListener));
1464        }
1465    }
1466
1467    Animation.AnimationListener mTickingDoneListener = new Animation.AnimationListener() {;
1468        public void onAnimationEnd(Animation animation) {
1469            mTicking = false;
1470        }
1471        public void onAnimationRepeat(Animation animation) {
1472        }
1473        public void onAnimationStart(Animation animation) {
1474        }
1475    };
1476
1477    private Animation loadAnim(int id, Animation.AnimationListener listener) {
1478        Animation anim = AnimationUtils.loadAnimation(mContext, id);
1479        if (listener != null) {
1480            anim.setAnimationListener(listener);
1481        }
1482        return anim;
1483    }
1484
1485    public static String viewInfo(View v) {
1486        return "[(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom()
1487                + ") " + v.getWidth() + "x" + v.getHeight() + "]";
1488    }
1489
1490    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1491        synchronized (mQueueLock) {
1492            pw.println("Current Status Bar state:");
1493            pw.println("  mExpandedVisible=" + mExpandedVisible
1494                    + ", mTrackingPosition=" + mTrackingPosition);
1495            pw.println("  mTicking=" + mTicking);
1496            pw.println("  mTracking=" + mTracking);
1497            pw.println("  mNotificationPanel=" +
1498                    ((mNotificationPanel == null)
1499                            ? "null"
1500                            : (mNotificationPanel + " params=" + mNotificationPanel.getLayoutParams().debug(""))));
1501            pw.println("  mAnimating=" + mAnimating
1502                    + ", mAnimY=" + mAnimY + ", mAnimVel=" + mAnimVel
1503                    + ", mAnimAccel=" + mAnimAccel);
1504            pw.println("  mAnimLastTimeNanos=" + mAnimLastTimeNanos);
1505            pw.println("  mAnimatingReveal=" + mAnimatingReveal
1506                    + " mViewDelta=" + mViewDelta);
1507            pw.println("  mDisplayMetrics=" + mDisplayMetrics);
1508            pw.println("  mPile: " + viewInfo(mPile));
1509            pw.println("  mTickerView: " + viewInfo(mTickerView));
1510            pw.println("  mScrollView: " + viewInfo(mScrollView)
1511                    + " scroll " + mScrollView.getScrollX() + "," + mScrollView.getScrollY());
1512        }
1513
1514        pw.print("  mNavigationBarView=");
1515        if (mNavigationBarView == null) {
1516            pw.println("null");
1517        } else {
1518            mNavigationBarView.dump(fd, pw, args);
1519        }
1520
1521        if (DUMPTRUCK) {
1522            synchronized (mNotificationData) {
1523                int N = mNotificationData.size();
1524                pw.println("  notification icons: " + N);
1525                for (int i=0; i<N; i++) {
1526                    NotificationData.Entry e = mNotificationData.get(i);
1527                    pw.println("    [" + i + "] key=" + e.key + " icon=" + e.icon);
1528                    StatusBarNotification n = e.notification;
1529                    pw.println("         pkg=" + n.pkg + " id=" + n.id + " score=" + n.score);
1530                    pw.println("         notification=" + n.notification);
1531                    pw.println("         tickerText=\"" + n.notification.tickerText + "\"");
1532                }
1533            }
1534
1535            int N = mStatusIcons.getChildCount();
1536            pw.println("  system icons: " + N);
1537            for (int i=0; i<N; i++) {
1538                StatusBarIconView ic = (StatusBarIconView) mStatusIcons.getChildAt(i);
1539                pw.println("    [" + i + "] icon=" + ic);
1540            }
1541
1542            if (false) {
1543                pw.println("see the logcat for a dump of the views we have created.");
1544                // must happen on ui thread
1545                mHandler.post(new Runnable() {
1546                        public void run() {
1547                            mStatusBarView.getLocationOnScreen(mAbsPos);
1548                            Slog.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
1549                                    + ") " + mStatusBarView.getWidth() + "x"
1550                                    + getStatusBarHeight());
1551                            mStatusBarView.debug();
1552                        }
1553                    });
1554            }
1555        }
1556
1557        pw.print("  status bar gestures: ");
1558        mGestureRec.dump(fd, pw, args);
1559
1560        mNetworkController.dump(fd, pw, args);
1561    }
1562
1563    @Override
1564    public void createAndAddWindows() {
1565        addStatusBarWindow();
1566    }
1567
1568    private void addStatusBarWindow() {
1569        // Put up the view
1570        final int height = getStatusBarHeight();
1571
1572        // Now that the status bar window encompasses the sliding panel and its
1573        // translucent backdrop, the entire thing is made TRANSLUCENT and is
1574        // hardware-accelerated.
1575        final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
1576                ViewGroup.LayoutParams.MATCH_PARENT,
1577                height,
1578                WindowManager.LayoutParams.TYPE_STATUS_BAR,
1579                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
1580                    | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
1581                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
1582                PixelFormat.TRANSLUCENT);
1583
1584        lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
1585
1586        lp.gravity = getStatusBarGravity();
1587        lp.setTitle("StatusBar");
1588        lp.packageName = mContext.getPackageName();
1589
1590        makeStatusBarView();
1591        mWindowManager.addView(mStatusBarWindow, lp);
1592    }
1593
1594    void setNotificationIconVisibility(boolean visible, int anim) {
1595        int old = mNotificationIcons.getVisibility();
1596        int v = visible ? View.VISIBLE : View.INVISIBLE;
1597        if (old != v) {
1598            mNotificationIcons.setVisibility(v);
1599            mNotificationIcons.startAnimation(loadAnim(anim, null));
1600        }
1601    }
1602
1603    void updateExpandedInvisiblePosition() {
1604        mTrackingPosition = -mDisplayMetrics.heightPixels;
1605    }
1606
1607    static final float saturate(float a) {
1608        return a < 0f ? 0f : (a > 1f ? 1f : a);
1609    }
1610
1611    @Override
1612    protected int getExpandedViewMaxHeight() {
1613        return mDisplayMetrics.heightPixels - mNotificationPanelMarginBottomPx;
1614    }
1615
1616    @Override
1617    public void updateExpandedViewPos(int thingy) {
1618        // TODO
1619        if (DEBUG) Slog.v(TAG, "updateExpandedViewPos");
1620        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mNotificationPanel.getLayoutParams();
1621        lp.gravity = mNotificationPanelGravity;
1622        lp.leftMargin = mNotificationPanelMarginPx;
1623        mNotificationPanel.setLayoutParams(lp);
1624        lp = (FrameLayout.LayoutParams) mSettingsPanel.getLayoutParams();
1625        lp.gravity = mSettingsPanelGravity;
1626        lp.rightMargin = mNotificationPanelMarginPx;
1627        mSettingsPanel.setLayoutParams(lp);
1628
1629        updateCarrierLabelVisibility(false);
1630    }
1631
1632    // called by makeStatusbar and also by PhoneStatusBarView
1633    void updateDisplaySize() {
1634        mDisplay.getMetrics(mDisplayMetrics);
1635        mGestureRec.tag("display",
1636                String.format("%dx%d", mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
1637    }
1638
1639    void performDisableActions(int net) {
1640        int old = mDisabled;
1641        int diff = net ^ old;
1642        mDisabled = net;
1643
1644        // act accordingly
1645        if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
1646            if ((net & StatusBarManager.DISABLE_EXPAND) != 0) {
1647                Slog.d(TAG, "DISABLE_EXPAND: yes");
1648                animateCollapse();
1649            }
1650        }
1651        if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
1652            if ((net & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
1653                Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
1654                if (mTicking) {
1655                    mNotificationIcons.setVisibility(View.INVISIBLE);
1656                    mTicker.halt();
1657                } else {
1658                    setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
1659                }
1660            } else {
1661                Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
1662                if (!mExpandedVisible) {
1663                    setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
1664                }
1665            }
1666        } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
1667            if (mTicking && (net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
1668                mTicker.halt();
1669            }
1670        }
1671    }
1672
1673    private View.OnClickListener mClearButtonListener = new View.OnClickListener() {
1674        public void onClick(View v) {
1675            synchronized (mNotificationData) {
1676                // animate-swipe all dismissable notifications, then animate the shade closed
1677                int numChildren = mPile.getChildCount();
1678
1679                int scrollTop = mScrollView.getScrollY();
1680                int scrollBottom = scrollTop + mScrollView.getHeight();
1681                final ArrayList<View> snapshot = new ArrayList<View>(numChildren);
1682                for (int i=0; i<numChildren; i++) {
1683                    final View child = mPile.getChildAt(i);
1684                    if (mPile.canChildBeDismissed(child) && child.getBottom() > scrollTop &&
1685                            child.getTop() < scrollBottom) {
1686                        snapshot.add(child);
1687                    }
1688                }
1689                if (snapshot.isEmpty()) {
1690                    animateCollapse(CommandQueue.FLAG_EXCLUDE_NONE);
1691                    return;
1692                }
1693                new Thread(new Runnable() {
1694                    @Override
1695                    public void run() {
1696                        // Decrease the delay for every row we animate to give the sense of
1697                        // accelerating the swipes
1698                        final int ROW_DELAY_DECREMENT = 10;
1699                        int currentDelay = 140;
1700                        int totalDelay = 0;
1701
1702                        // Set the shade-animating state to avoid doing other work during
1703                        // all of these animations. In particular, avoid layout and
1704                        // redrawing when collapsing the shade.
1705                        mPile.setViewRemoval(false);
1706
1707                        mPostCollapseCleanup = new Runnable() {
1708                            @Override
1709                            public void run() {
1710                                if (DEBUG) {
1711                                    Slog.v(TAG, "running post-collapse cleanup");
1712                                }
1713                                try {
1714                                    mPile.setViewRemoval(true);
1715                                    mBarService.onClearAllNotifications();
1716                                } catch (Exception ex) { }
1717                            }
1718                        };
1719
1720                        View sampleView = snapshot.get(0);
1721                        int width = sampleView.getWidth();
1722                        final int velocity = width * 8; // 1000/8 = 125 ms duration
1723                        for (final View _v : snapshot) {
1724                            mHandler.postDelayed(new Runnable() {
1725                                @Override
1726                                public void run() {
1727                                    mPile.dismissRowAnimated(_v, velocity);
1728                                }
1729                            }, totalDelay);
1730                            currentDelay = Math.max(50, currentDelay - ROW_DELAY_DECREMENT);
1731                            totalDelay += currentDelay;
1732                        }
1733                        // Delay the collapse animation until after all swipe animations have
1734                        // finished. Provide some buffer because there may be some extra delay
1735                        // before actually starting each swipe animation. Ideally, we'd
1736                        // synchronize the end of those animations with the start of the collaps
1737                        // exactly.
1738                        mHandler.postDelayed(new Runnable() {
1739                            @Override
1740                            public void run() {
1741                                animateCollapse(CommandQueue.FLAG_EXCLUDE_NONE);
1742                            }
1743                        }, totalDelay + 225);
1744                    }
1745                }).start();
1746            }
1747        }
1748    };
1749
1750    private View.OnClickListener mSettingsButtonListener = new View.OnClickListener() {
1751        public void onClick(View v) {
1752            // We take this as a good indicator that Setup is running and we shouldn't
1753            // allow you to go somewhere else
1754            if (!isDeviceProvisioned()) return;
1755            try {
1756                // Dismiss the lock screen when Settings starts.
1757                ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
1758            } catch (RemoteException e) {
1759            }
1760            v.getContext().startActivityAsUser(new Intent(Settings.ACTION_SETTINGS)
1761                    .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
1762                    new UserHandle(UserHandle.USER_CURRENT));
1763            animateCollapse();
1764        }
1765    };
1766
1767    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1768        public void onReceive(Context context, Intent intent) {
1769            Slog.v(TAG, "onReceive: " + intent);
1770            String action = intent.getAction();
1771            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
1772                int flags = CommandQueue.FLAG_EXCLUDE_NONE;
1773                if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
1774                    String reason = intent.getStringExtra("reason");
1775                    if (reason != null && reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) {
1776                        flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL;
1777                    }
1778                }
1779                animateCollapse(flags);
1780            }
1781            else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
1782                // no waiting!
1783                makeExpandedInvisible();
1784            }
1785            else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
1786                if (DEBUG) {
1787                    Slog.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration());
1788                }
1789                updateResources();
1790                repositionNavigationBar();
1791                updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
1792                updateShowSearchHoldoff();
1793            }
1794            else if (Intent.ACTION_SCREEN_ON.equals(action)) {
1795                // work around problem where mDisplay.getRotation() is not stable while screen is off (bug 7086018)
1796                repositionNavigationBar();
1797            }
1798        }
1799    };
1800
1801    @Override
1802    public void userSwitched(int newUserId) {
1803        if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId);
1804        animateCollapse();
1805        updateNotificationIcons();
1806    }
1807
1808    private void setIntruderAlertVisibility(boolean vis) {
1809        if (!ENABLE_INTRUDERS) return;
1810        if (DEBUG) {
1811            Slog.v(TAG, (vis ? "showing" : "hiding") + " intruder alert window");
1812        }
1813        mIntruderAlertView.setVisibility(vis ? View.VISIBLE : View.GONE);
1814    }
1815
1816    public void dismissIntruder() {
1817        if (mCurrentlyIntrudingNotification == null) return;
1818
1819        try {
1820            mBarService.onNotificationClear(
1821                    mCurrentlyIntrudingNotification.pkg,
1822                    mCurrentlyIntrudingNotification.tag,
1823                    mCurrentlyIntrudingNotification.id);
1824        } catch (android.os.RemoteException ex) {
1825            // oh well
1826        }
1827    }
1828
1829    /**
1830     * Reload some of our resources when the configuration changes.
1831     *
1832     * We don't reload everything when the configuration changes -- we probably
1833     * should, but getting that smooth is tough.  Someday we'll fix that.  In the
1834     * meantime, just update the things that we know change.
1835     */
1836    void updateResources() {
1837        final Context context = mContext;
1838        final Resources res = context.getResources();
1839
1840        if (mClearButton instanceof TextView) {
1841            ((TextView)mClearButton).setText(context.getText(R.string.status_bar_clear_all_button));
1842        }
1843        loadDimens();
1844    }
1845
1846    protected void loadDimens() {
1847        final Resources res = mContext.getResources();
1848
1849        mNaturalBarHeight = res.getDimensionPixelSize(
1850                com.android.internal.R.dimen.status_bar_height);
1851
1852        int newIconSize = res.getDimensionPixelSize(
1853            com.android.internal.R.dimen.status_bar_icon_size);
1854        int newIconHPadding = res.getDimensionPixelSize(
1855            R.dimen.status_bar_icon_padding);
1856
1857        if (newIconHPadding != mIconHPadding || newIconSize != mIconSize) {
1858//            Slog.d(TAG, "size=" + newIconSize + " padding=" + newIconHPadding);
1859            mIconHPadding = newIconHPadding;
1860            mIconSize = newIconSize;
1861            //reloadAllNotificationIcons(); // reload the tray
1862        }
1863
1864        mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
1865
1866        mSelfExpandVelocityPx = res.getDimension(R.dimen.self_expand_velocity);
1867        mSelfCollapseVelocityPx = res.getDimension(R.dimen.self_collapse_velocity);
1868        mFlingExpandMinVelocityPx = res.getDimension(R.dimen.fling_expand_min_velocity);
1869        mFlingCollapseMinVelocityPx = res.getDimension(R.dimen.fling_collapse_min_velocity);
1870
1871        mCollapseMinDisplayFraction = res.getFraction(R.dimen.collapse_min_display_fraction, 1, 1);
1872        mExpandMinDisplayFraction = res.getFraction(R.dimen.expand_min_display_fraction, 1, 1);
1873
1874        mExpandAccelPx = res.getDimension(R.dimen.expand_accel);
1875        mCollapseAccelPx = res.getDimension(R.dimen.collapse_accel);
1876
1877        mFlingGestureMaxXVelocityPx = res.getDimension(R.dimen.fling_gesture_max_x_velocity);
1878
1879        mFlingGestureMaxOutputVelocityPx = res.getDimension(R.dimen.fling_gesture_max_output_velocity);
1880
1881        mNotificationPanelMarginBottomPx
1882            = (int) res.getDimension(R.dimen.notification_panel_margin_bottom);
1883        mNotificationPanelMarginPx
1884            = (int) res.getDimension(R.dimen.notification_panel_margin_left);
1885        mNotificationPanelGravity = res.getInteger(R.integer.notification_panel_layout_gravity);
1886        if (mNotificationPanelGravity <= 0) {
1887            mNotificationPanelGravity = Gravity.LEFT | Gravity.TOP;
1888        }
1889        mSettingsPanelGravity = res.getInteger(R.integer.settings_panel_layout_gravity);
1890        if (mSettingsPanelGravity <= 0) {
1891            mSettingsPanelGravity = Gravity.RIGHT | Gravity.TOP;
1892        }
1893        getNinePatchPadding(res.getDrawable(R.drawable.notification_panel_bg), mNotificationPanelBackgroundPadding);
1894        final int notificationPanelDecorationHeight =
1895              res.getDimensionPixelSize(R.dimen.notification_panel_padding_top)
1896            + res.getDimensionPixelSize(R.dimen.notification_panel_header_height)
1897            + mNotificationPanelBackgroundPadding.top
1898            + mNotificationPanelBackgroundPadding.bottom;
1899        mNotificationPanelMinHeight =
1900              notificationPanelDecorationHeight
1901            + res.getDimensionPixelSize(R.dimen.close_handle_underlap);
1902
1903        mCarrierLabelHeight = res.getDimensionPixelSize(R.dimen.carrier_label_height);
1904        mNotificationHeaderHeight = res.getDimensionPixelSize(R.dimen.notification_panel_header_height);
1905
1906        if (false) Slog.v(TAG, "updateResources");
1907    }
1908
1909    private static void getNinePatchPadding(Drawable d, Rect outPadding) {
1910        if (d instanceof NinePatchDrawable) {
1911            NinePatchDrawable ninePatch = (NinePatchDrawable) d;
1912            ninePatch.getPadding(outPadding);
1913        }
1914    }
1915
1916    //
1917    // tracing
1918    //
1919
1920    void postStartTracing() {
1921        mHandler.postDelayed(mStartTracing, 3000);
1922    }
1923
1924    void vibrate() {
1925        android.os.Vibrator vib = (android.os.Vibrator)mContext.getSystemService(
1926                Context.VIBRATOR_SERVICE);
1927        vib.vibrate(250);
1928    }
1929
1930    Runnable mStartTracing = new Runnable() {
1931        public void run() {
1932            vibrate();
1933            SystemClock.sleep(250);
1934            Slog.d(TAG, "startTracing");
1935            android.os.Debug.startMethodTracing("/data/statusbar-traces/trace");
1936            mHandler.postDelayed(mStopTracing, 10000);
1937        }
1938    };
1939
1940    Runnable mStopTracing = new Runnable() {
1941        public void run() {
1942            android.os.Debug.stopMethodTracing();
1943            Slog.d(TAG, "stopTracing");
1944            vibrate();
1945        }
1946    };
1947
1948    @Override
1949    protected void haltTicker() {
1950        mTicker.halt();
1951    }
1952
1953    @Override
1954    protected boolean shouldDisableNavbarGestures() {
1955        return mExpandedVisible || (mDisabled & StatusBarManager.DISABLE_HOME) != 0;
1956    }
1957
1958    private static class FastColorDrawable extends Drawable {
1959        private final int mColor;
1960
1961        public FastColorDrawable(int color) {
1962            mColor = 0xff000000 | color;
1963        }
1964
1965        @Override
1966        public void draw(Canvas canvas) {
1967            canvas.drawColor(mColor, PorterDuff.Mode.SRC);
1968        }
1969
1970        @Override
1971        public void setAlpha(int alpha) {
1972        }
1973
1974        @Override
1975        public void setColorFilter(ColorFilter cf) {
1976        }
1977
1978        @Override
1979        public int getOpacity() {
1980            return PixelFormat.OPAQUE;
1981        }
1982
1983        @Override
1984        public void setBounds(int left, int top, int right, int bottom) {
1985        }
1986
1987        @Override
1988        public void setBounds(Rect bounds) {
1989        }
1990    }
1991}
1992