PhoneStatusBar.java revision d63c59786509aadd6a8d0c5cb45ed696339f16b7
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 = false;
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    RotationToggle mRotationButton;
181
182    // carrier/wifi label
183    private TextView mCarrierLabel;
184    private boolean mCarrierLabelVisible = false;
185    private int mCarrierLabelHeight;
186    private TextView mEmergencyCallLabel;
187    private int mNotificationHeaderHeight;
188
189    private boolean mShowCarrierInPanel = false;
190
191    // position
192    int[] mPositionTmp = new int[2];
193    boolean mExpandedVisible;
194
195    // the date view
196    DateView mDateView;
197
198    // for immersive activities
199    private IntruderAlertView mIntruderAlertView;
200
201    // on-screen navigation buttons
202    private NavigationBarView mNavigationBarView = null;
203
204    // the tracker view
205    int mTrackingPosition; // the position of the top of the tracking view.
206
207    // ticker
208    private Ticker mTicker;
209    private View mTickerView;
210    private boolean mTicking;
211
212    // Tracking finger for opening/closing.
213    int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore
214    boolean mTracking;
215    VelocityTracker mVelocityTracker;
216
217    boolean mAnimating;
218    boolean mClosing; // only valid when mAnimating; indicates the initial acceleration
219    float mAnimY;
220    float mAnimVel;
221    float mAnimAccel;
222    long mAnimLastTimeNanos;
223    boolean mAnimatingReveal = false;
224    int mViewDelta;
225    float mFlingVelocity;
226    int mFlingY;
227    int[] mAbsPos = new int[2];
228    Runnable mPostCollapseCleanup = null;
229
230    private AnimatorSet mLightsOutAnimation;
231    private AnimatorSet mLightsOnAnimation;
232
233    // for disabling the status bar
234    int mDisabled = 0;
235
236    // tracking calls to View.setSystemUiVisibility()
237    int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE;
238
239    DisplayMetrics mDisplayMetrics = new DisplayMetrics();
240
241    // XXX: gesture research
242    private GestureRecorder mGestureRec = new GestureRecorder("/sdcard/statusbar_gestures.dat");
243
244    private int mNavigationIconHints = 0;
245    private final Animator.AnimatorListener mMakeIconsInvisible = new AnimatorListenerAdapter() {
246        @Override
247        public void onAnimationEnd(Animator animation) {
248            // double-check to avoid races
249            if (mIcons.getAlpha() == 0) {
250                Slog.d(TAG, "makeIconsInvisible");
251                mIcons.setVisibility(View.INVISIBLE);
252            }
253        }
254    };
255
256    @Override
257    public void start() {
258        mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
259                .getDefaultDisplay();
260
261        mDreamManager = IDreamManager.Stub.asInterface(
262                ServiceManager.checkService("dreams"));
263
264        super.start(); // calls createAndAddWindows()
265
266        addNavigationBar();
267
268        if (ENABLE_INTRUDERS) addIntruderView();
269
270        // Lastly, call to the icon policy to install/update all the icons.
271        mIconPolicy = new PhoneStatusBarPolicy(mContext);
272    }
273
274    // ================================================================================
275    // Constructing the view
276    // ================================================================================
277    protected PhoneStatusBarView makeStatusBarView() {
278        final Context context = mContext;
279
280        Resources res = context.getResources();
281
282        updateDisplaySize(); // populates mDisplayMetrics
283        loadDimens();
284
285        mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
286
287        mStatusBarWindow = (StatusBarWindowView) View.inflate(context,
288                R.layout.super_status_bar, null);
289        if (DEBUG) {
290            mStatusBarWindow.setBackgroundColor(0x6000FF80);
291        }
292        mStatusBarWindow.mService = this;
293        mStatusBarWindow.setOnTouchListener(new View.OnTouchListener() {
294            @Override
295            public boolean onTouch(View v, MotionEvent event) {
296                if (event.getAction() == MotionEvent.ACTION_DOWN) {
297                    if (mExpandedVisible && !mAnimating) {
298                        animateCollapse();
299                    }
300                }
301                return mStatusBarWindow.onTouchEvent(event);
302            }});
303
304        mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar);
305        mStatusBarView.setBar(this);
306
307        PanelHolder holder = (PanelHolder) mStatusBarWindow.findViewById(R.id.panel_holder);
308        mStatusBarView.setPanelHolder(holder);
309
310        mNotificationPanel = (PanelView) mStatusBarWindow.findViewById(R.id.notification_panel);
311        mNotificationPanelIsFullScreenWidth =
312            (mNotificationPanel.getLayoutParams().width == ViewGroup.LayoutParams.MATCH_PARENT);
313        mNotificationPanel.setSystemUiVisibility(
314                  View.STATUS_BAR_DISABLE_NOTIFICATION_TICKER
315                | (mNotificationPanelIsFullScreenWidth ? 0 : View.STATUS_BAR_DISABLE_SYSTEM_INFO));
316
317        if (!ActivityManager.isHighEndGfx()) {
318            mStatusBarWindow.setBackground(null);
319            mNotificationPanel.setBackground(new FastColorDrawable(context.getResources().getColor(
320                    R.color.notification_panel_solid_background)));
321            mSettingsPanel.setBackground(new FastColorDrawable(context.getResources().getColor(
322                    R.color.notification_panel_solid_background)));
323        }
324        if (ENABLE_INTRUDERS) {
325            mIntruderAlertView = (IntruderAlertView) View.inflate(context, R.layout.intruder_alert, null);
326            mIntruderAlertView.setVisibility(View.GONE);
327            mIntruderAlertView.setBar(this);
328        }
329        if (MULTIUSER_DEBUG) {
330            mNotificationPanelDebugText = (TextView) mNotificationPanel.findViewById(R.id.header_debug_info);
331            mNotificationPanelDebugText.setVisibility(View.VISIBLE);
332        }
333
334        updateShowSearchHoldoff();
335
336        try {
337            boolean showNav = mWindowManagerService.hasNavigationBar();
338            if (DEBUG) Slog.v(TAG, "hasNavigationBar=" + showNav);
339            if (showNav) {
340                mNavigationBarView =
341                    (NavigationBarView) View.inflate(context, R.layout.navigation_bar, null);
342
343                mNavigationBarView.setDisabledFlags(mDisabled);
344                mNavigationBarView.setBar(this);
345            }
346        } catch (RemoteException ex) {
347            // no window manager? good luck with that
348        }
349
350        // figure out which pixel-format to use for the status bar.
351        mPixelFormat = PixelFormat.OPAQUE;
352        mStatusIcons = (LinearLayout)mStatusBarView.findViewById(R.id.statusIcons);
353        mNotificationIcons = (IconMerger)mStatusBarView.findViewById(R.id.notificationIcons);
354        mNotificationIcons.setOverflowIndicator(mMoreIcon);
355        mIcons = (LinearLayout)mStatusBarView.findViewById(R.id.icons);
356        mTickerView = mStatusBarView.findViewById(R.id.ticker);
357
358        mPile = (NotificationRowLayout)mStatusBarWindow.findViewById(R.id.latestItems);
359        mPile.setLayoutTransitionsEnabled(false);
360        mPile.setLongPressListener(getNotificationLongClicker());
361        mExpandedContents = mPile; // was: expanded.findViewById(R.id.notificationLinearLayout);
362
363        mClearButton = mStatusBarWindow.findViewById(R.id.clear_all_button);
364        mClearButton.setOnClickListener(mClearButtonListener);
365        mClearButton.setAlpha(0f);
366        mClearButton.setVisibility(View.INVISIBLE);
367        mClearButton.setEnabled(false);
368        mDateView = (DateView)mStatusBarWindow.findViewById(R.id.date);
369        mSettingsButton = mStatusBarWindow.findViewById(R.id.settings_button);
370        mSettingsButton.setOnClickListener(mSettingsButtonListener);
371        mRotationButton = (RotationToggle) mStatusBarWindow.findViewById(R.id.rotation_lock_button);
372
373        mScrollView = (ScrollView)mStatusBarWindow.findViewById(R.id.scroll);
374        mScrollView.setVerticalScrollBarEnabled(false); // less drawing during pulldowns
375
376        mTicker = new MyTicker(context, mStatusBarView);
377
378        TickerView tickerView = (TickerView)mStatusBarView.findViewById(R.id.tickerText);
379        tickerView.mTicker = mTicker;
380
381        mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
382
383        // set the inital view visibility
384        setAreThereNotifications();
385
386        // Other icons
387        mLocationController = new LocationController(mContext); // will post a notification
388        mBatteryController = new BatteryController(mContext);
389        mBatteryController.addIconView((ImageView)mStatusBarView.findViewById(R.id.battery));
390        mNetworkController = new NetworkController(mContext);
391        mBluetoothController = new BluetoothController(mContext);
392        final SignalClusterView signalCluster =
393                (SignalClusterView)mStatusBarView.findViewById(R.id.signal_cluster);
394
395
396        mNetworkController.addSignalCluster(signalCluster);
397        signalCluster.setNetworkController(mNetworkController);
398
399        mEmergencyCallLabel = (TextView)mStatusBarWindow.findViewById(R.id.emergency_calls_only);
400        if (mEmergencyCallLabel != null) {
401            mNetworkController.addEmergencyLabelView(mEmergencyCallLabel);
402            mEmergencyCallLabel.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
403                @Override
404                public void onLayoutChange(View v, int left, int top, int right, int bottom,
405                        int oldLeft, int oldTop, int oldRight, int oldBottom) {
406                    updateCarrierLabelVisibility(false);
407                }});
408        }
409
410        mCarrierLabel = (TextView)mStatusBarWindow.findViewById(R.id.carrier_label);
411        mShowCarrierInPanel = (mCarrierLabel != null);
412        Slog.v(TAG, "carrierlabel=" + mCarrierLabel + " show=" + mShowCarrierInPanel);
413        if (mShowCarrierInPanel) {
414            mCarrierLabel.setVisibility(mCarrierLabelVisible ? View.VISIBLE : View.INVISIBLE);
415
416            // for mobile devices, we always show mobile connection info here (SPN/PLMN)
417            // for other devices, we show whatever network is connected
418            if (mNetworkController.hasMobileDataFeature()) {
419                mNetworkController.addMobileLabelView(mCarrierLabel);
420            } else {
421                mNetworkController.addCombinedLabelView(mCarrierLabel);
422            }
423
424            // set up the dynamic hide/show of the label
425            mPile.setOnSizeChangedListener(new OnSizeChangedListener() {
426                @Override
427                public void onSizeChanged(View view, int w, int h, int oldw, int oldh) {
428                    updateCarrierLabelVisibility(false);
429                }
430            });
431        }
432
433        // Quick Settings (WIP)
434        mSettingsPanel = (PanelView) mStatusBarWindow.findViewById(R.id.settings_panel);
435        mSettingsPanel.setBar(mStatusBarView);
436        mSettingsPanel.setup(mNetworkController, mBluetoothController, mBatteryController,
437                mLocationController);
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        mSettingsButton.setEnabled(isDeviceProvisioned());
820    }
821
822    @Override
823    protected void updateNotificationIcons() {
824        if (mNotificationIcons == null) return;
825
826        loadNotificationShade();
827
828        final LinearLayout.LayoutParams params
829            = new LinearLayout.LayoutParams(mIconSize + 2*mIconHPadding, mNaturalBarHeight);
830
831        int N = mNotificationData.size();
832
833        if (DEBUG) {
834            Slog.d(TAG, "refreshing icons: " + N + " notifications, mNotificationIcons=" + mNotificationIcons);
835        }
836
837        ArrayList<View> toShow = new ArrayList<View>();
838
839        final boolean provisioned = isDeviceProvisioned();
840        // If the device hasn't been through Setup, we only show system notifications
841        for (int i=0; i<N; i++) {
842            Entry ent = mNotificationData.get(N-i-1);
843            if (!((provisioned && ent.notification.score >= HIDE_ICONS_BELOW_SCORE)
844                    || showNotificationEvenIfUnprovisioned(ent.notification))) continue;
845            if (!notificationIsForCurrentUser(ent.notification)) continue;
846            toShow.add(ent.icon);
847        }
848
849        ArrayList<View> toRemove = new ArrayList<View>();
850        for (int i=0; i<mNotificationIcons.getChildCount(); i++) {
851            View child = mNotificationIcons.getChildAt(i);
852            if (!toShow.contains(child)) {
853                toRemove.add(child);
854            }
855        }
856
857        for (View remove : toRemove) {
858            mNotificationIcons.removeView(remove);
859        }
860
861        for (int i=0; i<toShow.size(); i++) {
862            View v = toShow.get(i);
863            if (v.getParent() == null) {
864                mNotificationIcons.addView(v, i, params);
865            }
866        }
867    }
868
869    protected void updateCarrierLabelVisibility(boolean force) {
870        if (!mShowCarrierInPanel) return;
871        // The idea here is to only show the carrier label when there is enough room to see it,
872        // i.e. when there aren't enough notifications to fill the panel.
873        if (DEBUG) {
874            Slog.d(TAG, String.format("pileh=%d scrollh=%d carrierh=%d",
875                    mPile.getHeight(), mScrollView.getHeight(), mCarrierLabelHeight));
876        }
877
878        final boolean emergencyCallsShownElsewhere = mEmergencyCallLabel != null;
879        final boolean makeVisible =
880            !(emergencyCallsShownElsewhere && mNetworkController.isEmergencyOnly())
881            && mPile.getHeight() < (mNotificationPanel.getHeight() - mCarrierLabelHeight - mNotificationHeaderHeight);
882
883        if (force || mCarrierLabelVisible != makeVisible) {
884            mCarrierLabelVisible = makeVisible;
885            if (DEBUG) {
886                Slog.d(TAG, "making carrier label " + (makeVisible?"visible":"invisible"));
887            }
888            mCarrierLabel.animate().cancel();
889            if (makeVisible) {
890                mCarrierLabel.setVisibility(View.VISIBLE);
891            }
892            mCarrierLabel.animate()
893                .alpha(makeVisible ? 1f : 0f)
894                //.setStartDelay(makeVisible ? 500 : 0)
895                //.setDuration(makeVisible ? 750 : 100)
896                .setDuration(150)
897                .setListener(makeVisible ? null : new AnimatorListenerAdapter() {
898                    @Override
899                    public void onAnimationEnd(Animator animation) {
900                        if (!mCarrierLabelVisible) { // race
901                            mCarrierLabel.setVisibility(View.INVISIBLE);
902                            mCarrierLabel.setAlpha(0f);
903                        }
904                    }
905                })
906                .start();
907        }
908    }
909
910    @Override
911    protected void setAreThereNotifications() {
912        final boolean any = mNotificationData.size() > 0;
913
914        final boolean clearable = any && mNotificationData.hasClearableItems();
915
916        if (DEBUG) {
917            Slog.d(TAG, "setAreThereNotifications: N=" + mNotificationData.size()
918                    + " any=" + any + " clearable=" + clearable);
919        }
920
921        if (mClearButton.isShown()) {
922            if (clearable != (mClearButton.getAlpha() == 1.0f)) {
923                ObjectAnimator clearAnimation = ObjectAnimator.ofFloat(
924                        mClearButton, "alpha", clearable ? 1.0f : 0.0f).setDuration(250);
925                clearAnimation.addListener(new AnimatorListenerAdapter() {
926                    @Override
927                    public void onAnimationEnd(Animator animation) {
928                        if (mClearButton.getAlpha() <= 0.0f) {
929                            mClearButton.setVisibility(View.INVISIBLE);
930                        }
931                    }
932
933                    @Override
934                    public void onAnimationStart(Animator animation) {
935                        if (mClearButton.getAlpha() <= 0.0f) {
936                            mClearButton.setVisibility(View.VISIBLE);
937                        }
938                    }
939                });
940                clearAnimation.start();
941            }
942        } else {
943            mClearButton.setAlpha(clearable ? 1.0f : 0.0f);
944            mClearButton.setVisibility(clearable ? View.VISIBLE : View.INVISIBLE);
945        }
946        mClearButton.setEnabled(clearable);
947
948        final View nlo = mStatusBarView.findViewById(R.id.notification_lights_out);
949        final boolean showDot = (any&&!areLightsOn());
950        if (showDot != (nlo.getAlpha() == 1.0f)) {
951            if (showDot) {
952                nlo.setAlpha(0f);
953                nlo.setVisibility(View.VISIBLE);
954            }
955            nlo.animate()
956                .alpha(showDot?1:0)
957                .setDuration(showDot?750:250)
958                .setInterpolator(new AccelerateInterpolator(2.0f))
959                .setListener(showDot ? null : new AnimatorListenerAdapter() {
960                    @Override
961                    public void onAnimationEnd(Animator _a) {
962                        nlo.setVisibility(View.GONE);
963                    }
964                })
965                .start();
966        }
967
968        updateCarrierLabelVisibility(false);
969    }
970
971    public void showClock(boolean show) {
972        if (mStatusBarView == null) return;
973        View clock = mStatusBarView.findViewById(R.id.clock);
974        if (clock != null) {
975            clock.setVisibility(show ? View.VISIBLE : View.GONE);
976        }
977    }
978
979    /**
980     * State is one or more of the DISABLE constants from StatusBarManager.
981     */
982    public void disable(int state) {
983        final int old = mDisabled;
984        final int diff = state ^ old;
985        mDisabled = state;
986
987        if (DEBUG) {
988            Slog.d(TAG, String.format("disable: 0x%08x -> 0x%08x (diff: 0x%08x)",
989                old, state, diff));
990        }
991
992        StringBuilder flagdbg = new StringBuilder();
993        flagdbg.append("disable: < ");
994        flagdbg.append(((state & StatusBarManager.DISABLE_EXPAND) != 0) ? "EXPAND" : "expand");
995        flagdbg.append(((diff  & StatusBarManager.DISABLE_EXPAND) != 0) ? "* " : " ");
996        flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "ICONS" : "icons");
997        flagdbg.append(((diff  & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "* " : " ");
998        flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "ALERTS" : "alerts");
999        flagdbg.append(((diff  & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "* " : " ");
1000        flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) ? "TICKER" : "ticker");
1001        flagdbg.append(((diff  & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) ? "* " : " ");
1002        flagdbg.append(((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "SYSTEM_INFO" : "system_info");
1003        flagdbg.append(((diff  & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "* " : " ");
1004        flagdbg.append(((state & StatusBarManager.DISABLE_BACK) != 0) ? "BACK" : "back");
1005        flagdbg.append(((diff  & StatusBarManager.DISABLE_BACK) != 0) ? "* " : " ");
1006        flagdbg.append(((state & StatusBarManager.DISABLE_HOME) != 0) ? "HOME" : "home");
1007        flagdbg.append(((diff  & StatusBarManager.DISABLE_HOME) != 0) ? "* " : " ");
1008        flagdbg.append(((state & StatusBarManager.DISABLE_RECENT) != 0) ? "RECENT" : "recent");
1009        flagdbg.append(((diff  & StatusBarManager.DISABLE_RECENT) != 0) ? "* " : " ");
1010        flagdbg.append(((state & StatusBarManager.DISABLE_CLOCK) != 0) ? "CLOCK" : "clock");
1011        flagdbg.append(((diff  & StatusBarManager.DISABLE_CLOCK) != 0) ? "* " : " ");
1012        flagdbg.append(">");
1013        Slog.d(TAG, flagdbg.toString());
1014
1015        if ((diff & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
1016            mIcons.animate().cancel();
1017            if ((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
1018                if (mTicking) {
1019                    mTicker.halt();
1020                }
1021                mIcons.animate()
1022                    .alpha(0f)
1023                    .translationY(mNaturalBarHeight*0.5f)
1024                    //.setStartDelay(100)
1025                    .setDuration(175)
1026                    .setInterpolator(new DecelerateInterpolator(1.5f))
1027                    .setListener(mMakeIconsInvisible)
1028                    .start();
1029            } else {
1030                mIcons.setVisibility(View.VISIBLE);
1031                mIcons.animate()
1032                    .alpha(1f)
1033                    .translationY(0)
1034                    .setStartDelay(0)
1035                    .setInterpolator(new DecelerateInterpolator(1.5f))
1036                    .setDuration(175)
1037                    .start();
1038            }
1039        }
1040
1041        if ((diff & StatusBarManager.DISABLE_CLOCK) != 0) {
1042            boolean show = (state & StatusBarManager.DISABLE_CLOCK) == 0;
1043            showClock(show);
1044        }
1045        if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
1046            if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {
1047                animateCollapse();
1048            }
1049        }
1050
1051        if ((diff & (StatusBarManager.DISABLE_HOME
1052                        | StatusBarManager.DISABLE_RECENT
1053                        | StatusBarManager.DISABLE_BACK)) != 0) {
1054            // the nav bar will take care of these
1055            if (mNavigationBarView != null) mNavigationBarView.setDisabledFlags(state);
1056
1057            if ((state & StatusBarManager.DISABLE_RECENT) != 0) {
1058                // close recents if it's visible
1059                mHandler.removeMessages(MSG_CLOSE_RECENTS_PANEL);
1060                mHandler.sendEmptyMessage(MSG_CLOSE_RECENTS_PANEL);
1061            }
1062        }
1063
1064        if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
1065            if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
1066                if (mTicking) {
1067                    mTicker.halt();
1068                } else {
1069                    setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
1070                }
1071            } else {
1072                if (!mExpandedVisible) {
1073                    setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
1074                }
1075            }
1076        } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
1077            if (mTicking && (state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
1078                mTicker.halt();
1079            }
1080        }
1081    }
1082
1083    @Override
1084    protected BaseStatusBar.H createHandler() {
1085        return new PhoneStatusBar.H();
1086    }
1087
1088    /**
1089     * All changes to the status bar and notifications funnel through here and are batched.
1090     */
1091    private class H extends BaseStatusBar.H {
1092        public void handleMessage(Message m) {
1093            super.handleMessage(m);
1094            switch (m.what) {
1095                case MSG_OPEN_NOTIFICATION_PANEL:
1096                    animateExpand();
1097                    break;
1098                case MSG_CLOSE_NOTIFICATION_PANEL:
1099                    animateCollapse();
1100                    break;
1101                case MSG_SHOW_INTRUDER:
1102                    setIntruderAlertVisibility(true);
1103                    break;
1104                case MSG_HIDE_INTRUDER:
1105                    setIntruderAlertVisibility(false);
1106                    mCurrentlyIntrudingNotification = null;
1107                    break;
1108            }
1109        }
1110    }
1111
1112    View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() {
1113        public void onFocusChange(View v, boolean hasFocus) {
1114            // Because 'v' is a ViewGroup, all its children will be (un)selected
1115            // too, which allows marqueeing to work.
1116            v.setSelected(hasFocus);
1117        }
1118    };
1119
1120    void makeExpandedVisible(boolean revealAfterDraw) {
1121        if (SPEW) Slog.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
1122        if (mExpandedVisible) {
1123            return;
1124        }
1125
1126        mExpandedVisible = true;
1127        mPile.setLayoutTransitionsEnabled(true);
1128        if (mNavigationBarView != null)
1129            mNavigationBarView.setSlippery(true);
1130
1131        updateCarrierLabelVisibility(true);
1132
1133        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
1134
1135        // Expand the window to encompass the full screen in anticipation of the drag.
1136        // This is only possible to do atomically because the status bar is at the top of the screen!
1137        WindowManager.LayoutParams lp = (WindowManager.LayoutParams) mStatusBarWindow.getLayoutParams();
1138        lp.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
1139        lp.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
1140        lp.height = ViewGroup.LayoutParams.MATCH_PARENT;
1141        mWindowManager.updateViewLayout(mStatusBarWindow, lp);
1142
1143        // Updating the window layout will force an expensive traversal/redraw.
1144        // Kick off the reveal animation after this is complete to avoid animation latency.
1145        if (revealAfterDraw) {
1146//            mHandler.post(mStartRevealAnimation);
1147        }
1148
1149        visibilityChanged(true);
1150    }
1151
1152    public void animateCollapse() {
1153        animateCollapse(CommandQueue.FLAG_EXCLUDE_NONE);
1154    }
1155
1156    public void animateCollapse(int flags) {
1157        if (SPEW) {
1158            Slog.d(TAG, "animateCollapse(): "
1159                    + " mExpandedVisible=" + mExpandedVisible
1160                    + " mAnimating=" + mAnimating
1161                    + " mAnimatingReveal=" + mAnimatingReveal
1162                    + " mAnimY=" + mAnimY
1163                    + " mAnimVel=" + mAnimVel
1164                    + " flags=" + flags);
1165        }
1166
1167        if ((flags & CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL) == 0) {
1168            mHandler.removeMessages(MSG_CLOSE_RECENTS_PANEL);
1169            mHandler.sendEmptyMessage(MSG_CLOSE_RECENTS_PANEL);
1170        }
1171
1172        if ((flags & CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL) == 0) {
1173            mHandler.removeMessages(MSG_CLOSE_SEARCH_PANEL);
1174            mHandler.sendEmptyMessage(MSG_CLOSE_SEARCH_PANEL);
1175        }
1176
1177        mStatusBarView.collapseAllPanels(true);
1178    }
1179
1180    @Override
1181    public void animateExpand() {
1182        if (SPEW) Slog.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible);
1183        if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
1184            return ;
1185        }
1186
1187        mNotificationPanel.expand();
1188
1189        if (false) postStartTracing();
1190    }
1191
1192    void makeExpandedInvisible() {
1193        if (SPEW) Slog.d(TAG, "makeExpandedInvisible: mExpandedVisible=" + mExpandedVisible
1194                + " mExpandedVisible=" + mExpandedVisible);
1195
1196        if (!mExpandedVisible) {
1197            return;
1198        }
1199
1200        // Ensure the panel is fully collapsed (just in case; bug 6765842)
1201        mStatusBarView.collapseAllPanels(/*animate=*/ false);
1202
1203        mExpandedVisible = false;
1204        mPile.setLayoutTransitionsEnabled(false);
1205        if (mNavigationBarView != null)
1206            mNavigationBarView.setSlippery(false);
1207        visibilityChanged(false);
1208
1209        // Shrink the window to the size of the status bar only
1210        WindowManager.LayoutParams lp = (WindowManager.LayoutParams) mStatusBarWindow.getLayoutParams();
1211        lp.height = getStatusBarHeight();
1212        lp.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
1213        lp.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
1214        mWindowManager.updateViewLayout(mStatusBarWindow, lp);
1215
1216        if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) {
1217            setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
1218        }
1219
1220        // Close any "App info" popups that might have snuck on-screen
1221        dismissPopups();
1222
1223        if (mPostCollapseCleanup != null) {
1224            mPostCollapseCleanup.run();
1225            mPostCollapseCleanup = null;
1226        }
1227    }
1228
1229    /**
1230     * Enables or disables layers on the children of the notifications pile.
1231     *
1232     * When layers are enabled, this method attempts to enable layers for the minimal
1233     * number of children. Only children visible when the notification area is fully
1234     * expanded will receive a layer. The technique used in this method might cause
1235     * more children than necessary to get a layer (at most one extra child with the
1236     * current UI.)
1237     *
1238     * @param layerType {@link View#LAYER_TYPE_NONE} or {@link View#LAYER_TYPE_HARDWARE}
1239     */
1240    private void setPileLayers(int layerType) {
1241        final int count = mPile.getChildCount();
1242
1243        switch (layerType) {
1244            case View.LAYER_TYPE_NONE:
1245                for (int i = 0; i < count; i++) {
1246                    mPile.getChildAt(i).setLayerType(layerType, null);
1247                }
1248                break;
1249            case View.LAYER_TYPE_HARDWARE:
1250                final int[] location = new int[2];
1251                mNotificationPanel.getLocationInWindow(location);
1252
1253                final int left = location[0];
1254                final int top = location[1];
1255                final int right = left + mNotificationPanel.getWidth();
1256                final int bottom = top + getExpandedViewMaxHeight();
1257
1258                final Rect childBounds = new Rect();
1259
1260                for (int i = 0; i < count; i++) {
1261                    final View view = mPile.getChildAt(i);
1262                    view.getLocationInWindow(location);
1263
1264                    childBounds.set(location[0], location[1],
1265                            location[0] + view.getWidth(), location[1] + view.getHeight());
1266
1267                    if (childBounds.intersects(left, top, right, bottom)) {
1268                        view.setLayerType(layerType, null);
1269                    }
1270                }
1271
1272                break;
1273        }
1274    }
1275
1276    boolean interceptTouchEvent(MotionEvent event) {
1277        if (SPEW) {
1278            Slog.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled="
1279                + mDisabled + " mTracking=" + mTracking);
1280        } else if (CHATTY) {
1281            if (event.getAction() != MotionEvent.ACTION_MOVE) {
1282                Slog.d(TAG, String.format(
1283                            "panel: %s at (%f, %f) mDisabled=0x%08x",
1284                            MotionEvent.actionToString(event.getAction()),
1285                            event.getRawX(), event.getRawY(), mDisabled));
1286            }
1287        }
1288
1289        mGestureRec.add(event);
1290
1291        return false;
1292    }
1293
1294    public GestureRecorder getGestureRecorder() {
1295        return mGestureRec;
1296    }
1297
1298    @Override // CommandQueue
1299    public void setNavigationIconHints(int hints) {
1300        if (hints == mNavigationIconHints) return;
1301
1302        mNavigationIconHints = hints;
1303
1304        if (mNavigationBarView != null) {
1305            mNavigationBarView.setNavigationIconHints(hints);
1306        }
1307    }
1308
1309    @Override // CommandQueue
1310    public void setSystemUiVisibility(int vis, int mask) {
1311        final int oldVal = mSystemUiVisibility;
1312        final int newVal = (oldVal&~mask) | (vis&mask);
1313        final int diff = newVal ^ oldVal;
1314
1315        if (diff != 0) {
1316            mSystemUiVisibility = newVal;
1317
1318            if (0 != (diff & View.SYSTEM_UI_FLAG_LOW_PROFILE)) {
1319                final boolean lightsOut = (0 != (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE));
1320                if (lightsOut) {
1321                    animateCollapse();
1322                    if (mTicking) {
1323                        mTicker.halt();
1324                    }
1325                }
1326
1327                if (mNavigationBarView != null) {
1328                    mNavigationBarView.setLowProfile(lightsOut);
1329                }
1330
1331                setStatusBarLowProfile(lightsOut);
1332            }
1333
1334            notifyUiVisibilityChanged();
1335        }
1336    }
1337
1338    private void setStatusBarLowProfile(boolean lightsOut) {
1339        if (mLightsOutAnimation == null) {
1340            final View notifications = mStatusBarView.findViewById(R.id.notification_icon_area);
1341            final View systemIcons = mStatusBarView.findViewById(R.id.statusIcons);
1342            final View signal = mStatusBarView.findViewById(R.id.signal_cluster);
1343            final View battery = mStatusBarView.findViewById(R.id.battery);
1344            final View clock = mStatusBarView.findViewById(R.id.clock);
1345
1346            mLightsOutAnimation = new AnimatorSet();
1347            mLightsOutAnimation.playTogether(
1348                    ObjectAnimator.ofFloat(notifications, View.ALPHA, 0),
1349                    ObjectAnimator.ofFloat(systemIcons, View.ALPHA, 0),
1350                    ObjectAnimator.ofFloat(signal, View.ALPHA, 0),
1351                    ObjectAnimator.ofFloat(battery, View.ALPHA, 0.5f),
1352                    ObjectAnimator.ofFloat(clock, View.ALPHA, 0.5f)
1353                );
1354            mLightsOutAnimation.setDuration(750);
1355
1356            mLightsOnAnimation = new AnimatorSet();
1357            mLightsOnAnimation.playTogether(
1358                    ObjectAnimator.ofFloat(notifications, View.ALPHA, 1),
1359                    ObjectAnimator.ofFloat(systemIcons, View.ALPHA, 1),
1360                    ObjectAnimator.ofFloat(signal, View.ALPHA, 1),
1361                    ObjectAnimator.ofFloat(battery, View.ALPHA, 1),
1362                    ObjectAnimator.ofFloat(clock, View.ALPHA, 1)
1363                );
1364            mLightsOnAnimation.setDuration(250);
1365        }
1366
1367        mLightsOutAnimation.cancel();
1368        mLightsOnAnimation.cancel();
1369
1370        final Animator a = lightsOut ? mLightsOutAnimation : mLightsOnAnimation;
1371        a.start();
1372
1373        setAreThereNotifications();
1374    }
1375
1376    private boolean areLightsOn() {
1377        return 0 == (mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE);
1378    }
1379
1380    public void setLightsOn(boolean on) {
1381        Log.v(TAG, "setLightsOn(" + on + ")");
1382        if (on) {
1383            setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE);
1384        } else {
1385            setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE, View.SYSTEM_UI_FLAG_LOW_PROFILE);
1386        }
1387    }
1388
1389    private void notifyUiVisibilityChanged() {
1390        try {
1391            mWindowManagerService.statusBarVisibilityChanged(mSystemUiVisibility);
1392        } catch (RemoteException ex) {
1393        }
1394    }
1395
1396    public void topAppWindowChanged(boolean showMenu) {
1397        if (DEBUG) {
1398            Slog.d(TAG, (showMenu?"showing":"hiding") + " the MENU button");
1399        }
1400        if (mNavigationBarView != null) {
1401            mNavigationBarView.setMenuVisibility(showMenu);
1402        }
1403
1404        // See above re: lights-out policy for legacy apps.
1405        if (showMenu) setLightsOn(true);
1406    }
1407
1408    @Override
1409    public void setImeWindowStatus(IBinder token, int vis, int backDisposition) {
1410        boolean altBack = (backDisposition == InputMethodService.BACK_DISPOSITION_WILL_DISMISS)
1411            || ((vis & InputMethodService.IME_VISIBLE) != 0);
1412
1413        mCommandQueue.setNavigationIconHints(
1414                altBack ? (mNavigationIconHints | StatusBarManager.NAVIGATION_HINT_BACK_ALT)
1415                        : (mNavigationIconHints & ~StatusBarManager.NAVIGATION_HINT_BACK_ALT));
1416    }
1417
1418    @Override
1419    public void setHardKeyboardStatus(boolean available, boolean enabled) { }
1420
1421    @Override
1422    protected void tick(IBinder key, StatusBarNotification n, boolean firstTime) {
1423        // no ticking in lights-out mode
1424        if (!areLightsOn()) return;
1425
1426        // no ticking in Setup
1427        if (!isDeviceProvisioned()) return;
1428
1429        // Show the ticker if one is requested. Also don't do this
1430        // until status bar window is attached to the window manager,
1431        // because...  well, what's the point otherwise?  And trying to
1432        // run a ticker without being attached will crash!
1433        if (n.notification.tickerText != null && mStatusBarWindow.getWindowToken() != null) {
1434            if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS
1435                            | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
1436                mTicker.addEntry(n);
1437            }
1438        }
1439    }
1440
1441    private class MyTicker extends Ticker {
1442        MyTicker(Context context, View sb) {
1443            super(context, sb);
1444        }
1445
1446        @Override
1447        public void tickerStarting() {
1448            mTicking = true;
1449            mIcons.setVisibility(View.GONE);
1450            mTickerView.setVisibility(View.VISIBLE);
1451            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null));
1452            mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null));
1453        }
1454
1455        @Override
1456        public void tickerDone() {
1457            mIcons.setVisibility(View.VISIBLE);
1458            mTickerView.setVisibility(View.GONE);
1459            mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null));
1460            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out,
1461                        mTickingDoneListener));
1462        }
1463
1464        public void tickerHalting() {
1465            mIcons.setVisibility(View.VISIBLE);
1466            mTickerView.setVisibility(View.GONE);
1467            mIcons.startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
1468            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.fade_out,
1469                        mTickingDoneListener));
1470        }
1471    }
1472
1473    Animation.AnimationListener mTickingDoneListener = new Animation.AnimationListener() {;
1474        public void onAnimationEnd(Animation animation) {
1475            mTicking = false;
1476        }
1477        public void onAnimationRepeat(Animation animation) {
1478        }
1479        public void onAnimationStart(Animation animation) {
1480        }
1481    };
1482
1483    private Animation loadAnim(int id, Animation.AnimationListener listener) {
1484        Animation anim = AnimationUtils.loadAnimation(mContext, id);
1485        if (listener != null) {
1486            anim.setAnimationListener(listener);
1487        }
1488        return anim;
1489    }
1490
1491    public static String viewInfo(View v) {
1492        return "[(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom()
1493                + ") " + v.getWidth() + "x" + v.getHeight() + "]";
1494    }
1495
1496    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1497        synchronized (mQueueLock) {
1498            pw.println("Current Status Bar state:");
1499            pw.println("  mExpandedVisible=" + mExpandedVisible
1500                    + ", mTrackingPosition=" + mTrackingPosition);
1501            pw.println("  mTicking=" + mTicking);
1502            pw.println("  mTracking=" + mTracking);
1503            pw.println("  mNotificationPanel=" +
1504                    ((mNotificationPanel == null)
1505                            ? "null"
1506                            : (mNotificationPanel + " params=" + mNotificationPanel.getLayoutParams().debug(""))));
1507            pw.println("  mAnimating=" + mAnimating
1508                    + ", mAnimY=" + mAnimY + ", mAnimVel=" + mAnimVel
1509                    + ", mAnimAccel=" + mAnimAccel);
1510            pw.println("  mAnimLastTimeNanos=" + mAnimLastTimeNanos);
1511            pw.println("  mAnimatingReveal=" + mAnimatingReveal
1512                    + " mViewDelta=" + mViewDelta);
1513            pw.println("  mDisplayMetrics=" + mDisplayMetrics);
1514            pw.println("  mPile: " + viewInfo(mPile));
1515            pw.println("  mTickerView: " + viewInfo(mTickerView));
1516            pw.println("  mScrollView: " + viewInfo(mScrollView)
1517                    + " scroll " + mScrollView.getScrollX() + "," + mScrollView.getScrollY());
1518        }
1519
1520        pw.print("  mNavigationBarView=");
1521        if (mNavigationBarView == null) {
1522            pw.println("null");
1523        } else {
1524            mNavigationBarView.dump(fd, pw, args);
1525        }
1526
1527        if (DUMPTRUCK) {
1528            synchronized (mNotificationData) {
1529                int N = mNotificationData.size();
1530                pw.println("  notification icons: " + N);
1531                for (int i=0; i<N; i++) {
1532                    NotificationData.Entry e = mNotificationData.get(i);
1533                    pw.println("    [" + i + "] key=" + e.key + " icon=" + e.icon);
1534                    StatusBarNotification n = e.notification;
1535                    pw.println("         pkg=" + n.pkg + " id=" + n.id + " score=" + n.score);
1536                    pw.println("         notification=" + n.notification);
1537                    pw.println("         tickerText=\"" + n.notification.tickerText + "\"");
1538                }
1539            }
1540
1541            int N = mStatusIcons.getChildCount();
1542            pw.println("  system icons: " + N);
1543            for (int i=0; i<N; i++) {
1544                StatusBarIconView ic = (StatusBarIconView) mStatusIcons.getChildAt(i);
1545                pw.println("    [" + i + "] icon=" + ic);
1546            }
1547
1548            if (false) {
1549                pw.println("see the logcat for a dump of the views we have created.");
1550                // must happen on ui thread
1551                mHandler.post(new Runnable() {
1552                        public void run() {
1553                            mStatusBarView.getLocationOnScreen(mAbsPos);
1554                            Slog.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
1555                                    + ") " + mStatusBarView.getWidth() + "x"
1556                                    + getStatusBarHeight());
1557                            mStatusBarView.debug();
1558                        }
1559                    });
1560            }
1561        }
1562
1563        pw.print("  status bar gestures: ");
1564        mGestureRec.dump(fd, pw, args);
1565
1566        mNetworkController.dump(fd, pw, args);
1567    }
1568
1569    @Override
1570    public void createAndAddWindows() {
1571        addStatusBarWindow();
1572    }
1573
1574    private void addStatusBarWindow() {
1575        // Put up the view
1576        final int height = getStatusBarHeight();
1577
1578        // Now that the status bar window encompasses the sliding panel and its
1579        // translucent backdrop, the entire thing is made TRANSLUCENT and is
1580        // hardware-accelerated.
1581        final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
1582                ViewGroup.LayoutParams.MATCH_PARENT,
1583                height,
1584                WindowManager.LayoutParams.TYPE_STATUS_BAR,
1585                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
1586                    | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
1587                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
1588                PixelFormat.TRANSLUCENT);
1589
1590        lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
1591
1592        lp.gravity = getStatusBarGravity();
1593        lp.setTitle("StatusBar");
1594        lp.packageName = mContext.getPackageName();
1595
1596        makeStatusBarView();
1597        mWindowManager.addView(mStatusBarWindow, lp);
1598    }
1599
1600    void setNotificationIconVisibility(boolean visible, int anim) {
1601        int old = mNotificationIcons.getVisibility();
1602        int v = visible ? View.VISIBLE : View.INVISIBLE;
1603        if (old != v) {
1604            mNotificationIcons.setVisibility(v);
1605            mNotificationIcons.startAnimation(loadAnim(anim, null));
1606        }
1607    }
1608
1609    void updateExpandedInvisiblePosition() {
1610        mTrackingPosition = -mDisplayMetrics.heightPixels;
1611    }
1612
1613    static final float saturate(float a) {
1614        return a < 0f ? 0f : (a > 1f ? 1f : a);
1615    }
1616
1617    @Override
1618    protected int getExpandedViewMaxHeight() {
1619        return mDisplayMetrics.heightPixels - mNotificationPanelMarginBottomPx;
1620    }
1621
1622    @Override
1623    public void updateExpandedViewPos(int thingy) {
1624        // TODO
1625        if (DEBUG) Slog.v(TAG, "updateExpandedViewPos");
1626        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mNotificationPanel.getLayoutParams();
1627        lp.gravity = mNotificationPanelGravity;
1628        lp.leftMargin = mNotificationPanelMarginPx;
1629        mNotificationPanel.setLayoutParams(lp);
1630        lp = (FrameLayout.LayoutParams) mSettingsPanel.getLayoutParams();
1631        lp.gravity = mSettingsPanelGravity;
1632        lp.rightMargin = mNotificationPanelMarginPx;
1633        mSettingsPanel.setLayoutParams(lp);
1634
1635        updateCarrierLabelVisibility(false);
1636    }
1637
1638    // called by makeStatusbar and also by PhoneStatusBarView
1639    void updateDisplaySize() {
1640        mDisplay.getMetrics(mDisplayMetrics);
1641        mGestureRec.tag("display",
1642                String.format("%dx%d", mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
1643    }
1644
1645    void performDisableActions(int net) {
1646        int old = mDisabled;
1647        int diff = net ^ old;
1648        mDisabled = net;
1649
1650        // act accordingly
1651        if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
1652            if ((net & StatusBarManager.DISABLE_EXPAND) != 0) {
1653                Slog.d(TAG, "DISABLE_EXPAND: yes");
1654                animateCollapse();
1655            }
1656        }
1657        if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
1658            if ((net & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
1659                Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
1660                if (mTicking) {
1661                    mNotificationIcons.setVisibility(View.INVISIBLE);
1662                    mTicker.halt();
1663                } else {
1664                    setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
1665                }
1666            } else {
1667                Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
1668                if (!mExpandedVisible) {
1669                    setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
1670                }
1671            }
1672        } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
1673            if (mTicking && (net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
1674                mTicker.halt();
1675            }
1676        }
1677    }
1678
1679    private View.OnClickListener mClearButtonListener = new View.OnClickListener() {
1680        public void onClick(View v) {
1681            synchronized (mNotificationData) {
1682                // animate-swipe all dismissable notifications, then animate the shade closed
1683                int numChildren = mPile.getChildCount();
1684
1685                int scrollTop = mScrollView.getScrollY();
1686                int scrollBottom = scrollTop + mScrollView.getHeight();
1687                final ArrayList<View> snapshot = new ArrayList<View>(numChildren);
1688                for (int i=0; i<numChildren; i++) {
1689                    final View child = mPile.getChildAt(i);
1690                    if (mPile.canChildBeDismissed(child) && child.getBottom() > scrollTop &&
1691                            child.getTop() < scrollBottom) {
1692                        snapshot.add(child);
1693                    }
1694                }
1695                if (snapshot.isEmpty()) {
1696                    animateCollapse(CommandQueue.FLAG_EXCLUDE_NONE);
1697                    return;
1698                }
1699                new Thread(new Runnable() {
1700                    @Override
1701                    public void run() {
1702                        // Decrease the delay for every row we animate to give the sense of
1703                        // accelerating the swipes
1704                        final int ROW_DELAY_DECREMENT = 10;
1705                        int currentDelay = 140;
1706                        int totalDelay = 0;
1707
1708                        // Set the shade-animating state to avoid doing other work during
1709                        // all of these animations. In particular, avoid layout and
1710                        // redrawing when collapsing the shade.
1711                        mPile.setViewRemoval(false);
1712
1713                        mPostCollapseCleanup = new Runnable() {
1714                            @Override
1715                            public void run() {
1716                                if (DEBUG) {
1717                                    Slog.v(TAG, "running post-collapse cleanup");
1718                                }
1719                                try {
1720                                    mPile.setViewRemoval(true);
1721                                    mBarService.onClearAllNotifications();
1722                                } catch (Exception ex) { }
1723                            }
1724                        };
1725
1726                        View sampleView = snapshot.get(0);
1727                        int width = sampleView.getWidth();
1728                        final int velocity = width * 8; // 1000/8 = 125 ms duration
1729                        for (final View _v : snapshot) {
1730                            mHandler.postDelayed(new Runnable() {
1731                                @Override
1732                                public void run() {
1733                                    mPile.dismissRowAnimated(_v, velocity);
1734                                }
1735                            }, totalDelay);
1736                            currentDelay = Math.max(50, currentDelay - ROW_DELAY_DECREMENT);
1737                            totalDelay += currentDelay;
1738                        }
1739                        // Delay the collapse animation until after all swipe animations have
1740                        // finished. Provide some buffer because there may be some extra delay
1741                        // before actually starting each swipe animation. Ideally, we'd
1742                        // synchronize the end of those animations with the start of the collaps
1743                        // exactly.
1744                        mHandler.postDelayed(new Runnable() {
1745                            @Override
1746                            public void run() {
1747                                animateCollapse(CommandQueue.FLAG_EXCLUDE_NONE);
1748                            }
1749                        }, totalDelay + 225);
1750                    }
1751                }).start();
1752            }
1753        }
1754    };
1755
1756    private View.OnClickListener mSettingsButtonListener = new View.OnClickListener() {
1757        public void onClick(View v) {
1758            // We take this as a good indicator that Setup is running and we shouldn't
1759            // allow you to go somewhere else
1760            if (!isDeviceProvisioned()) return;
1761            try {
1762                // Dismiss the lock screen when Settings starts.
1763                ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
1764            } catch (RemoteException e) {
1765            }
1766            v.getContext().startActivityAsUser(new Intent(Settings.ACTION_SETTINGS)
1767                    .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
1768                    new UserHandle(UserHandle.USER_CURRENT));
1769            animateCollapse();
1770        }
1771    };
1772
1773    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1774        public void onReceive(Context context, Intent intent) {
1775            Slog.v(TAG, "onReceive: " + intent);
1776            String action = intent.getAction();
1777            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
1778                int flags = CommandQueue.FLAG_EXCLUDE_NONE;
1779                if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
1780                    String reason = intent.getStringExtra("reason");
1781                    if (reason != null && reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) {
1782                        flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL;
1783                    }
1784                }
1785                animateCollapse(flags);
1786            }
1787            else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
1788                // no waiting!
1789                makeExpandedInvisible();
1790            }
1791            else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
1792                if (DEBUG) {
1793                    Slog.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration());
1794                }
1795                updateResources();
1796                repositionNavigationBar();
1797                updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
1798                updateShowSearchHoldoff();
1799            }
1800            else if (Intent.ACTION_SCREEN_ON.equals(action)) {
1801                // work around problem where mDisplay.getRotation() is not stable while screen is off (bug 7086018)
1802                repositionNavigationBar();
1803            }
1804        }
1805    };
1806
1807    @Override
1808    public void userSwitched(int newUserId) {
1809        if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId);
1810        animateCollapse();
1811        updateNotificationIcons();
1812    }
1813
1814    private void setIntruderAlertVisibility(boolean vis) {
1815        if (!ENABLE_INTRUDERS) return;
1816        if (DEBUG) {
1817            Slog.v(TAG, (vis ? "showing" : "hiding") + " intruder alert window");
1818        }
1819        mIntruderAlertView.setVisibility(vis ? View.VISIBLE : View.GONE);
1820    }
1821
1822    public void dismissIntruder() {
1823        if (mCurrentlyIntrudingNotification == null) return;
1824
1825        try {
1826            mBarService.onNotificationClear(
1827                    mCurrentlyIntrudingNotification.pkg,
1828                    mCurrentlyIntrudingNotification.tag,
1829                    mCurrentlyIntrudingNotification.id);
1830        } catch (android.os.RemoteException ex) {
1831            // oh well
1832        }
1833    }
1834
1835    /**
1836     * Reload some of our resources when the configuration changes.
1837     *
1838     * We don't reload everything when the configuration changes -- we probably
1839     * should, but getting that smooth is tough.  Someday we'll fix that.  In the
1840     * meantime, just update the things that we know change.
1841     */
1842    void updateResources() {
1843        final Context context = mContext;
1844        final Resources res = context.getResources();
1845
1846        if (mClearButton instanceof TextView) {
1847            ((TextView)mClearButton).setText(context.getText(R.string.status_bar_clear_all_button));
1848        }
1849
1850        // Update the QuickSettings container
1851        ((SettingsPanelView) mSettingsPanel).updateResources();
1852
1853        loadDimens();
1854    }
1855
1856    protected void loadDimens() {
1857        final Resources res = mContext.getResources();
1858
1859        mNaturalBarHeight = res.getDimensionPixelSize(
1860                com.android.internal.R.dimen.status_bar_height);
1861
1862        int newIconSize = res.getDimensionPixelSize(
1863            com.android.internal.R.dimen.status_bar_icon_size);
1864        int newIconHPadding = res.getDimensionPixelSize(
1865            R.dimen.status_bar_icon_padding);
1866
1867        if (newIconHPadding != mIconHPadding || newIconSize != mIconSize) {
1868//            Slog.d(TAG, "size=" + newIconSize + " padding=" + newIconHPadding);
1869            mIconHPadding = newIconHPadding;
1870            mIconSize = newIconSize;
1871            //reloadAllNotificationIcons(); // reload the tray
1872        }
1873
1874        mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
1875
1876        mSelfExpandVelocityPx = res.getDimension(R.dimen.self_expand_velocity);
1877        mSelfCollapseVelocityPx = res.getDimension(R.dimen.self_collapse_velocity);
1878        mFlingExpandMinVelocityPx = res.getDimension(R.dimen.fling_expand_min_velocity);
1879        mFlingCollapseMinVelocityPx = res.getDimension(R.dimen.fling_collapse_min_velocity);
1880
1881        mCollapseMinDisplayFraction = res.getFraction(R.dimen.collapse_min_display_fraction, 1, 1);
1882        mExpandMinDisplayFraction = res.getFraction(R.dimen.expand_min_display_fraction, 1, 1);
1883
1884        mExpandAccelPx = res.getDimension(R.dimen.expand_accel);
1885        mCollapseAccelPx = res.getDimension(R.dimen.collapse_accel);
1886
1887        mFlingGestureMaxXVelocityPx = res.getDimension(R.dimen.fling_gesture_max_x_velocity);
1888
1889        mFlingGestureMaxOutputVelocityPx = res.getDimension(R.dimen.fling_gesture_max_output_velocity);
1890
1891        mNotificationPanelMarginBottomPx
1892            = (int) res.getDimension(R.dimen.notification_panel_margin_bottom);
1893        mNotificationPanelMarginPx
1894            = (int) res.getDimension(R.dimen.notification_panel_margin_left);
1895        mNotificationPanelGravity = res.getInteger(R.integer.notification_panel_layout_gravity);
1896        if (mNotificationPanelGravity <= 0) {
1897            mNotificationPanelGravity = Gravity.LEFT | Gravity.TOP;
1898        }
1899        mSettingsPanelGravity = res.getInteger(R.integer.settings_panel_layout_gravity);
1900        if (mSettingsPanelGravity <= 0) {
1901            mSettingsPanelGravity = Gravity.RIGHT | Gravity.TOP;
1902        }
1903        getNinePatchPadding(res.getDrawable(R.drawable.notification_panel_bg), mNotificationPanelBackgroundPadding);
1904        final int notificationPanelDecorationHeight =
1905              res.getDimensionPixelSize(R.dimen.notification_panel_padding_top)
1906            + res.getDimensionPixelSize(R.dimen.notification_panel_header_height)
1907            + mNotificationPanelBackgroundPadding.top
1908            + mNotificationPanelBackgroundPadding.bottom;
1909        mNotificationPanelMinHeight =
1910              notificationPanelDecorationHeight
1911            + res.getDimensionPixelSize(R.dimen.close_handle_underlap);
1912
1913        mCarrierLabelHeight = res.getDimensionPixelSize(R.dimen.carrier_label_height);
1914        mNotificationHeaderHeight = res.getDimensionPixelSize(R.dimen.notification_panel_header_height);
1915
1916        if (false) Slog.v(TAG, "updateResources");
1917    }
1918
1919    private static void getNinePatchPadding(Drawable d, Rect outPadding) {
1920        if (d instanceof NinePatchDrawable) {
1921            NinePatchDrawable ninePatch = (NinePatchDrawable) d;
1922            ninePatch.getPadding(outPadding);
1923        }
1924    }
1925
1926    //
1927    // tracing
1928    //
1929
1930    void postStartTracing() {
1931        mHandler.postDelayed(mStartTracing, 3000);
1932    }
1933
1934    void vibrate() {
1935        android.os.Vibrator vib = (android.os.Vibrator)mContext.getSystemService(
1936                Context.VIBRATOR_SERVICE);
1937        vib.vibrate(250);
1938    }
1939
1940    Runnable mStartTracing = new Runnable() {
1941        public void run() {
1942            vibrate();
1943            SystemClock.sleep(250);
1944            Slog.d(TAG, "startTracing");
1945            android.os.Debug.startMethodTracing("/data/statusbar-traces/trace");
1946            mHandler.postDelayed(mStopTracing, 10000);
1947        }
1948    };
1949
1950    Runnable mStopTracing = new Runnable() {
1951        public void run() {
1952            android.os.Debug.stopMethodTracing();
1953            Slog.d(TAG, "stopTracing");
1954            vibrate();
1955        }
1956    };
1957
1958    @Override
1959    protected void haltTicker() {
1960        mTicker.halt();
1961    }
1962
1963    @Override
1964    protected boolean shouldDisableNavbarGestures() {
1965        return mExpandedVisible || (mDisabled & StatusBarManager.DISABLE_HOME) != 0;
1966    }
1967
1968    private static class FastColorDrawable extends Drawable {
1969        private final int mColor;
1970
1971        public FastColorDrawable(int color) {
1972            mColor = 0xff000000 | color;
1973        }
1974
1975        @Override
1976        public void draw(Canvas canvas) {
1977            canvas.drawColor(mColor, PorterDuff.Mode.SRC);
1978        }
1979
1980        @Override
1981        public void setAlpha(int alpha) {
1982        }
1983
1984        @Override
1985        public void setColorFilter(ColorFilter cf) {
1986        }
1987
1988        @Override
1989        public int getOpacity() {
1990            return PixelFormat.OPAQUE;
1991        }
1992
1993        @Override
1994        public void setBounds(int left, int top, int right, int bottom) {
1995        }
1996
1997        @Override
1998        public void setBounds(Rect bounds) {
1999        }
2000    }
2001}
2002