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