TabletStatusBar.java revision 3623128d13839b48693245a64448fb2dabc1977f
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.tablet;
18
19import android.animation.LayoutTransition;
20import android.animation.ObjectAnimator;
21import android.app.ActivityManager;
22import android.app.ActivityManagerNative;
23import android.app.Notification;
24import android.app.PendingIntent;
25import android.app.StatusBarManager;
26import android.content.BroadcastReceiver;
27import android.content.Context;
28import android.content.Intent;
29import android.content.IntentFilter;
30import android.content.SharedPreferences;
31import android.content.res.Configuration;
32import android.content.res.Resources;
33import android.graphics.PixelFormat;
34import android.graphics.Point;
35import android.graphics.drawable.Drawable;
36import android.graphics.drawable.LayerDrawable;
37import android.inputmethodservice.InputMethodService;
38import android.os.IBinder;
39import android.os.Message;
40import android.os.RemoteException;
41import android.os.ServiceManager;
42import android.text.TextUtils;
43import android.util.Slog;
44import android.view.Display;
45import android.view.Gravity;
46import android.view.IWindowManager;
47import android.view.KeyEvent;
48import android.view.MotionEvent;
49import android.view.SoundEffectConstants;
50import android.view.VelocityTracker;
51import android.view.View;
52import android.view.ViewConfiguration;
53import android.view.ViewGroup;
54import android.view.ViewGroup.LayoutParams;
55import android.view.WindowManager;
56import android.view.WindowManagerImpl;
57import android.view.accessibility.AccessibilityEvent;
58import android.widget.ImageView;
59import android.widget.LinearLayout;
60import android.widget.RemoteViews;
61import android.widget.ScrollView;
62import android.widget.TextView;
63
64import com.android.internal.statusbar.StatusBarIcon;
65import com.android.internal.statusbar.StatusBarNotification;
66import com.android.systemui.R;
67import com.android.systemui.recent.RecentTasksLoader;
68import com.android.systemui.recent.RecentsPanelView;
69import com.android.systemui.statusbar.BaseStatusBar;
70import com.android.systemui.statusbar.CommandQueue;
71import com.android.systemui.statusbar.DoNotDisturb;
72import com.android.systemui.statusbar.NotificationData;
73import com.android.systemui.statusbar.SignalClusterView;
74import com.android.systemui.statusbar.StatusBarIconView;
75import com.android.systemui.statusbar.NotificationData.Entry;
76import com.android.systemui.statusbar.policy.BatteryController;
77import com.android.systemui.statusbar.policy.BluetoothController;
78import com.android.systemui.statusbar.policy.CompatModeButton;
79import com.android.systemui.statusbar.policy.LocationController;
80import com.android.systemui.statusbar.policy.NetworkController;
81import com.android.systemui.statusbar.policy.NotificationRowLayout;
82import com.android.systemui.statusbar.policy.Prefs;
83
84import java.io.FileDescriptor;
85import java.io.PrintWriter;
86import java.util.ArrayList;
87
88public class TabletStatusBar extends BaseStatusBar implements
89        InputMethodsPanel.OnHardKeyboardEnabledChangeListener,
90        RecentsPanelView.OnRecentsPanelVisibilityChangedListener {
91    public static final boolean DEBUG = false;
92    public static final boolean DEBUG_COMPAT_HELP = false;
93    public static final String TAG = "TabletStatusBar";
94
95
96    public static final int MSG_OPEN_NOTIFICATION_PANEL = 1000;
97    public static final int MSG_CLOSE_NOTIFICATION_PANEL = 1001;
98    public static final int MSG_OPEN_NOTIFICATION_PEEK = 1002;
99    public static final int MSG_CLOSE_NOTIFICATION_PEEK = 1003;
100    // 1020-1029 reserved for BaseStatusBar
101    public static final int MSG_SHOW_CHROME = 1030;
102    public static final int MSG_HIDE_CHROME = 1031;
103    public static final int MSG_OPEN_INPUT_METHODS_PANEL = 1040;
104    public static final int MSG_CLOSE_INPUT_METHODS_PANEL = 1041;
105    public static final int MSG_OPEN_COMPAT_MODE_PANEL = 1050;
106    public static final int MSG_CLOSE_COMPAT_MODE_PANEL = 1051;
107    public static final int MSG_STOP_TICKER = 2000;
108
109    // Fitts' Law assistance for LatinIME; see policy.EventHole
110    private static final boolean FAKE_SPACE_BAR = true;
111
112    // Notification "peeking" (flyover preview of individual notifications)
113    final static int NOTIFICATION_PEEK_HOLD_THRESH = 200; // ms
114    final static int NOTIFICATION_PEEK_FADE_DELAY = 3000; // ms
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    // The height of the bar, as definied by the build.  It may be taller if we're plugged
120    // into hdmi.
121    int mNaturalBarHeight = -1;
122    int mIconSize = -1;
123    int mIconHPadding = -1;
124    int mNavIconWidth = -1;
125    int mMenuNavIconWidth = -1;
126    private int mMaxNotificationIcons = 5;
127
128    IWindowManager mWindowManager;
129
130    TabletStatusBarView mStatusBarView;
131    View mNotificationArea;
132    View mNotificationTrigger;
133    NotificationIconArea mNotificationIconArea;
134    ViewGroup mNavigationArea;
135
136    boolean mNotificationDNDMode;
137    NotificationData.Entry mNotificationDNDDummyEntry;
138
139    ImageView mBackButton;
140    View mHomeButton;
141    View mMenuButton;
142    View mRecentButton;
143    private boolean mAltBackButtonEnabledForIme;
144
145    ViewGroup mFeedbackIconArea; // notification icons, IME icon, compat icon
146    InputMethodButton mInputMethodSwitchButton;
147    CompatModeButton mCompatModeButton;
148
149    NotificationPanel mNotificationPanel;
150    WindowManager.LayoutParams mNotificationPanelParams;
151    NotificationPeekPanel mNotificationPeekWindow;
152    ViewGroup mNotificationPeekRow;
153    int mNotificationPeekIndex;
154    IBinder mNotificationPeekKey;
155    LayoutTransition mNotificationPeekScrubLeft, mNotificationPeekScrubRight;
156
157    int mNotificationPeekTapDuration;
158    int mNotificationFlingVelocity;
159
160    BatteryController mBatteryController;
161    BluetoothController mBluetoothController;
162    LocationController mLocationController;
163    NetworkController mNetworkController;
164    DoNotDisturb mDoNotDisturb;
165
166    ViewGroup mBarContents;
167
168    // hide system chrome ("lights out") support
169    View mShadow;
170
171    NotificationIconArea.IconLayout mIconLayout;
172
173    TabletTicker mTicker;
174
175    View mFakeSpaceBar;
176    KeyEvent mSpaceBarKeyEvent = null;
177
178    View mCompatibilityHelpDialog = null;
179
180    // for disabling the status bar
181    int mDisabled = 0;
182
183    private InputMethodsPanel mInputMethodsPanel;
184    private CompatModePanel mCompatModePanel;
185
186    private int mSystemUiVisibility = 0;
187
188    private int mNavigationIconHints = 0;
189
190    private int mShowSearchHoldoff = 0;
191
192    public Context getContext() { return mContext; }
193
194    private Runnable mShowSearchPanel = new Runnable() {
195        public void run() {
196            showSearchPanel();
197        }
198    };
199
200    private View.OnTouchListener mHomeSearchActionListener = new View.OnTouchListener() {
201        public boolean onTouch(View v, MotionEvent event) {
202            switch(event.getAction()) {
203                case MotionEvent.ACTION_DOWN:
204                    if (!shouldDisableNavbarGestures()) {
205                        mHandler.removeCallbacks(mShowSearchPanel);
206                        mHandler.postDelayed(mShowSearchPanel, mShowSearchHoldoff);
207                    }
208                break;
209
210                case MotionEvent.ACTION_UP:
211                case MotionEvent.ACTION_CANCEL:
212                    mHandler.removeCallbacks(mShowSearchPanel);
213                break;
214            }
215            return false;
216        }
217    };
218
219    @Override
220    protected void createAndAddWindows() {
221        addStatusBarWindow();
222        addPanelWindows();
223    }
224
225    private void addStatusBarWindow() {
226        final View sb = makeStatusBarView();
227
228        final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
229                ViewGroup.LayoutParams.MATCH_PARENT,
230                ViewGroup.LayoutParams.MATCH_PARENT,
231                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
232                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
233                    | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
234                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
235                // We use a pixel format of RGB565 for the status bar to save memory bandwidth and
236                // to ensure that the layer can be handled by HWComposer.  On some devices the
237                // HWComposer is unable to handle SW-rendered RGBX_8888 layers.
238                PixelFormat.RGB_565);
239
240        // We explicitly leave FLAG_HARDWARE_ACCELERATED out of the flags.  The status bar occupies
241        // very little screen real-estate and is updated fairly frequently.  By using CPU rendering
242        // for the status bar, we prevent the GPU from having to wake up just to do these small
243        // updates, which should help keep power consumption down.
244
245        lp.gravity = getStatusBarGravity();
246        lp.setTitle("SystemBar");
247        lp.packageName = mContext.getPackageName();
248        WindowManagerImpl.getDefault().addView(sb, lp);
249    }
250
251    protected void addPanelWindows() {
252        final Context context = mContext;
253        final Resources res = mContext.getResources();
254
255        // Notification Panel
256        mNotificationPanel = (NotificationPanel)View.inflate(context,
257                R.layout.system_bar_notification_panel, null);
258        mNotificationPanel.setBar(this);
259        mNotificationPanel.show(false, false);
260        mNotificationPanel.setOnTouchListener(
261                new TouchOutsideListener(MSG_CLOSE_NOTIFICATION_PANEL, mNotificationPanel));
262
263        // the battery icon
264        mBatteryController.addIconView((ImageView)mNotificationPanel.findViewById(R.id.battery));
265        mBatteryController.addLabelView(
266                (TextView)mNotificationPanel.findViewById(R.id.battery_text));
267
268        // Bt
269        mBluetoothController.addIconView(
270                (ImageView)mNotificationPanel.findViewById(R.id.bluetooth));
271
272        // network icons: either a combo icon that switches between mobile and data, or distinct
273        // mobile and data icons
274        final ImageView mobileRSSI =
275                (ImageView)mNotificationPanel.findViewById(R.id.mobile_signal);
276        if (mobileRSSI != null) {
277            mNetworkController.addPhoneSignalIconView(mobileRSSI);
278        }
279        final ImageView wifiRSSI =
280                (ImageView)mNotificationPanel.findViewById(R.id.wifi_signal);
281        if (wifiRSSI != null) {
282            mNetworkController.addWifiIconView(wifiRSSI);
283        }
284        mNetworkController.addWifiLabelView(
285                (TextView)mNotificationPanel.findViewById(R.id.wifi_text));
286
287        mNetworkController.addDataTypeIconView(
288                (ImageView)mNotificationPanel.findViewById(R.id.mobile_type));
289        mNetworkController.addMobileLabelView(
290                (TextView)mNotificationPanel.findViewById(R.id.mobile_text));
291        mNetworkController.addCombinedLabelView(
292                (TextView)mBarContents.findViewById(R.id.network_text));
293
294        mStatusBarView.setIgnoreChildren(0, mNotificationTrigger, mNotificationPanel);
295
296        WindowManager.LayoutParams lp = mNotificationPanelParams = new WindowManager.LayoutParams(
297                res.getDimensionPixelSize(R.dimen.notification_panel_width),
298                getNotificationPanelHeight(),
299                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
300                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
301                    | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
302                    | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
303                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
304                    | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
305                PixelFormat.TRANSLUCENT);
306        lp.gravity = Gravity.BOTTOM | Gravity.RIGHT;
307        lp.setTitle("NotificationPanel");
308        lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
309                | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
310        lp.windowAnimations = com.android.internal.R.style.Animation; // == no animation
311//        lp.windowAnimations = com.android.internal.R.style.Animation_ZoomButtons; // simple fade
312
313        WindowManagerImpl.getDefault().addView(mNotificationPanel, lp);
314
315        // Recents Panel
316        mRecentTasksLoader = new RecentTasksLoader(context);
317        updateRecentsPanel();
318
319        // Search Panel
320        mStatusBarView.setBar(this);
321        mHomeButton.setOnTouchListener(mHomeSearchActionListener);
322        updateSearchPanel();
323
324        // Input methods Panel
325        mInputMethodsPanel = (InputMethodsPanel) View.inflate(context,
326                R.layout.system_bar_input_methods_panel, null);
327        mInputMethodsPanel.setHardKeyboardEnabledChangeListener(this);
328        mInputMethodsPanel.setOnTouchListener(new TouchOutsideListener(
329                MSG_CLOSE_INPUT_METHODS_PANEL, mInputMethodsPanel));
330        mInputMethodsPanel.setImeSwitchButton(mInputMethodSwitchButton);
331        mStatusBarView.setIgnoreChildren(2, mInputMethodSwitchButton, mInputMethodsPanel);
332        lp = new WindowManager.LayoutParams(
333                ViewGroup.LayoutParams.WRAP_CONTENT,
334                ViewGroup.LayoutParams.WRAP_CONTENT,
335                WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
336                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
337                    | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
338                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
339                    | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
340                PixelFormat.TRANSLUCENT);
341        lp.gravity = Gravity.BOTTOM | Gravity.RIGHT;
342        lp.setTitle("InputMethodsPanel");
343        lp.windowAnimations = R.style.Animation_RecentPanel;
344
345        WindowManagerImpl.getDefault().addView(mInputMethodsPanel, lp);
346
347        // Compatibility mode selector panel
348        mCompatModePanel = (CompatModePanel) View.inflate(context,
349                R.layout.system_bar_compat_mode_panel, null);
350        mCompatModePanel.setOnTouchListener(new TouchOutsideListener(
351                MSG_CLOSE_COMPAT_MODE_PANEL, mCompatModePanel));
352        mCompatModePanel.setTrigger(mCompatModeButton);
353        mCompatModePanel.setVisibility(View.GONE);
354        mStatusBarView.setIgnoreChildren(3, mCompatModeButton, mCompatModePanel);
355        lp = new WindowManager.LayoutParams(
356                250,
357                ViewGroup.LayoutParams.WRAP_CONTENT,
358                WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
359                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
360                    | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
361                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
362                    | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
363                PixelFormat.TRANSLUCENT);
364        lp.gravity = Gravity.BOTTOM | Gravity.RIGHT;
365        lp.setTitle("CompatModePanel");
366        lp.windowAnimations = android.R.style.Animation_Dialog;
367
368        WindowManagerImpl.getDefault().addView(mCompatModePanel, lp);
369
370        mRecentButton.setOnTouchListener(mRecentsPanel);
371
372        mPile = (NotificationRowLayout)mNotificationPanel.findViewById(R.id.content);
373        mPile.removeAllViews();
374        mPile.setLongPressListener(getNotificationLongClicker());
375
376        ScrollView scroller = (ScrollView)mPile.getParent();
377        scroller.setFillViewport(true);
378    }
379
380    @Override
381    protected int getExpandedViewMaxHeight() {
382        return getNotificationPanelHeight();
383    }
384
385    private int getNotificationPanelHeight() {
386        final Resources res = mContext.getResources();
387        final Display d = WindowManagerImpl.getDefault().getDefaultDisplay();
388        final Point size = new Point();
389        d.getRealSize(size);
390        return Math.max(res.getDimensionPixelSize(R.dimen.notification_panel_min_height), size.y);
391    }
392
393    @Override
394    public void start() {
395        super.start(); // will add the main bar view
396    }
397
398    @Override
399    protected void onConfigurationChanged(Configuration newConfig) {
400        loadDimens();
401        mNotificationPanelParams.height = getNotificationPanelHeight();
402        WindowManagerImpl.getDefault().updateViewLayout(mNotificationPanel,
403                mNotificationPanelParams);
404        mRecentsPanel.updateValuesFromResources();
405        mShowSearchHoldoff = mContext.getResources().getInteger(
406                R.integer.config_show_search_delay);
407        updateSearchPanel();
408    }
409
410    protected void loadDimens() {
411        final Resources res = mContext.getResources();
412
413        mNaturalBarHeight = res.getDimensionPixelSize(
414                com.android.internal.R.dimen.navigation_bar_height);
415
416        int newIconSize = res.getDimensionPixelSize(
417            com.android.internal.R.dimen.system_bar_icon_size);
418        int newIconHPadding = res.getDimensionPixelSize(
419            R.dimen.status_bar_icon_padding);
420        int newNavIconWidth = res.getDimensionPixelSize(R.dimen.navigation_key_width);
421        int newMenuNavIconWidth = res.getDimensionPixelSize(R.dimen.navigation_menu_key_width);
422
423        if (mNavigationArea != null && newNavIconWidth != mNavIconWidth) {
424            mNavIconWidth = newNavIconWidth;
425
426            LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
427                     mNavIconWidth, ViewGroup.LayoutParams.MATCH_PARENT);
428            mBackButton.setLayoutParams(lp);
429            mHomeButton.setLayoutParams(lp);
430            mRecentButton.setLayoutParams(lp);
431        }
432
433        if (mNavigationArea != null && newMenuNavIconWidth != mMenuNavIconWidth) {
434            mMenuNavIconWidth = newMenuNavIconWidth;
435
436            LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
437                     mMenuNavIconWidth, ViewGroup.LayoutParams.MATCH_PARENT);
438            mMenuButton.setLayoutParams(lp);
439        }
440
441        if (newIconHPadding != mIconHPadding || newIconSize != mIconSize) {
442//            Slog.d(TAG, "size=" + newIconSize + " padding=" + newIconHPadding);
443            mIconHPadding = newIconHPadding;
444            mIconSize = newIconSize;
445            reloadAllNotificationIcons(); // reload the tray
446        }
447
448        final int numIcons = res.getInteger(R.integer.config_maxNotificationIcons);
449        if (numIcons != mMaxNotificationIcons) {
450            mMaxNotificationIcons = numIcons;
451            if (DEBUG) Slog.d(TAG, "max notification icons: " + mMaxNotificationIcons);
452            reloadAllNotificationIcons();
453        }
454    }
455
456    public View getStatusBarView() {
457        return mStatusBarView;
458    }
459
460    protected View makeStatusBarView() {
461        final Context context = mContext;
462
463        mWindowManager = IWindowManager.Stub.asInterface(
464                ServiceManager.getService(Context.WINDOW_SERVICE));
465
466        loadDimens();
467
468        final TabletStatusBarView sb = (TabletStatusBarView)View.inflate(
469                context, R.layout.system_bar, null);
470        mStatusBarView = sb;
471
472        sb.setHandler(mHandler);
473
474        try {
475            // Sanity-check that someone hasn't set up the config wrong and asked for a navigation
476            // bar on a tablet that has only the system bar
477            if (mWindowManager.hasNavigationBar()) {
478                Slog.e(TAG, "Tablet device cannot show navigation bar and system bar");
479            }
480        } catch (RemoteException ex) {
481        }
482
483        mBarContents = (ViewGroup) sb.findViewById(R.id.bar_contents);
484
485        // the whole right-hand side of the bar
486        mNotificationArea = sb.findViewById(R.id.notificationArea);
487        mNotificationArea.setOnTouchListener(new NotificationTriggerTouchListener());
488
489        // the button to open the notification area
490        mNotificationTrigger = sb.findViewById(R.id.notificationTrigger);
491
492        // the more notifications icon
493        mNotificationIconArea = (NotificationIconArea)sb.findViewById(R.id.notificationIcons);
494
495        // where the icons go
496        mIconLayout = (NotificationIconArea.IconLayout) sb.findViewById(R.id.icons);
497
498        ViewConfiguration vc = ViewConfiguration.get(context);
499        mNotificationPeekTapDuration = vc.getTapTimeout();
500        mNotificationFlingVelocity = 300; // px/s
501
502        mTicker = new TabletTicker(this);
503
504        // The icons
505        mLocationController = new LocationController(mContext); // will post a notification
506
507        // watch the PREF_DO_NOT_DISTURB and convert to appropriate disable() calls
508        mDoNotDisturb = new DoNotDisturb(mContext);
509
510        mBatteryController = new BatteryController(mContext);
511        mBatteryController.addIconView((ImageView)sb.findViewById(R.id.battery));
512        mBluetoothController = new BluetoothController(mContext);
513        mBluetoothController.addIconView((ImageView)sb.findViewById(R.id.bluetooth));
514
515        mNetworkController = new NetworkController(mContext);
516        final SignalClusterView signalCluster =
517                (SignalClusterView)sb.findViewById(R.id.signal_cluster);
518        mNetworkController.addSignalCluster(signalCluster);
519
520        // The navigation buttons
521        mBackButton = (ImageView)sb.findViewById(R.id.back);
522        mNavigationArea = (ViewGroup) sb.findViewById(R.id.navigationArea);
523        mHomeButton = mNavigationArea.findViewById(R.id.home);
524        mMenuButton = mNavigationArea.findViewById(R.id.menu);
525        mRecentButton = mNavigationArea.findViewById(R.id.recent_apps);
526        mRecentButton.setOnClickListener(mOnClickListener);
527
528        LayoutTransition lt = new LayoutTransition();
529        lt.setDuration(250);
530        // don't wait for these transitions; we just want icons to fade in/out, not move around
531        lt.setDuration(LayoutTransition.CHANGE_APPEARING, 0);
532        lt.setDuration(LayoutTransition.CHANGE_DISAPPEARING, 0);
533        lt.addTransitionListener(new LayoutTransition.TransitionListener() {
534            public void endTransition(LayoutTransition transition, ViewGroup container,
535                    View view, int transitionType) {
536                // ensure the menu button doesn't stick around on the status bar after it's been
537                // removed
538                mBarContents.invalidate();
539            }
540            public void startTransition(LayoutTransition transition, ViewGroup container,
541                    View view, int transitionType) {}
542        });
543        mNavigationArea.setLayoutTransition(lt);
544        // no multi-touch on the nav buttons
545        mNavigationArea.setMotionEventSplittingEnabled(false);
546
547        // The bar contents buttons
548        mFeedbackIconArea = (ViewGroup)sb.findViewById(R.id.feedbackIconArea);
549        mInputMethodSwitchButton = (InputMethodButton) sb.findViewById(R.id.imeSwitchButton);
550        // Overwrite the lister
551        mInputMethodSwitchButton.setOnClickListener(mOnClickListener);
552
553        mCompatModeButton = (CompatModeButton) sb.findViewById(R.id.compatModeButton);
554        mCompatModeButton.setOnClickListener(mOnClickListener);
555        mCompatModeButton.setVisibility(View.GONE);
556
557        // for redirecting errant bar taps to the IME
558        mFakeSpaceBar = sb.findViewById(R.id.fake_space_bar);
559
560        // "shadows" of the status bar features, for lights-out mode
561        mShadow = sb.findViewById(R.id.bar_shadow);
562        mShadow.setOnTouchListener(
563            new View.OnTouchListener() {
564                public boolean onTouch(View v, MotionEvent ev) {
565                    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
566                        // even though setting the systemUI visibility below will turn these views
567                        // on, we need them to come up faster so that they can catch this motion
568                        // event
569                        mShadow.setVisibility(View.GONE);
570                        mBarContents.setVisibility(View.VISIBLE);
571
572                        try {
573                            mBarService.setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE);
574                        } catch (RemoteException ex) {
575                            // system process dead
576                        }
577                    }
578                    return false;
579                }
580            });
581
582        // tuning parameters
583        final int LIGHTS_GOING_OUT_SYSBAR_DURATION = 750;
584        final int LIGHTS_GOING_OUT_SHADOW_DURATION = 750;
585        final int LIGHTS_GOING_OUT_SHADOW_DELAY    = 0;
586
587        final int LIGHTS_COMING_UP_SYSBAR_DURATION = 200;
588//        final int LIGHTS_COMING_UP_SYSBAR_DELAY    = 50;
589        final int LIGHTS_COMING_UP_SHADOW_DURATION = 0;
590
591        LayoutTransition xition = new LayoutTransition();
592        xition.setAnimator(LayoutTransition.APPEARING,
593               ObjectAnimator.ofFloat(null, "alpha", 0.5f, 1f));
594        xition.setDuration(LayoutTransition.APPEARING, LIGHTS_COMING_UP_SYSBAR_DURATION);
595        xition.setStartDelay(LayoutTransition.APPEARING, 0);
596        xition.setAnimator(LayoutTransition.DISAPPEARING,
597               ObjectAnimator.ofFloat(null, "alpha", 1f, 0f));
598        xition.setDuration(LayoutTransition.DISAPPEARING, LIGHTS_GOING_OUT_SYSBAR_DURATION);
599        xition.setStartDelay(LayoutTransition.DISAPPEARING, 0);
600        ((ViewGroup)sb.findViewById(R.id.bar_contents_holder)).setLayoutTransition(xition);
601
602        xition = new LayoutTransition();
603        xition.setAnimator(LayoutTransition.APPEARING,
604               ObjectAnimator.ofFloat(null, "alpha", 0f, 1f));
605        xition.setDuration(LayoutTransition.APPEARING, LIGHTS_GOING_OUT_SHADOW_DURATION);
606        xition.setStartDelay(LayoutTransition.APPEARING, LIGHTS_GOING_OUT_SHADOW_DELAY);
607        xition.setAnimator(LayoutTransition.DISAPPEARING,
608               ObjectAnimator.ofFloat(null, "alpha", 1f, 0f));
609        xition.setDuration(LayoutTransition.DISAPPEARING, LIGHTS_COMING_UP_SHADOW_DURATION);
610        xition.setStartDelay(LayoutTransition.DISAPPEARING, 0);
611        ((ViewGroup)sb.findViewById(R.id.bar_shadow_holder)).setLayoutTransition(xition);
612
613        // set the initial view visibility
614        setAreThereNotifications();
615
616        // receive broadcasts
617        IntentFilter filter = new IntentFilter();
618        filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
619        filter.addAction(Intent.ACTION_SCREEN_OFF);
620        context.registerReceiver(mBroadcastReceiver, filter);
621
622        return sb;
623    }
624
625    @Override
626    protected WindowManager.LayoutParams getRecentsLayoutParams(LayoutParams layoutParams) {
627        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
628                (int) mContext.getResources().getDimension(R.dimen.status_bar_recents_width),
629                ViewGroup.LayoutParams.MATCH_PARENT,
630                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
631                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
632                | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
633                | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
634                | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
635                PixelFormat.TRANSLUCENT);
636        lp.gravity = Gravity.BOTTOM | Gravity.LEFT;
637        lp.setTitle("RecentsPanel");
638        lp.windowAnimations = com.android.internal.R.style.Animation_RecentApplications;
639        lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
640            | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
641
642        return lp;
643    }
644
645    @Override
646    protected WindowManager.LayoutParams getSearchLayoutParams(LayoutParams layoutParams) {
647        boolean opaque = false;
648        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
649                LayoutParams.MATCH_PARENT,
650                LayoutParams.MATCH_PARENT,
651                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
652                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
653                        | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
654                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
655                (opaque ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT));
656        if (ActivityManager.isHighEndGfx(mDisplay)) {
657            lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
658        } else {
659            lp.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;
660            lp.dimAmount = 0.7f;
661        }
662        lp.gravity = Gravity.BOTTOM | Gravity.LEFT;
663        lp.setTitle("SearchPanel");
664        // TODO: Define custom animation for Search panel
665        lp.windowAnimations = com.android.internal.R.style.Animation_RecentApplications;
666        lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
667                | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
668        return lp;
669    }
670
671    protected void updateRecentsPanel() {
672        super.updateRecentsPanel(R.layout.system_bar_recent_panel);
673        mRecentsPanel.setStatusBarView(mStatusBarView);
674    }
675
676    @Override
677    protected void updateSearchPanel() {
678        super.updateSearchPanel();
679        mSearchPanelView.setStatusBarView(mStatusBarView);
680        mStatusBarView.setDelegateView(mSearchPanelView);
681    }
682
683    @Override
684    public void showSearchPanel() {
685        super.showSearchPanel();
686        WindowManager.LayoutParams lp =
687            (android.view.WindowManager.LayoutParams) mStatusBarView.getLayoutParams();
688        lp.flags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
689        WindowManagerImpl.getDefault().updateViewLayout(mStatusBarView, lp);
690    }
691
692    @Override
693    public void hideSearchPanel() {
694        super.hideSearchPanel();
695        WindowManager.LayoutParams lp =
696            (android.view.WindowManager.LayoutParams) mStatusBarView.getLayoutParams();
697        lp.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
698        WindowManagerImpl.getDefault().updateViewLayout(mStatusBarView, lp);
699    }
700
701    public int getStatusBarHeight() {
702        return mStatusBarView != null ? mStatusBarView.getHeight()
703                : mContext.getResources().getDimensionPixelSize(
704                        com.android.internal.R.dimen.navigation_bar_height);
705    }
706
707    protected int getStatusBarGravity() {
708        return Gravity.BOTTOM | Gravity.FILL_HORIZONTAL;
709    }
710
711    public void onBarHeightChanged(int height) {
712        final WindowManager.LayoutParams lp
713                = (WindowManager.LayoutParams)mStatusBarView.getLayoutParams();
714        if (lp == null) {
715            // haven't been added yet
716            return;
717        }
718        if (lp.height != height) {
719            lp.height = height;
720            final WindowManager wm = WindowManagerImpl.getDefault();
721            wm.updateViewLayout(mStatusBarView, lp);
722        }
723    }
724
725    @Override
726    protected BaseStatusBar.H createHandler() {
727        return new TabletStatusBar.H();
728    }
729
730    private class H extends BaseStatusBar.H {
731        public void handleMessage(Message m) {
732            super.handleMessage(m);
733            switch (m.what) {
734                case MSG_OPEN_NOTIFICATION_PEEK:
735                    if (DEBUG) Slog.d(TAG, "opening notification peek window; arg=" + m.arg1);
736
737                    if (m.arg1 >= 0) {
738                        final int N = mNotificationData.size();
739
740                        if (!mNotificationDNDMode) {
741                            if (mNotificationPeekIndex >= 0 && mNotificationPeekIndex < N) {
742                                NotificationData.Entry entry = mNotificationData.get(N-1-mNotificationPeekIndex);
743                                entry.icon.setBackgroundColor(0);
744                                mNotificationPeekIndex = -1;
745                                mNotificationPeekKey = null;
746                            }
747                        }
748
749                        final int peekIndex = m.arg1;
750                        if (peekIndex < N) {
751                            //Slog.d(TAG, "loading peek: " + peekIndex);
752                            NotificationData.Entry entry =
753                                mNotificationDNDMode
754                                    ? mNotificationDNDDummyEntry
755                                    : mNotificationData.get(N-1-peekIndex);
756                            NotificationData.Entry copy = new NotificationData.Entry(
757                                    entry.key,
758                                    entry.notification,
759                                    entry.icon);
760                            inflateViews(copy, mNotificationPeekRow);
761
762                            if (mNotificationDNDMode) {
763                                copy.content.setOnClickListener(new View.OnClickListener() {
764                                    public void onClick(View v) {
765                                        SharedPreferences.Editor editor = Prefs.edit(mContext);
766                                        editor.putBoolean(Prefs.DO_NOT_DISTURB_PREF, false);
767                                        editor.apply();
768                                        animateCollapse();
769                                        visibilityChanged(false);
770                                    }
771                                });
772                            }
773
774                            entry.icon.setBackgroundColor(0x20FFFFFF);
775
776//                          mNotificationPeekRow.setLayoutTransition(
777//                              peekIndex < mNotificationPeekIndex
778//                                  ? mNotificationPeekScrubLeft
779//                                  : mNotificationPeekScrubRight);
780
781                            mNotificationPeekRow.removeAllViews();
782                            mNotificationPeekRow.addView(copy.row);
783
784                            mNotificationPeekWindow.setVisibility(View.VISIBLE);
785                            mNotificationPanel.show(false, true);
786
787                            mNotificationPeekIndex = peekIndex;
788                            mNotificationPeekKey = entry.key;
789                        }
790                    }
791                    break;
792                case MSG_CLOSE_NOTIFICATION_PEEK:
793                    if (DEBUG) Slog.d(TAG, "closing notification peek window");
794                    mNotificationPeekWindow.setVisibility(View.GONE);
795                    mNotificationPeekRow.removeAllViews();
796
797                    final int N = mNotificationData.size();
798                    if (mNotificationPeekIndex >= 0 && mNotificationPeekIndex < N) {
799                        NotificationData.Entry entry =
800                            mNotificationDNDMode
801                                ? mNotificationDNDDummyEntry
802                                : mNotificationData.get(N-1-mNotificationPeekIndex);
803                        entry.icon.setBackgroundColor(0);
804                    }
805
806                    mNotificationPeekIndex = -1;
807                    mNotificationPeekKey = null;
808                    break;
809                case MSG_OPEN_NOTIFICATION_PANEL:
810                    if (DEBUG) Slog.d(TAG, "opening notifications panel");
811                    if (!mNotificationPanel.isShowing()) {
812                        mNotificationPanel.show(true, true);
813                        mNotificationArea.setVisibility(View.INVISIBLE);
814                        mTicker.halt();
815                    }
816                    break;
817                case MSG_CLOSE_NOTIFICATION_PANEL:
818                    if (DEBUG) Slog.d(TAG, "closing notifications panel");
819                    if (mNotificationPanel.isShowing()) {
820                        mNotificationPanel.show(false, true);
821                        mNotificationArea.setVisibility(View.VISIBLE);
822                    }
823                    break;
824                case MSG_OPEN_INPUT_METHODS_PANEL:
825                    if (DEBUG) Slog.d(TAG, "opening input methods panel");
826                    if (mInputMethodsPanel != null) mInputMethodsPanel.openPanel();
827                    break;
828                case MSG_CLOSE_INPUT_METHODS_PANEL:
829                    if (DEBUG) Slog.d(TAG, "closing input methods panel");
830                    if (mInputMethodsPanel != null) mInputMethodsPanel.closePanel(false);
831                    break;
832                case MSG_OPEN_COMPAT_MODE_PANEL:
833                    if (DEBUG) Slog.d(TAG, "opening compat panel");
834                    if (mCompatModePanel != null) mCompatModePanel.openPanel();
835                    break;
836                case MSG_CLOSE_COMPAT_MODE_PANEL:
837                    if (DEBUG) Slog.d(TAG, "closing compat panel");
838                    if (mCompatModePanel != null) mCompatModePanel.closePanel();
839                    break;
840                case MSG_SHOW_CHROME:
841                    if (DEBUG) Slog.d(TAG, "hiding shadows (lights on)");
842                    mBarContents.setVisibility(View.VISIBLE);
843                    mShadow.setVisibility(View.GONE);
844                    mSystemUiVisibility &= ~View.SYSTEM_UI_FLAG_LOW_PROFILE;
845                    notifyUiVisibilityChanged();
846                    break;
847                case MSG_HIDE_CHROME:
848                    if (DEBUG) Slog.d(TAG, "showing shadows (lights out)");
849                    animateCollapse();
850                    visibilityChanged(false);
851                    mBarContents.setVisibility(View.GONE);
852                    mShadow.setVisibility(View.VISIBLE);
853                    mSystemUiVisibility |= View.SYSTEM_UI_FLAG_LOW_PROFILE;
854                    notifyUiVisibilityChanged();
855                    break;
856                case MSG_STOP_TICKER:
857                    mTicker.halt();
858                    break;
859            }
860        }
861    }
862
863    public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
864        if (DEBUG) Slog.d(TAG, "addIcon(" + slot + ") -> " + icon);
865    }
866
867    public void updateIcon(String slot, int index, int viewIndex,
868            StatusBarIcon old, StatusBarIcon icon) {
869        if (DEBUG) Slog.d(TAG, "updateIcon(" + slot + ") -> " + icon);
870    }
871
872    public void removeIcon(String slot, int index, int viewIndex) {
873        if (DEBUG) Slog.d(TAG, "removeIcon(" + slot + ")");
874    }
875
876    public void addNotification(IBinder key, StatusBarNotification notification) {
877        if (DEBUG) Slog.d(TAG, "addNotification(" + key + " -> " + notification + ")");
878        addNotificationViews(key, notification);
879
880        final boolean immersive = isImmersive();
881        if (false && immersive) {
882            // TODO: immersive mode popups for tablet
883        } else if (notification.notification.fullScreenIntent != null) {
884            // not immersive & a full-screen alert should be shown
885            Slog.w(TAG, "Notification has fullScreenIntent and activity is not immersive;"
886                    + " sending fullScreenIntent");
887            try {
888                notification.notification.fullScreenIntent.send();
889            } catch (PendingIntent.CanceledException e) {
890            }
891        } else {
892            tick(key, notification, true);
893        }
894
895        setAreThereNotifications();
896    }
897
898    public void removeNotification(IBinder key) {
899        if (DEBUG) Slog.d(TAG, "removeNotification(" + key + ")");
900        removeNotificationViews(key);
901        mTicker.remove(key);
902        setAreThereNotifications();
903    }
904
905    public void showClock(boolean show) {
906        View clock = mBarContents.findViewById(R.id.clock);
907        View network_text = mBarContents.findViewById(R.id.network_text);
908        if (clock != null) {
909            clock.setVisibility(show ? View.VISIBLE : View.GONE);
910        }
911        if (network_text != null) {
912            network_text.setVisibility((!show) ? View.VISIBLE : View.GONE);
913        }
914    }
915
916    public void disable(int state) {
917        int old = mDisabled;
918        int diff = state ^ old;
919        mDisabled = state;
920
921        // act accordingly
922        if ((diff & StatusBarManager.DISABLE_CLOCK) != 0) {
923            boolean show = (state & StatusBarManager.DISABLE_CLOCK) == 0;
924            Slog.i(TAG, "DISABLE_CLOCK: " + (show ? "no" : "yes"));
925            showClock(show);
926        }
927        if ((diff & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
928            boolean show = (state & StatusBarManager.DISABLE_SYSTEM_INFO) == 0;
929            Slog.i(TAG, "DISABLE_SYSTEM_INFO: " + (show ? "no" : "yes"));
930            mNotificationTrigger.setVisibility(show ? View.VISIBLE : View.GONE);
931        }
932        if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
933            if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {
934                Slog.i(TAG, "DISABLE_EXPAND: yes");
935                animateCollapse();
936                visibilityChanged(false);
937            }
938        }
939        if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
940            mNotificationDNDMode = Prefs.read(mContext)
941                        .getBoolean(Prefs.DO_NOT_DISTURB_PREF, Prefs.DO_NOT_DISTURB_DEFAULT);
942
943            if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
944                Slog.i(TAG, "DISABLE_NOTIFICATION_ICONS: yes" + (mNotificationDNDMode?" (DND)":""));
945                mTicker.halt();
946            } else {
947                Slog.i(TAG, "DISABLE_NOTIFICATION_ICONS: no" + (mNotificationDNDMode?" (DND)":""));
948            }
949
950            // refresh icons to show either notifications or the DND message
951            reloadAllNotificationIcons();
952        } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
953            if ((state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
954                mTicker.halt();
955            }
956        }
957        if ((diff & (StatusBarManager.DISABLE_RECENT
958                        | StatusBarManager.DISABLE_BACK
959                        | StatusBarManager.DISABLE_HOME)) != 0) {
960            setNavigationVisibility(state);
961
962            if ((state & StatusBarManager.DISABLE_RECENT) != 0) {
963                // close recents if it's visible
964                mHandler.removeMessages(MSG_CLOSE_RECENTS_PANEL);
965                mHandler.sendEmptyMessage(MSG_CLOSE_RECENTS_PANEL);
966            }
967        }
968    }
969
970    private void setNavigationVisibility(int visibility) {
971        boolean disableHome = ((visibility & StatusBarManager.DISABLE_HOME) != 0);
972        boolean disableRecent = ((visibility & StatusBarManager.DISABLE_RECENT) != 0);
973        boolean disableBack = ((visibility & StatusBarManager.DISABLE_BACK) != 0);
974
975        mBackButton.setVisibility(disableBack ? View.INVISIBLE : View.VISIBLE);
976        mHomeButton.setVisibility(disableHome ? View.INVISIBLE : View.VISIBLE);
977        mRecentButton.setVisibility(disableRecent ? View.INVISIBLE : View.VISIBLE);
978
979        mInputMethodSwitchButton.setScreenLocked(
980                (visibility & StatusBarManager.DISABLE_SYSTEM_INFO) != 0);
981    }
982
983    private boolean hasTicker(Notification n) {
984        return n.tickerView != null || !TextUtils.isEmpty(n.tickerText);
985    }
986
987    @Override
988    protected void tick(IBinder key, StatusBarNotification n, boolean firstTime) {
989        // Don't show the ticker when the windowshade is open.
990        if (mNotificationPanel.isShowing()) {
991            return;
992        }
993        // If they asked for FLAG_ONLY_ALERT_ONCE, then only show this notification
994        // if it's a new notification.
995        if (!firstTime && (n.notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0) {
996            return;
997        }
998        // Show the ticker if one is requested. Also don't do this
999        // until status bar window is attached to the window manager,
1000        // because...  well, what's the point otherwise?  And trying to
1001        // run a ticker without being attached will crash!
1002        if (hasTicker(n.notification) && mStatusBarView.getWindowToken() != null) {
1003            if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS
1004                            | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
1005                mTicker.add(key, n);
1006                mFeedbackIconArea.setVisibility(View.GONE);
1007            }
1008        }
1009    }
1010
1011    // called by TabletTicker when it's done with all queued ticks
1012    public void doneTicking() {
1013        mFeedbackIconArea.setVisibility(View.VISIBLE);
1014    }
1015
1016    public void animateExpand() {
1017        mHandler.removeMessages(MSG_OPEN_NOTIFICATION_PANEL);
1018        mHandler.sendEmptyMessage(MSG_OPEN_NOTIFICATION_PANEL);
1019    }
1020
1021    public void animateCollapse() {
1022        animateCollapse(CommandQueue.FLAG_EXCLUDE_NONE);
1023    }
1024
1025    public void animateCollapse(int flags) {
1026        if ((flags & CommandQueue.FLAG_EXCLUDE_NOTIFICATION_PANEL) == 0) {
1027            mHandler.removeMessages(MSG_CLOSE_NOTIFICATION_PANEL);
1028            mHandler.sendEmptyMessage(MSG_CLOSE_NOTIFICATION_PANEL);
1029        }
1030        if ((flags & CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL) == 0) {
1031            mHandler.removeMessages(MSG_CLOSE_RECENTS_PANEL);
1032            mHandler.sendEmptyMessage(MSG_CLOSE_RECENTS_PANEL);
1033        }
1034        if ((flags & CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL) == 0) {
1035            mHandler.removeMessages(MSG_CLOSE_SEARCH_PANEL);
1036            mHandler.sendEmptyMessage(MSG_CLOSE_SEARCH_PANEL);
1037        }
1038        if ((flags & CommandQueue.FLAG_EXCLUDE_INPUT_METHODS_PANEL) == 0) {
1039            mHandler.removeMessages(MSG_CLOSE_INPUT_METHODS_PANEL);
1040            mHandler.sendEmptyMessage(MSG_CLOSE_INPUT_METHODS_PANEL);
1041        }
1042        if ((flags & CommandQueue.FLAG_EXCLUDE_COMPAT_MODE_PANEL) == 0) {
1043            mHandler.removeMessages(MSG_CLOSE_COMPAT_MODE_PANEL);
1044            mHandler.sendEmptyMessage(MSG_CLOSE_COMPAT_MODE_PANEL);
1045        }
1046
1047    }
1048
1049    @Override // CommandQueue
1050    public void setNavigationIconHints(int hints) {
1051        if (hints == mNavigationIconHints) return;
1052
1053        if (DEBUG) {
1054            android.widget.Toast.makeText(mContext,
1055                "Navigation icon hints = " + hints,
1056                500).show();
1057        }
1058
1059        mNavigationIconHints = hints;
1060
1061        mBackButton.setAlpha(
1062            (0 != (hints & StatusBarManager.NAVIGATION_HINT_BACK_NOP)) ? 0.5f : 1.0f);
1063        mHomeButton.setAlpha(
1064            (0 != (hints & StatusBarManager.NAVIGATION_HINT_HOME_NOP)) ? 0.5f : 1.0f);
1065        mRecentButton.setAlpha(
1066            (0 != (hints & StatusBarManager.NAVIGATION_HINT_RECENT_NOP)) ? 0.5f : 1.0f);
1067
1068        mBackButton.setImageResource(
1069            (0 != (hints & StatusBarManager.NAVIGATION_HINT_BACK_ALT))
1070                ? R.drawable.ic_sysbar_back_ime
1071                : R.drawable.ic_sysbar_back);
1072    }
1073
1074    private void notifyUiVisibilityChanged() {
1075        try {
1076            mWindowManager.statusBarVisibilityChanged(mSystemUiVisibility);
1077        } catch (RemoteException ex) {
1078        }
1079    }
1080
1081    @Override // CommandQueue
1082    public void setSystemUiVisibility(int vis, int mask) {
1083        final int oldVal = mSystemUiVisibility;
1084        final int newVal = (oldVal&~mask) | (vis&mask);
1085        final int diff = newVal ^ oldVal;
1086
1087        if (diff != 0) {
1088            mSystemUiVisibility = newVal;
1089
1090            if (0 != (diff & View.SYSTEM_UI_FLAG_LOW_PROFILE)) {
1091                mHandler.removeMessages(MSG_HIDE_CHROME);
1092                mHandler.removeMessages(MSG_SHOW_CHROME);
1093                mHandler.sendEmptyMessage(0 == (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE)
1094                        ? MSG_SHOW_CHROME : MSG_HIDE_CHROME);
1095            }
1096
1097            notifyUiVisibilityChanged();
1098        }
1099    }
1100
1101    public void setLightsOn(boolean on) {
1102        // Policy note: if the frontmost activity needs the menu key, we assume it is a legacy app
1103        // that can't handle lights-out mode.
1104        if (mMenuButton.getVisibility() == View.VISIBLE) {
1105            on = true;
1106        }
1107
1108        Slog.v(TAG, "setLightsOn(" + on + ")");
1109        if (on) {
1110            setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE);
1111        } else {
1112            setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE, View.SYSTEM_UI_FLAG_LOW_PROFILE);
1113        }
1114    }
1115
1116    public void topAppWindowChanged(boolean showMenu) {
1117        if (DEBUG) {
1118            Slog.d(TAG, (showMenu?"showing":"hiding") + " the MENU button");
1119        }
1120        mMenuButton.setVisibility(showMenu ? View.VISIBLE : View.GONE);
1121
1122        // See above re: lights-out policy for legacy apps.
1123        if (showMenu) setLightsOn(true);
1124
1125        mCompatModeButton.refresh();
1126        if (mCompatModeButton.getVisibility() == View.VISIBLE) {
1127            if (DEBUG_COMPAT_HELP
1128                    || ! Prefs.read(mContext).getBoolean(Prefs.SHOWN_COMPAT_MODE_HELP, false)) {
1129                showCompatibilityHelp();
1130            }
1131        } else {
1132            hideCompatibilityHelp();
1133            mCompatModePanel.closePanel();
1134        }
1135    }
1136
1137    private void showCompatibilityHelp() {
1138        if (mCompatibilityHelpDialog != null) {
1139            return;
1140        }
1141
1142        mCompatibilityHelpDialog = View.inflate(mContext, R.layout.compat_mode_help, null);
1143        View button = mCompatibilityHelpDialog.findViewById(R.id.button);
1144
1145        button.setOnClickListener(new View.OnClickListener() {
1146            @Override
1147            public void onClick(View v) {
1148                hideCompatibilityHelp();
1149                SharedPreferences.Editor editor = Prefs.edit(mContext);
1150                editor.putBoolean(Prefs.SHOWN_COMPAT_MODE_HELP, true);
1151                editor.apply();
1152            }
1153        });
1154
1155        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
1156                ViewGroup.LayoutParams.MATCH_PARENT,
1157                ViewGroup.LayoutParams.MATCH_PARENT,
1158                WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG,
1159                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
1160                    | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
1161                    | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
1162                PixelFormat.TRANSLUCENT);
1163        lp.setTitle("CompatibilityModeDialog");
1164        lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
1165                | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
1166        lp.windowAnimations = com.android.internal.R.style.Animation_ZoomButtons; // simple fade
1167
1168        WindowManagerImpl.getDefault().addView(mCompatibilityHelpDialog, lp);
1169    }
1170
1171    private void hideCompatibilityHelp() {
1172        if (mCompatibilityHelpDialog != null) {
1173            WindowManagerImpl.getDefault().removeView(mCompatibilityHelpDialog);
1174            mCompatibilityHelpDialog = null;
1175        }
1176    }
1177
1178    public void setImeWindowStatus(IBinder token, int vis, int backDisposition) {
1179        mInputMethodSwitchButton.setImeWindowStatus(token,
1180                (vis & InputMethodService.IME_ACTIVE) != 0);
1181        updateNotificationIcons();
1182        mInputMethodsPanel.setImeToken(token);
1183
1184        boolean altBack = (backDisposition == InputMethodService.BACK_DISPOSITION_WILL_DISMISS)
1185            || ((vis & InputMethodService.IME_VISIBLE) != 0);
1186        mAltBackButtonEnabledForIme = altBack;
1187
1188        mCommandQueue.setNavigationIconHints(
1189                altBack ? (mNavigationIconHints | StatusBarManager.NAVIGATION_HINT_BACK_ALT)
1190                        : (mNavigationIconHints & ~StatusBarManager.NAVIGATION_HINT_BACK_ALT));
1191
1192        if (FAKE_SPACE_BAR) {
1193            mFakeSpaceBar.setVisibility(((vis & InputMethodService.IME_VISIBLE) != 0)
1194                    ? View.VISIBLE : View.GONE);
1195        }
1196    }
1197
1198    @Override
1199    public void onRecentsPanelVisibilityChanged(boolean visible) {
1200        boolean altBack = visible || mAltBackButtonEnabledForIme;
1201        mCommandQueue.setNavigationIconHints(
1202                altBack ? (mNavigationIconHints | StatusBarManager.NAVIGATION_HINT_BACK_ALT)
1203                        : (mNavigationIconHints & ~StatusBarManager.NAVIGATION_HINT_BACK_ALT));
1204    }
1205
1206    @Override
1207    public void setHardKeyboardStatus(boolean available, boolean enabled) {
1208        if (DEBUG) {
1209            Slog.d(TAG, "Set hard keyboard status: available=" + available
1210                    + ", enabled=" + enabled);
1211        }
1212        mInputMethodSwitchButton.setHardKeyboardStatus(available);
1213        updateNotificationIcons();
1214        mInputMethodsPanel.setHardKeyboardStatus(available, enabled);
1215    }
1216
1217    @Override
1218    public void onHardKeyboardEnabledChange(boolean enabled) {
1219        try {
1220            mBarService.setHardKeyboardEnabled(enabled);
1221        } catch (RemoteException ex) {
1222        }
1223    }
1224
1225    private boolean isImmersive() {
1226        try {
1227            return ActivityManagerNative.getDefault().isTopActivityImmersive();
1228            //Slog.d(TAG, "Top activity is " + (immersive?"immersive":"not immersive"));
1229        } catch (RemoteException ex) {
1230            // the end is nigh
1231            return false;
1232        }
1233    }
1234
1235    @Override
1236    protected void setAreThereNotifications() {
1237        if (mNotificationPanel != null) {
1238            mNotificationPanel.setClearable(isDeviceProvisioned() && mNotificationData.hasClearableItems());
1239        }
1240    }
1241
1242    private View.OnClickListener mOnClickListener = new View.OnClickListener() {
1243        public void onClick(View v) {
1244            if (v == mRecentButton) {
1245                onClickRecentButton();
1246            } else if (v == mInputMethodSwitchButton) {
1247                onClickInputMethodSwitchButton();
1248            } else if (v == mCompatModeButton) {
1249                onClickCompatModeButton();
1250            }
1251        }
1252    };
1253
1254    public void onClickRecentButton() {
1255        if (DEBUG) Slog.d(TAG, "clicked recent apps; disabled=" + mDisabled);
1256        if ((mDisabled & StatusBarManager.DISABLE_EXPAND) == 0) {
1257            int msg = (mRecentsPanel.getVisibility() == View.VISIBLE)
1258                ? MSG_CLOSE_RECENTS_PANEL : MSG_OPEN_RECENTS_PANEL;
1259            mHandler.removeMessages(msg);
1260            mHandler.sendEmptyMessage(msg);
1261        }
1262    }
1263
1264    public void onClickInputMethodSwitchButton() {
1265        if (DEBUG) Slog.d(TAG, "clicked input methods panel; disabled=" + mDisabled);
1266        int msg = (mInputMethodsPanel.getVisibility() == View.GONE) ?
1267                MSG_OPEN_INPUT_METHODS_PANEL : MSG_CLOSE_INPUT_METHODS_PANEL;
1268        mHandler.removeMessages(msg);
1269        mHandler.sendEmptyMessage(msg);
1270    }
1271
1272    public void onClickCompatModeButton() {
1273        int msg = (mCompatModePanel.getVisibility() == View.GONE) ?
1274                MSG_OPEN_COMPAT_MODE_PANEL : MSG_CLOSE_COMPAT_MODE_PANEL;
1275        mHandler.removeMessages(msg);
1276        mHandler.sendEmptyMessage(msg);
1277    }
1278
1279    private class NotificationTriggerTouchListener implements View.OnTouchListener {
1280        VelocityTracker mVT;
1281        float mInitialTouchX, mInitialTouchY;
1282        int mTouchSlop;
1283
1284        public NotificationTriggerTouchListener() {
1285            mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
1286        }
1287
1288        private Runnable mHiliteOnR = new Runnable() { public void run() {
1289            mNotificationArea.setBackgroundResource(
1290                com.android.internal.R.drawable.list_selector_pressed_holo_dark);
1291        }};
1292        public void hilite(final boolean on) {
1293            if (on) {
1294                mNotificationArea.postDelayed(mHiliteOnR, 100);
1295            } else {
1296                mNotificationArea.removeCallbacks(mHiliteOnR);
1297                mNotificationArea.setBackgroundDrawable(null);
1298            }
1299        }
1300
1301        public boolean onTouch(View v, MotionEvent event) {
1302//            Slog.d(TAG, String.format("touch: (%.1f, %.1f) initial: (%.1f, %.1f)",
1303//                        event.getX(),
1304//                        event.getY(),
1305//                        mInitialTouchX,
1306//                        mInitialTouchY));
1307
1308            if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
1309                return true;
1310            }
1311
1312            final int action = event.getAction();
1313            switch (action) {
1314                case MotionEvent.ACTION_DOWN:
1315                    mVT = VelocityTracker.obtain();
1316                    mInitialTouchX = event.getX();
1317                    mInitialTouchY = event.getY();
1318                    hilite(true);
1319                    // fall through
1320                case MotionEvent.ACTION_OUTSIDE:
1321                case MotionEvent.ACTION_MOVE:
1322                    // check for fling
1323                    if (mVT != null) {
1324                        mVT.addMovement(event);
1325                        mVT.computeCurrentVelocity(1000); // pixels per second
1326                        // require a little more oomph once we're already in peekaboo mode
1327                        if (mVT.getYVelocity() < -mNotificationFlingVelocity) {
1328                            animateExpand();
1329                            visibilityChanged(true);
1330                            hilite(false);
1331                            mVT.recycle();
1332                            mVT = null;
1333                        }
1334                    }
1335                    return true;
1336                case MotionEvent.ACTION_UP:
1337                case MotionEvent.ACTION_CANCEL:
1338                    hilite(false);
1339                    if (mVT != null) {
1340                        if (action == MotionEvent.ACTION_UP
1341                         // was this a sloppy tap?
1342                         && Math.abs(event.getX() - mInitialTouchX) < mTouchSlop
1343                         && Math.abs(event.getY() - mInitialTouchY) < (mTouchSlop / 3)
1344                         // dragging off the bottom doesn't count
1345                         && (int)event.getY() < v.getBottom()) {
1346                            animateExpand();
1347                            visibilityChanged(true);
1348                            v.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
1349                            v.playSoundEffect(SoundEffectConstants.CLICK);
1350                        }
1351
1352                        mVT.recycle();
1353                        mVT = null;
1354                        return true;
1355                    }
1356            }
1357            return false;
1358        }
1359    }
1360
1361    public void resetNotificationPeekFadeTimer() {
1362        if (DEBUG) {
1363            Slog.d(TAG, "setting peek fade timer for " + NOTIFICATION_PEEK_FADE_DELAY
1364                + "ms from now");
1365        }
1366        mHandler.removeMessages(MSG_CLOSE_NOTIFICATION_PEEK);
1367        mHandler.sendEmptyMessageDelayed(MSG_CLOSE_NOTIFICATION_PEEK,
1368                NOTIFICATION_PEEK_FADE_DELAY);
1369    }
1370
1371    private class NotificationIconTouchListener implements View.OnTouchListener {
1372        VelocityTracker mVT;
1373        int mPeekIndex;
1374        float mInitialTouchX, mInitialTouchY;
1375        int mTouchSlop;
1376
1377        public NotificationIconTouchListener() {
1378            mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
1379        }
1380
1381        public boolean onTouch(View v, MotionEvent event) {
1382            boolean peeking = mNotificationPeekWindow.getVisibility() != View.GONE;
1383            boolean panelShowing = mNotificationPanel.isShowing();
1384            if (panelShowing) return false;
1385
1386            int numIcons = mIconLayout.getChildCount();
1387            int newPeekIndex = (int)(event.getX() * numIcons / mIconLayout.getWidth());
1388            if (newPeekIndex > numIcons - 1) newPeekIndex = numIcons - 1;
1389            else if (newPeekIndex < 0) newPeekIndex = 0;
1390
1391            final int action = event.getAction();
1392            switch (action) {
1393                case MotionEvent.ACTION_DOWN:
1394                    mVT = VelocityTracker.obtain();
1395                    mInitialTouchX = event.getX();
1396                    mInitialTouchY = event.getY();
1397                    mPeekIndex = -1;
1398
1399                    // fall through
1400                case MotionEvent.ACTION_OUTSIDE:
1401                case MotionEvent.ACTION_MOVE:
1402                    // peek and switch icons if necessary
1403
1404                    if (newPeekIndex != mPeekIndex) {
1405                        mPeekIndex = newPeekIndex;
1406
1407                        if (DEBUG) Slog.d(TAG, "will peek at notification #" + mPeekIndex);
1408                        Message peekMsg = mHandler.obtainMessage(MSG_OPEN_NOTIFICATION_PEEK);
1409                        peekMsg.arg1 = mPeekIndex;
1410
1411                        mHandler.removeMessages(MSG_OPEN_NOTIFICATION_PEEK);
1412
1413                        if (peeking) {
1414                            // no delay if we're scrubbing left-right
1415                            mHandler.sendMessage(peekMsg);
1416                        } else {
1417                            // wait for fling
1418                            mHandler.sendMessageDelayed(peekMsg, NOTIFICATION_PEEK_HOLD_THRESH);
1419                        }
1420                    }
1421
1422                    // check for fling
1423                    if (mVT != null) {
1424                        mVT.addMovement(event);
1425                        mVT.computeCurrentVelocity(1000); // pixels per second
1426                        // require a little more oomph once we're already in peekaboo mode
1427                        if (!panelShowing && (
1428                               (peeking && mVT.getYVelocity() < -mNotificationFlingVelocity*3)
1429                            || (mVT.getYVelocity() < -mNotificationFlingVelocity))) {
1430                            mHandler.removeMessages(MSG_OPEN_NOTIFICATION_PEEK);
1431                            mHandler.removeMessages(MSG_OPEN_NOTIFICATION_PANEL);
1432                            mHandler.sendEmptyMessage(MSG_CLOSE_NOTIFICATION_PEEK);
1433                            mHandler.sendEmptyMessage(MSG_OPEN_NOTIFICATION_PANEL);
1434                        }
1435                    }
1436                    return true;
1437                case MotionEvent.ACTION_UP:
1438                case MotionEvent.ACTION_CANCEL:
1439                    mHandler.removeMessages(MSG_OPEN_NOTIFICATION_PEEK);
1440                    if (!peeking) {
1441                        if (action == MotionEvent.ACTION_UP
1442                                // was this a sloppy tap?
1443                                && Math.abs(event.getX() - mInitialTouchX) < mTouchSlop
1444                                && Math.abs(event.getY() - mInitialTouchY) < (mTouchSlop / 3)
1445                                // dragging off the bottom doesn't count
1446                                && (int)event.getY() < v.getBottom()) {
1447                            Message peekMsg = mHandler.obtainMessage(MSG_OPEN_NOTIFICATION_PEEK);
1448                            peekMsg.arg1 = mPeekIndex;
1449                            mHandler.removeMessages(MSG_OPEN_NOTIFICATION_PEEK);
1450                            mHandler.sendMessage(peekMsg);
1451
1452                            v.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
1453                            v.playSoundEffect(SoundEffectConstants.CLICK);
1454
1455                            peeking = true; // not technically true yet, but the next line will run
1456                        }
1457                    }
1458
1459                    if (peeking) {
1460                        resetNotificationPeekFadeTimer();
1461                    }
1462
1463                    mVT.recycle();
1464                    mVT = null;
1465                    return true;
1466            }
1467            return false;
1468        }
1469    }
1470
1471    private void reloadAllNotificationIcons() {
1472        if (mIconLayout == null) return;
1473        mIconLayout.removeAllViews();
1474        updateNotificationIcons();
1475    }
1476
1477    @Override
1478    protected void updateNotificationIcons() {
1479        // XXX: need to implement a new limited linear layout class
1480        // to avoid removing & readding everything
1481
1482        if (mIconLayout == null) return;
1483
1484        // first, populate the main notification panel
1485        loadNotificationPanel();
1486
1487        final LinearLayout.LayoutParams params
1488            = new LinearLayout.LayoutParams(mIconSize + 2*mIconHPadding, mNaturalBarHeight);
1489
1490        // alternate behavior in DND mode
1491        if (mNotificationDNDMode) {
1492            if (mIconLayout.getChildCount() == 0) {
1493                final Notification dndNotification = new Notification.Builder(mContext)
1494                    .setContentTitle(mContext.getText(R.string.notifications_off_title))
1495                    .setContentText(mContext.getText(R.string.notifications_off_text))
1496                    .setSmallIcon(R.drawable.ic_notification_dnd)
1497                    .setOngoing(true)
1498                    .getNotification();
1499
1500                final StatusBarIconView iconView = new StatusBarIconView(mContext, "_dnd",
1501                        dndNotification);
1502                iconView.setImageResource(R.drawable.ic_notification_dnd);
1503                iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
1504                iconView.setPadding(mIconHPadding, 0, mIconHPadding, 0);
1505
1506                mNotificationDNDDummyEntry = new NotificationData.Entry(
1507                        null,
1508                        new StatusBarNotification("", 0, "", 0, 0, Notification.PRIORITY_MAX, dndNotification),
1509                        iconView);
1510
1511                mIconLayout.addView(iconView, params);
1512            }
1513
1514            return;
1515        } else if (0 != (mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS)) {
1516            // if icons are disabled but we're not in DND mode, this is probably Setup and we should
1517            // just leave the area totally empty
1518            return;
1519        }
1520
1521        int N = mNotificationData.size();
1522
1523        if (DEBUG) {
1524            Slog.d(TAG, "refreshing icons: " + N + " notifications, mIconLayout=" + mIconLayout);
1525        }
1526
1527        ArrayList<View> toShow = new ArrayList<View>();
1528
1529        // Extra Special Icons
1530        // The IME switcher and compatibility mode icons take the place of notifications. You didn't
1531        // need to see all those new emails, did you?
1532        int maxNotificationIconsCount = mMaxNotificationIcons;
1533        if (mInputMethodSwitchButton.getVisibility() != View.GONE) maxNotificationIconsCount --;
1534        if (mCompatModeButton.getVisibility()        != View.GONE) maxNotificationIconsCount --;
1535
1536        final boolean provisioned = isDeviceProvisioned();
1537        // If the device hasn't been through Setup, we only show system notifications
1538        for (int i=0; toShow.size()< maxNotificationIconsCount; i++) {
1539            if (i >= N) break;
1540            Entry ent = mNotificationData.get(N-i-1);
1541            if ((provisioned && ent.notification.score >= HIDE_ICONS_BELOW_SCORE)
1542                    || showNotificationEvenIfUnprovisioned(ent.notification)) {
1543                toShow.add(ent.icon);
1544            }
1545        }
1546
1547        ArrayList<View> toRemove = new ArrayList<View>();
1548        for (int i=0; i<mIconLayout.getChildCount(); i++) {
1549            View child = mIconLayout.getChildAt(i);
1550            if (!toShow.contains(child)) {
1551                toRemove.add(child);
1552            }
1553        }
1554
1555        for (View remove : toRemove) {
1556            mIconLayout.removeView(remove);
1557        }
1558
1559        for (int i=0; i<toShow.size(); i++) {
1560            View v = toShow.get(i);
1561            v.setPadding(mIconHPadding, 0, mIconHPadding, 0);
1562            if (v.getParent() == null) {
1563                mIconLayout.addView(v, i, params);
1564            }
1565        }
1566    }
1567
1568    private void loadNotificationPanel() {
1569        int N = mNotificationData.size();
1570
1571        ArrayList<View> toShow = new ArrayList<View>();
1572
1573        final boolean provisioned = isDeviceProvisioned();
1574        // If the device hasn't been through Setup, we only show system notifications
1575        for (int i=0; i<N; i++) {
1576            Entry ent = mNotificationData.get(N-i-1);
1577            if (provisioned || showNotificationEvenIfUnprovisioned(ent.notification)) {
1578                toShow.add(ent.row);
1579            }
1580        }
1581
1582        ArrayList<View> toRemove = new ArrayList<View>();
1583        for (int i=0; i<mPile.getChildCount(); i++) {
1584            View child = mPile.getChildAt(i);
1585            if (!toShow.contains(child)) {
1586                toRemove.add(child);
1587            }
1588        }
1589
1590        for (View remove : toRemove) {
1591            mPile.removeView(remove);
1592        }
1593
1594        for (int i=0; i<toShow.size(); i++) {
1595            View v = toShow.get(i);
1596            if (v.getParent() == null) {
1597                // the notification panel has the most important things at the bottom
1598                mPile.addView(v, Math.min(toShow.size()-1-i, mPile.getChildCount()));
1599            }
1600        }
1601
1602        mNotificationPanel.setNotificationCount(toShow.size());
1603        mNotificationPanel.setSettingsEnabled(isDeviceProvisioned());
1604    }
1605
1606    @Override
1607    protected void workAroundBadLayerDrawableOpacity(View v) {
1608        Drawable bgd = v.getBackground();
1609        if (!(bgd instanceof LayerDrawable)) return;
1610
1611        LayerDrawable d = (LayerDrawable) bgd;
1612        v.setBackgroundDrawable(null);
1613        d.setOpacity(PixelFormat.TRANSLUCENT);
1614        v.setBackgroundDrawable(d);
1615    }
1616
1617    public void clearAll() {
1618        try {
1619            mBarService.onClearAllNotifications();
1620        } catch (RemoteException ex) {
1621            // system process is dead if we're here.
1622        }
1623        animateCollapse();
1624        visibilityChanged(false);
1625    }
1626
1627    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1628        public void onReceive(Context context, Intent intent) {
1629            String action = intent.getAction();
1630            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
1631                || Intent.ACTION_SCREEN_OFF.equals(action)) {
1632                int flags = CommandQueue.FLAG_EXCLUDE_NONE;
1633                if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
1634                    String reason = intent.getStringExtra("reason");
1635                    if (reason != null && reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) {
1636                        flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL;
1637                    }
1638                }
1639                if (Intent.ACTION_SCREEN_OFF.equals(action)) {
1640                    // If we're turning the screen off, we want to hide the
1641                    // recents panel with no animation
1642                    // TODO: hide other things, like the notification tray,
1643                    // with no animation as well
1644                    mRecentsPanel.show(false, false);
1645                    flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL;
1646                }
1647                animateCollapse(flags);
1648            }
1649        }
1650    };
1651
1652    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1653        pw.print("mDisabled=0x");
1654        pw.println(Integer.toHexString(mDisabled));
1655        pw.println("mNetworkController:");
1656        mNetworkController.dump(fd, pw, args);
1657    }
1658
1659    @Override
1660    protected boolean isTopNotification(ViewGroup parent, NotificationData.Entry entry) {
1661        return parent.indexOfChild(entry.row) == parent.getChildCount()-1;
1662    }
1663
1664    @Override
1665    protected void haltTicker() {
1666        mTicker.halt();
1667    }
1668
1669    @Override
1670    protected void updateExpandedViewPos(int expandedPosition) {
1671    }
1672
1673    @Override
1674    protected boolean shouldDisableNavbarGestures() {
1675        return mNotificationPanel.getVisibility() == View.VISIBLE
1676                || (mDisabled & StatusBarManager.DISABLE_HOME) != 0;
1677    }
1678}
1679
1680
1681