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