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