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