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