• 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.phone;
18 
19 import android.animation.Animator;
20 import android.animation.AnimatorListenerAdapter;
21 import android.animation.AnimatorSet;
22 import android.animation.ObjectAnimator;
23 import android.animation.TimeInterpolator;
24 import android.app.ActivityManager;
25 import android.app.ActivityManagerNative;
26 import android.app.Notification;
27 import android.app.PendingIntent;
28 import android.app.StatusBarManager;
29 import android.service.notification.StatusBarNotification;
30 import android.content.BroadcastReceiver;
31 import android.content.Context;
32 import android.content.Intent;
33 import android.content.IntentFilter;
34 import android.content.SharedPreferences;
35 import android.content.res.Resources;
36 import android.database.ContentObserver;
37 import android.graphics.Canvas;
38 import android.graphics.ColorFilter;
39 import android.graphics.PixelFormat;
40 import android.graphics.Point;
41 import android.graphics.PorterDuff;
42 import android.graphics.Rect;
43 import android.graphics.drawable.Drawable;
44 import android.inputmethodservice.InputMethodService;
45 import android.os.Handler;
46 import android.os.IBinder;
47 import android.os.Message;
48 import android.os.RemoteException;
49 import android.os.ServiceManager;
50 import android.os.SystemClock;
51 import android.os.UserHandle;
52 import android.provider.Settings;
53 import android.service.dreams.DreamService;
54 import android.service.dreams.IDreamManager;
55 import android.util.DisplayMetrics;
56 import android.util.EventLog;
57 import android.util.Log;
58 import android.util.Slog;
59 import android.view.Display;
60 import android.view.Gravity;
61 import android.view.MotionEvent;
62 import android.view.VelocityTracker;
63 import android.view.View;
64 import android.view.ViewGroup;
65 import android.view.ViewGroup.LayoutParams;
66 import android.view.ViewPropertyAnimator;
67 import android.view.ViewStub;
68 import android.view.WindowManager;
69 import android.view.animation.AccelerateInterpolator;
70 import android.view.animation.Animation;
71 import android.view.animation.AnimationUtils;
72 import android.view.animation.DecelerateInterpolator;
73 import android.widget.FrameLayout;
74 import android.widget.ImageView;
75 import android.widget.LinearLayout;
76 import android.widget.ScrollView;
77 import android.widget.TextView;
78 
79 import com.android.internal.statusbar.StatusBarIcon;
80 import com.android.systemui.EventLogTags;
81 import com.android.systemui.R;
82 import com.android.systemui.statusbar.BaseStatusBar;
83 import com.android.systemui.statusbar.CommandQueue;
84 import com.android.systemui.statusbar.GestureRecorder;
85 import com.android.systemui.statusbar.NotificationData;
86 import com.android.systemui.statusbar.NotificationData.Entry;
87 import com.android.systemui.statusbar.SignalClusterView;
88 import com.android.systemui.statusbar.StatusBarIconView;
89 import com.android.systemui.statusbar.policy.BatteryController;
90 import com.android.systemui.statusbar.policy.BluetoothController;
91 import com.android.systemui.statusbar.policy.DateView;
92 import com.android.systemui.statusbar.policy.IntruderAlertView;
93 import com.android.systemui.statusbar.policy.LocationController;
94 import com.android.systemui.statusbar.policy.NetworkController;
95 import com.android.systemui.statusbar.policy.NotificationRowLayout;
96 import com.android.systemui.statusbar.policy.OnSizeChangedListener;
97 import com.android.systemui.statusbar.policy.Prefs;
98 
99 import java.io.FileDescriptor;
100 import java.io.PrintWriter;
101 import java.util.ArrayList;
102 
103 public class PhoneStatusBar extends BaseStatusBar {
104     static final String TAG = "PhoneStatusBar";
105     public static final boolean DEBUG = BaseStatusBar.DEBUG;
106     public static final boolean SPEW = DEBUG;
107     public static final boolean DUMPTRUCK = true; // extra dumpsys info
108     public static final boolean DEBUG_GESTURES = false;
109 
110     public static final boolean DEBUG_CLINGS = false;
111 
112     public static final boolean ENABLE_NOTIFICATION_PANEL_CLING = false;
113 
114     public static final boolean SETTINGS_DRAG_SHORTCUT = true;
115 
116     // additional instrumentation for testing purposes; intended to be left on during development
117     public static final boolean CHATTY = DEBUG;
118 
119     public static final String ACTION_STATUSBAR_START
120             = "com.android.internal.policy.statusbar.START";
121 
122     private static final int MSG_OPEN_NOTIFICATION_PANEL = 1000;
123     private static final int MSG_CLOSE_PANELS = 1001;
124     private static final int MSG_OPEN_SETTINGS_PANEL = 1002;
125     // 1020-1030 reserved for BaseStatusBar
126 
127     // will likely move to a resource or other tunable param at some point
128     private static final int INTRUDER_ALERT_DECAY_MS = 0; // disabled, was 10000;
129 
130     private static final boolean CLOSE_PANEL_WHEN_EMPTIED = true;
131 
132     private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10; // see NotificationManagerService
133     private static final int HIDE_ICONS_BELOW_SCORE = Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER;
134 
135     // fling gesture tuning parameters, scaled to display density
136     private float mSelfExpandVelocityPx; // classic value: 2000px/s
137     private float mSelfCollapseVelocityPx; // classic value: 2000px/s (will be negated to collapse "up")
138     private float mFlingExpandMinVelocityPx; // classic value: 200px/s
139     private float mFlingCollapseMinVelocityPx; // classic value: 200px/s
140     private float mCollapseMinDisplayFraction; // classic value: 0.08 (25px/min(320px,480px) on G1)
141     private float mExpandMinDisplayFraction; // classic value: 0.5 (drag open halfway to expand)
142     private float mFlingGestureMaxXVelocityPx; // classic value: 150px/s
143 
144     private float mExpandAccelPx; // classic value: 2000px/s/s
145     private float mCollapseAccelPx; // classic value: 2000px/s/s (will be negated to collapse "up")
146 
147     private float mFlingGestureMaxOutputVelocityPx; // how fast can it really go? (should be a little
148                                                     // faster than mSelfCollapseVelocityPx)
149 
150     PhoneStatusBarPolicy mIconPolicy;
151 
152     // These are no longer handled by the policy, because we need custom strategies for them
153     BluetoothController mBluetoothController;
154     BatteryController mBatteryController;
155     LocationController mLocationController;
156     NetworkController mNetworkController;
157 
158     int mNaturalBarHeight = -1;
159     int mIconSize = -1;
160     int mIconHPadding = -1;
161     Display mDisplay;
162     Point mCurrentDisplaySize = new Point();
163 
164     IDreamManager mDreamManager;
165 
166     StatusBarWindowView mStatusBarWindow;
167     PhoneStatusBarView mStatusBarView;
168 
169     int mPixelFormat;
170     Object mQueueLock = new Object();
171 
172     // viewgroup containing the normal contents of the statusbar
173     LinearLayout mStatusBarContents;
174 
175     // right-hand icons
176     LinearLayout mSystemIconArea;
177 
178     // left-hand icons
179     LinearLayout mStatusIcons;
180     // the icons themselves
181     IconMerger mNotificationIcons;
182     // [+>
183     View mMoreIcon;
184 
185     // expanded notifications
186     NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window
187     ScrollView mScrollView;
188     View mExpandedContents;
189     int mNotificationPanelGravity;
190     int mNotificationPanelMarginBottomPx, mNotificationPanelMarginPx;
191     float mNotificationPanelMinHeightFrac;
192     boolean mNotificationPanelIsFullScreenWidth;
193     TextView mNotificationPanelDebugText;
194 
195     // settings
196     QuickSettings mQS;
197     boolean mHasSettingsPanel, mHasFlipSettings;
198     SettingsPanelView mSettingsPanel;
199     View mFlipSettingsView;
200     QuickSettingsContainerView mSettingsContainer;
201     int mSettingsPanelGravity;
202 
203     // top bar
204     View mNotificationPanelHeader;
205     View mDateTimeView;
206     View mClearButton;
207     ImageView mSettingsButton, mNotificationButton;
208 
209     // carrier/wifi label
210     private TextView mCarrierLabel;
211     private boolean mCarrierLabelVisible = false;
212     private int mCarrierLabelHeight;
213     private TextView mEmergencyCallLabel;
214     private int mNotificationHeaderHeight;
215 
216     private boolean mShowCarrierInPanel = false;
217 
218     // position
219     int[] mPositionTmp = new int[2];
220     boolean mExpandedVisible;
221 
222     // the date view
223     DateView mDateView;
224 
225     // for immersive activities
226     private IntruderAlertView mIntruderAlertView;
227 
228     // on-screen navigation buttons
229     private NavigationBarView mNavigationBarView = null;
230 
231     // the tracker view
232     int mTrackingPosition; // the position of the top of the tracking view.
233 
234     // ticker
235     private Ticker mTicker;
236     private View mTickerView;
237     private boolean mTicking;
238 
239     // Tracking finger for opening/closing.
240     int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore
241     boolean mTracking;
242     VelocityTracker mVelocityTracker;
243 
244     // help screen
245     private boolean mClingShown;
246     private ViewGroup mCling;
247     private boolean mSuppressStatusBarDrags; // while a cling is up, briefly deaden the bar to give things time to settle
248 
249     int[] mAbsPos = new int[2];
250     Runnable mPostCollapseCleanup = null;
251 
252     private Animator mLightsOutAnimation;
253     private Animator mLightsOnAnimation;
254 
255     // for disabling the status bar
256     int mDisabled = 0;
257 
258     // tracking calls to View.setSystemUiVisibility()
259     int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE;
260 
261     DisplayMetrics mDisplayMetrics = new DisplayMetrics();
262 
263     // XXX: gesture research
264     private final GestureRecorder mGestureRec = DEBUG_GESTURES
265         ? new GestureRecorder("/sdcard/statusbar_gestures.dat")
266         : null;
267 
268     private int mNavigationIconHints = 0;
269     private final Animator.AnimatorListener mMakeIconsInvisible = new AnimatorListenerAdapter() {
270         @Override
271         public void onAnimationEnd(Animator animation) {
272             // double-check to avoid races
273             if (mStatusBarContents.getAlpha() == 0) {
274                 if (DEBUG) Slog.d(TAG, "makeIconsInvisible");
275                 mStatusBarContents.setVisibility(View.INVISIBLE);
276             }
277         }
278     };
279 
280     // ensure quick settings is disabled until the current user makes it through the setup wizard
281     private boolean mUserSetup = false;
282     private ContentObserver mUserSetupObserver = new ContentObserver(new Handler()) {
283         @Override
284         public void onChange(boolean selfChange) {
285             final boolean userSetup = 0 != Settings.Secure.getIntForUser(
286                     mContext.getContentResolver(),
287                     Settings.Secure.USER_SETUP_COMPLETE,
288                     0 /*default */,
289                     mCurrentUserId);
290             if (MULTIUSER_DEBUG) Slog.d(TAG, String.format("User setup changed: " +
291                     "selfChange=%s userSetup=%s mUserSetup=%s",
292                     selfChange, userSetup, mUserSetup));
293             if (mSettingsButton != null && mHasFlipSettings) {
294                 mSettingsButton.setVisibility(userSetup ? View.VISIBLE : View.INVISIBLE);
295             }
296             if (mSettingsPanel != null) {
297                 mSettingsPanel.setEnabled(userSetup);
298             }
299             if (userSetup != mUserSetup) {
300                 mUserSetup = userSetup;
301                 if (!mUserSetup && mStatusBarView != null)
302                     animateCollapseQuickSettings();
303             }
304         }
305     };
306 
307     @Override
start()308     public void start() {
309         mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
310                 .getDefaultDisplay();
311 
312         mDreamManager = IDreamManager.Stub.asInterface(
313                 ServiceManager.checkService(DreamService.DREAM_SERVICE));
314 
315         super.start(); // calls createAndAddWindows()
316 
317         addNavigationBar();
318 
319         if (ENABLE_INTRUDERS) addIntruderView();
320 
321         // Lastly, call to the icon policy to install/update all the icons.
322         mIconPolicy = new PhoneStatusBarPolicy(mContext);
323     }
324 
325     // ================================================================================
326     // Constructing the view
327     // ================================================================================
makeStatusBarView()328     protected PhoneStatusBarView makeStatusBarView() {
329         final Context context = mContext;
330 
331         Resources res = context.getResources();
332 
333         updateDisplaySize(); // populates mDisplayMetrics
334         loadDimens();
335 
336         mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
337 
338         mStatusBarWindow = (StatusBarWindowView) View.inflate(context,
339                 R.layout.super_status_bar, null);
340         mStatusBarWindow.mService = this;
341         mStatusBarWindow.setOnTouchListener(new View.OnTouchListener() {
342             @Override
343             public boolean onTouch(View v, MotionEvent event) {
344                 if (event.getAction() == MotionEvent.ACTION_DOWN) {
345                     if (mExpandedVisible) {
346                         animateCollapsePanels();
347                     }
348                 }
349                 return mStatusBarWindow.onTouchEvent(event);
350             }});
351 
352         mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar);
353         mStatusBarView.setBar(this);
354 
355 
356         PanelHolder holder = (PanelHolder) mStatusBarWindow.findViewById(R.id.panel_holder);
357         mStatusBarView.setPanelHolder(holder);
358 
359         mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById(R.id.notification_panel);
360         mNotificationPanel.setStatusBar(this);
361         mNotificationPanelIsFullScreenWidth =
362             (mNotificationPanel.getLayoutParams().width == ViewGroup.LayoutParams.MATCH_PARENT);
363 
364         // make the header non-responsive to clicks
365         mNotificationPanel.findViewById(R.id.header).setOnTouchListener(
366                 new View.OnTouchListener() {
367                     @Override
368                     public boolean onTouch(View v, MotionEvent event) {
369                         return true; // e eats everything
370                     }
371                 });
372 
373         if (!ActivityManager.isHighEndGfx()) {
374             mStatusBarWindow.setBackground(null);
375             mNotificationPanel.setBackground(new FastColorDrawable(context.getResources().getColor(
376                     R.color.notification_panel_solid_background)));
377         }
378         if (ENABLE_INTRUDERS) {
379             mIntruderAlertView = (IntruderAlertView) View.inflate(context, R.layout.intruder_alert, null);
380             mIntruderAlertView.setVisibility(View.GONE);
381             mIntruderAlertView.setBar(this);
382         }
383         if (MULTIUSER_DEBUG) {
384             mNotificationPanelDebugText = (TextView) mNotificationPanel.findViewById(R.id.header_debug_info);
385             mNotificationPanelDebugText.setVisibility(View.VISIBLE);
386         }
387 
388         updateShowSearchHoldoff();
389 
390         try {
391             boolean showNav = mWindowManagerService.hasNavigationBar();
392             if (DEBUG) Slog.v(TAG, "hasNavigationBar=" + showNav);
393             if (showNav) {
394                 mNavigationBarView =
395                     (NavigationBarView) View.inflate(context, R.layout.navigation_bar, null);
396 
397                 mNavigationBarView.setDisabledFlags(mDisabled);
398                 mNavigationBarView.setBar(this);
399             }
400         } catch (RemoteException ex) {
401             // no window manager? good luck with that
402         }
403 
404         // figure out which pixel-format to use for the status bar.
405         mPixelFormat = PixelFormat.OPAQUE;
406 
407         mSystemIconArea = (LinearLayout) mStatusBarView.findViewById(R.id.system_icon_area);
408         mStatusIcons = (LinearLayout)mStatusBarView.findViewById(R.id.statusIcons);
409         mNotificationIcons = (IconMerger)mStatusBarView.findViewById(R.id.notificationIcons);
410         mMoreIcon = mStatusBarView.findViewById(R.id.moreIcon);
411         mNotificationIcons.setOverflowIndicator(mMoreIcon);
412         mStatusBarContents = (LinearLayout)mStatusBarView.findViewById(R.id.status_bar_contents);
413         mTickerView = mStatusBarView.findViewById(R.id.ticker);
414 
415         mPile = (NotificationRowLayout)mStatusBarWindow.findViewById(R.id.latestItems);
416         mPile.setLayoutTransitionsEnabled(false);
417         mPile.setLongPressListener(getNotificationLongClicker());
418         mExpandedContents = mPile; // was: expanded.findViewById(R.id.notificationLinearLayout);
419 
420         mNotificationPanelHeader = mStatusBarWindow.findViewById(R.id.header);
421 
422         mClearButton = mStatusBarWindow.findViewById(R.id.clear_all_button);
423         mClearButton.setOnClickListener(mClearButtonListener);
424         mClearButton.setAlpha(0f);
425         mClearButton.setVisibility(View.INVISIBLE);
426         mClearButton.setEnabled(false);
427         mDateView = (DateView)mStatusBarWindow.findViewById(R.id.date);
428 
429         mHasSettingsPanel = res.getBoolean(R.bool.config_hasSettingsPanel);
430         mHasFlipSettings = res.getBoolean(R.bool.config_hasFlipSettingsPanel);
431 
432         mDateTimeView = mNotificationPanelHeader.findViewById(R.id.datetime);
433         if (mDateTimeView != null) {
434             mDateTimeView.setOnClickListener(mClockClickListener);
435             mDateTimeView.setEnabled(true);
436         }
437 
438         mSettingsButton = (ImageView) mStatusBarWindow.findViewById(R.id.settings_button);
439         if (mSettingsButton != null) {
440             mSettingsButton.setOnClickListener(mSettingsButtonListener);
441             if (mHasSettingsPanel) {
442                 if (mStatusBarView.hasFullWidthNotifications()) {
443                     // the settings panel is hiding behind this button
444                     mSettingsButton.setImageResource(R.drawable.ic_notify_quicksettings);
445                     mSettingsButton.setVisibility(View.VISIBLE);
446                 } else {
447                     // there is a settings panel, but it's on the other side of the (large) screen
448                     final View buttonHolder = mStatusBarWindow.findViewById(
449                             R.id.settings_button_holder);
450                     if (buttonHolder != null) {
451                         buttonHolder.setVisibility(View.GONE);
452                     }
453                 }
454             } else {
455                 // no settings panel, go straight to settings
456                 mSettingsButton.setVisibility(View.VISIBLE);
457                 mSettingsButton.setImageResource(R.drawable.ic_notify_settings);
458             }
459         }
460         if (mHasFlipSettings) {
461             mNotificationButton = (ImageView) mStatusBarWindow.findViewById(R.id.notification_button);
462             if (mNotificationButton != null) {
463                 mNotificationButton.setOnClickListener(mNotificationButtonListener);
464             }
465         }
466 
467         mScrollView = (ScrollView)mStatusBarWindow.findViewById(R.id.scroll);
468         mScrollView.setVerticalScrollBarEnabled(false); // less drawing during pulldowns
469         if (!mNotificationPanelIsFullScreenWidth) {
470             mScrollView.setSystemUiVisibility(
471                     View.STATUS_BAR_DISABLE_NOTIFICATION_TICKER |
472                     View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS |
473                     View.STATUS_BAR_DISABLE_CLOCK);
474         }
475 
476         mTicker = new MyTicker(context, mStatusBarView);
477 
478         TickerView tickerView = (TickerView)mStatusBarView.findViewById(R.id.tickerText);
479         tickerView.mTicker = mTicker;
480 
481         mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
482 
483         // set the inital view visibility
484         setAreThereNotifications();
485 
486         // Other icons
487         mLocationController = new LocationController(mContext); // will post a notification
488         mBatteryController = new BatteryController(mContext);
489         mBatteryController.addIconView((ImageView)mStatusBarView.findViewById(R.id.battery));
490         mNetworkController = new NetworkController(mContext);
491         mBluetoothController = new BluetoothController(mContext);
492         final SignalClusterView signalCluster =
493                 (SignalClusterView)mStatusBarView.findViewById(R.id.signal_cluster);
494 
495 
496         mNetworkController.addSignalCluster(signalCluster);
497         signalCluster.setNetworkController(mNetworkController);
498 
499         final boolean isAPhone = mNetworkController.hasVoiceCallingFeature();
500         if (isAPhone) {
501             mEmergencyCallLabel =
502                     (TextView) mStatusBarWindow.findViewById(R.id.emergency_calls_only);
503             if (mEmergencyCallLabel != null) {
504                 mNetworkController.addEmergencyLabelView(mEmergencyCallLabel);
505                 mEmergencyCallLabel.setOnClickListener(new View.OnClickListener() {
506                     public void onClick(View v) { }});
507                 mEmergencyCallLabel.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
508                     @Override
509                     public void onLayoutChange(View v, int left, int top, int right, int bottom,
510                             int oldLeft, int oldTop, int oldRight, int oldBottom) {
511                         updateCarrierLabelVisibility(false);
512                     }});
513             }
514         }
515 
516         mCarrierLabel = (TextView)mStatusBarWindow.findViewById(R.id.carrier_label);
517         mShowCarrierInPanel = (mCarrierLabel != null);
518         if (DEBUG) Slog.v(TAG, "carrierlabel=" + mCarrierLabel + " show=" + mShowCarrierInPanel);
519         if (mShowCarrierInPanel) {
520             mCarrierLabel.setVisibility(mCarrierLabelVisible ? View.VISIBLE : View.INVISIBLE);
521 
522             // for mobile devices, we always show mobile connection info here (SPN/PLMN)
523             // for other devices, we show whatever network is connected
524             if (mNetworkController.hasMobileDataFeature()) {
525                 mNetworkController.addMobileLabelView(mCarrierLabel);
526             } else {
527                 mNetworkController.addCombinedLabelView(mCarrierLabel);
528             }
529 
530             // set up the dynamic hide/show of the label
531             mPile.setOnSizeChangedListener(new OnSizeChangedListener() {
532                 @Override
533                 public void onSizeChanged(View view, int w, int h, int oldw, int oldh) {
534                     updateCarrierLabelVisibility(false);
535                 }
536             });
537         }
538 
539         // Quick Settings (where available, some restrictions apply)
540         if (mHasSettingsPanel) {
541             // first, figure out where quick settings should be inflated
542             final View settings_stub;
543             if (mHasFlipSettings) {
544                 // a version of quick settings that flips around behind the notifications
545                 settings_stub = mStatusBarWindow.findViewById(R.id.flip_settings_stub);
546                 if (settings_stub != null) {
547                     mFlipSettingsView = ((ViewStub)settings_stub).inflate();
548                     mFlipSettingsView.setVisibility(View.GONE);
549                     mFlipSettingsView.setVerticalScrollBarEnabled(false);
550                 }
551             } else {
552                 // full quick settings panel
553                 settings_stub = mStatusBarWindow.findViewById(R.id.quick_settings_stub);
554                 if (settings_stub != null) {
555                     mSettingsPanel = (SettingsPanelView) ((ViewStub)settings_stub).inflate();
556                 } else {
557                     mSettingsPanel = (SettingsPanelView) mStatusBarWindow.findViewById(R.id.settings_panel);
558                 }
559 
560                 if (mSettingsPanel != null) {
561                     if (!ActivityManager.isHighEndGfx()) {
562                         mSettingsPanel.setBackground(new FastColorDrawable(context.getResources().getColor(
563                                 R.color.notification_panel_solid_background)));
564                     }
565                 }
566             }
567 
568             // wherever you find it, Quick Settings needs a container to survive
569             mSettingsContainer = (QuickSettingsContainerView)
570                     mStatusBarWindow.findViewById(R.id.quick_settings_container);
571             if (mSettingsContainer != null) {
572                 mQS = new QuickSettings(mContext, mSettingsContainer);
573                 if (!mNotificationPanelIsFullScreenWidth) {
574                     mSettingsContainer.setSystemUiVisibility(
575                             View.STATUS_BAR_DISABLE_NOTIFICATION_TICKER
576                             | View.STATUS_BAR_DISABLE_SYSTEM_INFO);
577                 }
578                 if (mSettingsPanel != null) {
579                     mSettingsPanel.setQuickSettings(mQS);
580                 }
581                 mQS.setService(this);
582                 mQS.setBar(mStatusBarView);
583                 mQS.setup(mNetworkController, mBluetoothController, mBatteryController,
584                         mLocationController);
585             } else {
586                 mQS = null; // fly away, be free
587             }
588         }
589 
590         mClingShown = ! (DEBUG_CLINGS
591             || !Prefs.read(mContext).getBoolean(Prefs.SHOWN_QUICK_SETTINGS_HELP, false));
592 
593         if (!ENABLE_NOTIFICATION_PANEL_CLING || ActivityManager.isRunningInTestHarness()) {
594             mClingShown = true;
595         }
596 
597 //        final ImageView wimaxRSSI =
598 //                (ImageView)sb.findViewById(R.id.wimax_signal);
599 //        if (wimaxRSSI != null) {
600 //            mNetworkController.addWimaxIconView(wimaxRSSI);
601 //        }
602 
603         // receive broadcasts
604         IntentFilter filter = new IntentFilter();
605         filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
606         filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
607         filter.addAction(Intent.ACTION_SCREEN_OFF);
608         filter.addAction(Intent.ACTION_SCREEN_ON);
609         context.registerReceiver(mBroadcastReceiver, filter);
610 
611         // listen for USER_SETUP_COMPLETE setting (per-user)
612         resetUserSetupObserver();
613 
614         return mStatusBarView;
615     }
616 
617     @Override
getStatusBarView()618     protected View getStatusBarView() {
619         return mStatusBarView;
620     }
621 
622     @Override
getRecentsLayoutParams(LayoutParams layoutParams)623     protected WindowManager.LayoutParams getRecentsLayoutParams(LayoutParams layoutParams) {
624         boolean opaque = false;
625         WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
626                 layoutParams.width,
627                 layoutParams.height,
628                 WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
629                 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
630                 | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
631                 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
632                 (opaque ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT));
633         if (ActivityManager.isHighEndGfx()) {
634             lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
635         } else {
636             lp.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;
637             lp.dimAmount = 0.75f;
638         }
639         lp.gravity = Gravity.BOTTOM | Gravity.START;
640         lp.setTitle("RecentsPanel");
641         lp.windowAnimations = com.android.internal.R.style.Animation_RecentApplications;
642         lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
643         | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
644         return lp;
645     }
646 
647     @Override
getSearchLayoutParams(LayoutParams layoutParams)648     protected WindowManager.LayoutParams getSearchLayoutParams(LayoutParams layoutParams) {
649         boolean opaque = false;
650         WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
651                 LayoutParams.MATCH_PARENT,
652                 LayoutParams.MATCH_PARENT,
653                 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
654                 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
655                 | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
656                 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
657                 (opaque ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT));
658         if (ActivityManager.isHighEndGfx()) {
659             lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
660         }
661         lp.gravity = Gravity.BOTTOM | Gravity.START;
662         lp.setTitle("SearchPanel");
663         // TODO: Define custom animation for Search panel
664         lp.windowAnimations = com.android.internal.R.style.Animation_RecentApplications;
665         lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
666         | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
667         return lp;
668     }
669 
670     @Override
updateSearchPanel()671     protected void updateSearchPanel() {
672         super.updateSearchPanel();
673         mSearchPanelView.setStatusBarView(mNavigationBarView);
674         mNavigationBarView.setDelegateView(mSearchPanelView);
675     }
676 
677     @Override
showSearchPanel()678     public void showSearchPanel() {
679         super.showSearchPanel();
680         mHandler.removeCallbacks(mShowSearchPanel);
681 
682         // we want to freeze the sysui state wherever it is
683         mSearchPanelView.setSystemUiVisibility(mSystemUiVisibility);
684 
685         WindowManager.LayoutParams lp =
686             (android.view.WindowManager.LayoutParams) mNavigationBarView.getLayoutParams();
687         lp.flags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
688         mWindowManager.updateViewLayout(mNavigationBarView, lp);
689     }
690 
691     @Override
hideSearchPanel()692     public void hideSearchPanel() {
693         super.hideSearchPanel();
694         WindowManager.LayoutParams lp =
695             (android.view.WindowManager.LayoutParams) mNavigationBarView.getLayoutParams();
696         lp.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
697         mWindowManager.updateViewLayout(mNavigationBarView, lp);
698     }
699 
getStatusBarGravity()700     protected int getStatusBarGravity() {
701         return Gravity.TOP | Gravity.FILL_HORIZONTAL;
702     }
703 
getStatusBarHeight()704     public int getStatusBarHeight() {
705         if (mNaturalBarHeight < 0) {
706             final Resources res = mContext.getResources();
707             mNaturalBarHeight =
708                     res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
709         }
710         return mNaturalBarHeight;
711     }
712 
713     private View.OnClickListener mRecentsClickListener = new View.OnClickListener() {
714         public void onClick(View v) {
715             awakenDreams();
716             toggleRecentApps();
717         }
718     };
719 
720     private int mShowSearchHoldoff = 0;
721     private Runnable mShowSearchPanel = new Runnable() {
722         public void run() {
723             showSearchPanel();
724             awakenDreams();
725         }
726     };
727 
728     View.OnTouchListener mHomeSearchActionListener = new View.OnTouchListener() {
729         public boolean onTouch(View v, MotionEvent event) {
730             switch(event.getAction()) {
731             case MotionEvent.ACTION_DOWN:
732                 if (!shouldDisableNavbarGestures()) {
733                     mHandler.removeCallbacks(mShowSearchPanel);
734                     mHandler.postDelayed(mShowSearchPanel, mShowSearchHoldoff);
735                 }
736             break;
737 
738             case MotionEvent.ACTION_UP:
739             case MotionEvent.ACTION_CANCEL:
740                 mHandler.removeCallbacks(mShowSearchPanel);
741                 awakenDreams();
742             break;
743         }
744         return false;
745         }
746     };
747 
awakenDreams()748     private void awakenDreams() {
749         if (mDreamManager != null) {
750             try {
751                 mDreamManager.awaken();
752             } catch (RemoteException e) {
753                 // fine, stay asleep then
754             }
755         }
756     }
757 
prepareNavigationBarView()758     private void prepareNavigationBarView() {
759         mNavigationBarView.reorient();
760 
761         mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
762         mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPreloadOnTouchListener);
763         mNavigationBarView.getHomeButton().setOnTouchListener(mHomeSearchActionListener);
764         mNavigationBarView.getSearchLight().setOnTouchListener(mHomeSearchActionListener);
765         updateSearchPanel();
766     }
767 
768     // For small-screen devices (read: phones) that lack hardware navigation buttons
addNavigationBar()769     private void addNavigationBar() {
770         if (DEBUG) Slog.v(TAG, "addNavigationBar: about to add " + mNavigationBarView);
771         if (mNavigationBarView == null) return;
772 
773         prepareNavigationBarView();
774 
775         mWindowManager.addView(mNavigationBarView, getNavigationBarLayoutParams());
776     }
777 
repositionNavigationBar()778     private void repositionNavigationBar() {
779         if (mNavigationBarView == null) return;
780 
781         prepareNavigationBarView();
782 
783         mWindowManager.updateViewLayout(mNavigationBarView, getNavigationBarLayoutParams());
784     }
785 
notifyNavigationBarScreenOn(boolean screenOn)786     private void notifyNavigationBarScreenOn(boolean screenOn) {
787         if (mNavigationBarView == null) return;
788         mNavigationBarView.notifyScreenOn(screenOn);
789     }
790 
getNavigationBarLayoutParams()791     private WindowManager.LayoutParams getNavigationBarLayoutParams() {
792         WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
793                 LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
794                 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
795                     0
796                     | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
797                     | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
798                     | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
799                     | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
800                     | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
801                 PixelFormat.OPAQUE);
802         // this will allow the navbar to run in an overlay on devices that support this
803         if (ActivityManager.isHighEndGfx()) {
804             lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
805         }
806 
807         lp.setTitle("NavigationBar");
808         lp.windowAnimations = 0;
809         return lp;
810     }
811 
addIntruderView()812     private void addIntruderView() {
813         WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
814                 ViewGroup.LayoutParams.MATCH_PARENT,
815                 ViewGroup.LayoutParams.WRAP_CONTENT,
816                 WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, // above the status bar!
817                 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
818                     | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
819                     | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
820                     | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
821                     | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
822                     | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
823                 PixelFormat.TRANSLUCENT);
824         lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
825         //lp.y += height * 1.5; // FIXME
826         lp.setTitle("IntruderAlert");
827         lp.packageName = mContext.getPackageName();
828         lp.windowAnimations = R.style.Animation_StatusBar_IntruderAlert;
829 
830         mWindowManager.addView(mIntruderAlertView, lp);
831     }
832 
refreshAllStatusBarIcons()833     public void refreshAllStatusBarIcons() {
834         refreshAllIconsForLayout(mStatusIcons);
835         refreshAllIconsForLayout(mNotificationIcons);
836     }
837 
refreshAllIconsForLayout(LinearLayout ll)838     private void refreshAllIconsForLayout(LinearLayout ll) {
839         final int count = ll.getChildCount();
840         for (int n = 0; n < count; n++) {
841             View child = ll.getChildAt(n);
842             if (child instanceof StatusBarIconView) {
843                 ((StatusBarIconView) child).updateDrawable();
844             }
845         }
846     }
847 
addIcon(String slot, int index, int viewIndex, StatusBarIcon icon)848     public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
849         if (SPEW) Slog.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
850                 + " icon=" + icon);
851         StatusBarIconView view = new StatusBarIconView(mContext, slot, null);
852         view.set(icon);
853         mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(mIconSize, mIconSize));
854     }
855 
updateIcon(String slot, int index, int viewIndex, StatusBarIcon old, StatusBarIcon icon)856     public void updateIcon(String slot, int index, int viewIndex,
857             StatusBarIcon old, StatusBarIcon icon) {
858         if (SPEW) Slog.d(TAG, "updateIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
859                 + " old=" + old + " icon=" + icon);
860         StatusBarIconView view = (StatusBarIconView)mStatusIcons.getChildAt(viewIndex);
861         view.set(icon);
862     }
863 
removeIcon(String slot, int index, int viewIndex)864     public void removeIcon(String slot, int index, int viewIndex) {
865         if (SPEW) Slog.d(TAG, "removeIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex);
866         mStatusIcons.removeViewAt(viewIndex);
867     }
868 
addNotification(IBinder key, StatusBarNotification notification)869     public void addNotification(IBinder key, StatusBarNotification notification) {
870         if (DEBUG) Slog.d(TAG, "addNotification score=" + notification.getScore());
871         StatusBarIconView iconView = addNotificationViews(key, notification);
872         if (iconView == null) return;
873 
874         boolean immersive = false;
875         try {
876             immersive = ActivityManagerNative.getDefault().isTopActivityImmersive();
877             if (DEBUG) {
878                 Slog.d(TAG, "Top activity is " + (immersive?"immersive":"not immersive"));
879             }
880         } catch (RemoteException ex) {
881         }
882 
883         /*
884          * DISABLED due to missing API
885         if (ENABLE_INTRUDERS && (
886                    // TODO(dsandler): Only if the screen is on
887                 notification.notification.intruderView != null)) {
888             Slog.d(TAG, "Presenting high-priority notification");
889             // special new transient ticker mode
890             // 1. Populate mIntruderAlertView
891 
892             if (notification.notification.intruderView == null) {
893                 Slog.e(TAG, notification.notification.toString() + " wanted to intrude but intruderView was null");
894                 return;
895             }
896 
897             // bind the click event to the content area
898             PendingIntent contentIntent = notification.notification.contentIntent;
899             final View.OnClickListener listener = (contentIntent != null)
900                     ? new NotificationClicker(contentIntent,
901                             notification.pkg, notification.tag, notification.id)
902                     : null;
903 
904             mIntruderAlertView.applyIntruderContent(notification.notification.intruderView, listener);
905 
906             mCurrentlyIntrudingNotification = notification;
907 
908             // 2. Animate mIntruderAlertView in
909             mHandler.sendEmptyMessage(MSG_SHOW_INTRUDER);
910 
911             // 3. Set alarm to age the notification off (TODO)
912             mHandler.removeMessages(MSG_HIDE_INTRUDER);
913             if (INTRUDER_ALERT_DECAY_MS > 0) {
914                 mHandler.sendEmptyMessageDelayed(MSG_HIDE_INTRUDER, INTRUDER_ALERT_DECAY_MS);
915             }
916         } else
917          */
918 
919         if (notification.getNotification().fullScreenIntent != null) {
920             // Stop screensaver if the notification has a full-screen intent.
921             // (like an incoming phone call)
922             awakenDreams();
923 
924             // not immersive & a full-screen alert should be shown
925             if (DEBUG) Slog.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
926             try {
927                 notification.getNotification().fullScreenIntent.send();
928             } catch (PendingIntent.CanceledException e) {
929             }
930         } else {
931             // usual case: status bar visible & not immersive
932 
933             // show the ticker if there isn't an intruder too
934             if (mCurrentlyIntrudingNotification == null) {
935                 tick(null, notification, true);
936             }
937         }
938 
939         // Recalculate the position of the sliding windows and the titles.
940         setAreThereNotifications();
941         updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
942     }
943 
removeNotification(IBinder key)944     public void removeNotification(IBinder key) {
945         StatusBarNotification old = removeNotificationViews(key);
946         if (SPEW) Slog.d(TAG, "removeNotification key=" + key + " old=" + old);
947 
948         if (old != null) {
949             // Cancel the ticker if it's still running
950             mTicker.removeEntry(old);
951 
952             // Recalculate the position of the sliding windows and the titles.
953             updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
954 
955             if (ENABLE_INTRUDERS && old == mCurrentlyIntrudingNotification) {
956                 mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER);
957             }
958 
959             if (CLOSE_PANEL_WHEN_EMPTIED && mNotificationData.size() == 0) {
960                 animateCollapsePanels();
961             }
962         }
963 
964         setAreThereNotifications();
965     }
966 
967     @Override
refreshLayout(int layoutDirection)968     protected void refreshLayout(int layoutDirection) {
969         if (mNavigationBarView != null) {
970             mNavigationBarView.setLayoutDirection(layoutDirection);
971         }
972 
973         if (mClearButton != null && mClearButton instanceof ImageView) {
974             // Force asset reloading
975             ((ImageView)mClearButton).setImageDrawable(null);
976             ((ImageView)mClearButton).setImageResource(R.drawable.ic_notify_clear);
977         }
978 
979         if (mSettingsButton != null) {
980             // Force asset reloading
981             mSettingsButton.setImageDrawable(null);
982             mSettingsButton.setImageResource(R.drawable.ic_notify_quicksettings);
983         }
984 
985         if (mNotificationButton != null) {
986             // Force asset reloading
987             mNotificationButton.setImageDrawable(null);
988             mNotificationButton.setImageResource(R.drawable.ic_notifications);
989         }
990 
991         refreshAllStatusBarIcons();
992     }
993 
updateShowSearchHoldoff()994     private void updateShowSearchHoldoff() {
995         mShowSearchHoldoff = mContext.getResources().getInteger(
996             R.integer.config_show_search_delay);
997     }
998 
loadNotificationShade()999     private void loadNotificationShade() {
1000         if (mPile == null) return;
1001 
1002         int N = mNotificationData.size();
1003 
1004         ArrayList<View> toShow = new ArrayList<View>();
1005 
1006         final boolean provisioned = isDeviceProvisioned();
1007         // If the device hasn't been through Setup, we only show system notifications
1008         for (int i=0; i<N; i++) {
1009             Entry ent = mNotificationData.get(N-i-1);
1010             if (!(provisioned || showNotificationEvenIfUnprovisioned(ent.notification))) continue;
1011             if (!notificationIsForCurrentUser(ent.notification)) continue;
1012             toShow.add(ent.row);
1013         }
1014 
1015         ArrayList<View> toRemove = new ArrayList<View>();
1016         for (int i=0; i<mPile.getChildCount(); i++) {
1017             View child = mPile.getChildAt(i);
1018             if (!toShow.contains(child)) {
1019                 toRemove.add(child);
1020             }
1021         }
1022 
1023         for (View remove : toRemove) {
1024             mPile.removeView(remove);
1025         }
1026 
1027         for (int i=0; i<toShow.size(); i++) {
1028             View v = toShow.get(i);
1029             if (v.getParent() == null) {
1030                 mPile.addView(v, i);
1031             }
1032         }
1033 
1034         if (mSettingsButton != null) {
1035             mSettingsButton.setEnabled(isDeviceProvisioned());
1036         }
1037     }
1038 
1039     @Override
updateNotificationIcons()1040     protected void updateNotificationIcons() {
1041         if (mNotificationIcons == null) return;
1042 
1043         loadNotificationShade();
1044 
1045         final LinearLayout.LayoutParams params
1046             = new LinearLayout.LayoutParams(mIconSize + 2*mIconHPadding, mNaturalBarHeight);
1047 
1048         int N = mNotificationData.size();
1049 
1050         if (DEBUG) {
1051             Slog.d(TAG, "refreshing icons: " + N + " notifications, mNotificationIcons=" + mNotificationIcons);
1052         }
1053 
1054         ArrayList<View> toShow = new ArrayList<View>();
1055 
1056         final boolean provisioned = isDeviceProvisioned();
1057         // If the device hasn't been through Setup, we only show system notifications
1058         for (int i=0; i<N; i++) {
1059             Entry ent = mNotificationData.get(N-i-1);
1060             if (!((provisioned && ent.notification.getScore() >= HIDE_ICONS_BELOW_SCORE)
1061                     || showNotificationEvenIfUnprovisioned(ent.notification))) continue;
1062             if (!notificationIsForCurrentUser(ent.notification)) continue;
1063             toShow.add(ent.icon);
1064         }
1065 
1066         ArrayList<View> toRemove = new ArrayList<View>();
1067         for (int i=0; i<mNotificationIcons.getChildCount(); i++) {
1068             View child = mNotificationIcons.getChildAt(i);
1069             if (!toShow.contains(child)) {
1070                 toRemove.add(child);
1071             }
1072         }
1073 
1074         for (View remove : toRemove) {
1075             mNotificationIcons.removeView(remove);
1076         }
1077 
1078         for (int i=0; i<toShow.size(); i++) {
1079             View v = toShow.get(i);
1080             if (v.getParent() == null) {
1081                 mNotificationIcons.addView(v, i, params);
1082             }
1083         }
1084     }
1085 
updateCarrierLabelVisibility(boolean force)1086     protected void updateCarrierLabelVisibility(boolean force) {
1087         if (!mShowCarrierInPanel) return;
1088         // The idea here is to only show the carrier label when there is enough room to see it,
1089         // i.e. when there aren't enough notifications to fill the panel.
1090         if (DEBUG) {
1091             Slog.d(TAG, String.format("pileh=%d scrollh=%d carrierh=%d",
1092                     mPile.getHeight(), mScrollView.getHeight(), mCarrierLabelHeight));
1093         }
1094 
1095         final boolean emergencyCallsShownElsewhere = mEmergencyCallLabel != null;
1096         final boolean makeVisible =
1097             !(emergencyCallsShownElsewhere && mNetworkController.isEmergencyOnly())
1098             && mPile.getHeight() < (mNotificationPanel.getHeight() - mCarrierLabelHeight - mNotificationHeaderHeight)
1099             && mScrollView.getVisibility() == View.VISIBLE;
1100 
1101         if (force || mCarrierLabelVisible != makeVisible) {
1102             mCarrierLabelVisible = makeVisible;
1103             if (DEBUG) {
1104                 Slog.d(TAG, "making carrier label " + (makeVisible?"visible":"invisible"));
1105             }
1106             mCarrierLabel.animate().cancel();
1107             if (makeVisible) {
1108                 mCarrierLabel.setVisibility(View.VISIBLE);
1109             }
1110             mCarrierLabel.animate()
1111                 .alpha(makeVisible ? 1f : 0f)
1112                 //.setStartDelay(makeVisible ? 500 : 0)
1113                 //.setDuration(makeVisible ? 750 : 100)
1114                 .setDuration(150)
1115                 .setListener(makeVisible ? null : new AnimatorListenerAdapter() {
1116                     @Override
1117                     public void onAnimationEnd(Animator animation) {
1118                         if (!mCarrierLabelVisible) { // race
1119                             mCarrierLabel.setVisibility(View.INVISIBLE);
1120                             mCarrierLabel.setAlpha(0f);
1121                         }
1122                     }
1123                 })
1124                 .start();
1125         }
1126     }
1127 
1128     @Override
1129     protected void setAreThereNotifications() {
1130         final boolean any = mNotificationData.size() > 0;
1131 
1132         final boolean clearable = any && mNotificationData.hasClearableItems();
1133 
1134         if (DEBUG) {
1135             Slog.d(TAG, "setAreThereNotifications: N=" + mNotificationData.size()
1136                     + " any=" + any + " clearable=" + clearable);
1137         }
1138 
1139         if (mHasFlipSettings
1140                 && mFlipSettingsView != null
1141                 && mFlipSettingsView.getVisibility() == View.VISIBLE
1142                 && mScrollView.getVisibility() != View.VISIBLE) {
1143             // the flip settings panel is unequivocally showing; we should not be shown
1144             mClearButton.setVisibility(View.INVISIBLE);
1145         } else if (mClearButton.isShown()) {
1146             if (clearable != (mClearButton.getAlpha() == 1.0f)) {
1147                 ObjectAnimator clearAnimation = ObjectAnimator.ofFloat(
1148                         mClearButton, "alpha", clearable ? 1.0f : 0.0f).setDuration(250);
1149                 clearAnimation.addListener(new AnimatorListenerAdapter() {
1150                     @Override
1151                     public void onAnimationEnd(Animator animation) {
1152                         if (mClearButton.getAlpha() <= 0.0f) {
1153                             mClearButton.setVisibility(View.INVISIBLE);
1154                         }
1155                     }
1156 
1157                     @Override
1158                     public void onAnimationStart(Animator animation) {
1159                         if (mClearButton.getAlpha() <= 0.0f) {
1160                             mClearButton.setVisibility(View.VISIBLE);
1161                         }
1162                     }
1163                 });
1164                 clearAnimation.start();
1165             }
1166         } else {
1167             mClearButton.setAlpha(clearable ? 1.0f : 0.0f);
1168             mClearButton.setVisibility(clearable ? View.VISIBLE : View.INVISIBLE);
1169         }
1170         mClearButton.setEnabled(clearable);
1171 
1172         final View nlo = mStatusBarView.findViewById(R.id.notification_lights_out);
1173         final boolean showDot = (any&&!areLightsOn());
1174         if (showDot != (nlo.getAlpha() == 1.0f)) {
1175             if (showDot) {
1176                 nlo.setAlpha(0f);
1177                 nlo.setVisibility(View.VISIBLE);
1178             }
1179             nlo.animate()
1180                 .alpha(showDot?1:0)
1181                 .setDuration(showDot?750:250)
1182                 .setInterpolator(new AccelerateInterpolator(2.0f))
1183                 .setListener(showDot ? null : new AnimatorListenerAdapter() {
1184                     @Override
1185                     public void onAnimationEnd(Animator _a) {
1186                         nlo.setVisibility(View.GONE);
1187                     }
1188                 })
1189                 .start();
1190         }
1191 
1192         updateCarrierLabelVisibility(false);
1193     }
1194 
showClock(boolean show)1195     public void showClock(boolean show) {
1196         if (mStatusBarView == null) return;
1197         View clock = mStatusBarView.findViewById(R.id.clock);
1198         if (clock != null) {
1199             clock.setVisibility(show ? View.VISIBLE : View.GONE);
1200         }
1201     }
1202 
1203     /**
1204      * State is one or more of the DISABLE constants from StatusBarManager.
1205      */
disable(int state)1206     public void disable(int state) {
1207         final int old = mDisabled;
1208         final int diff = state ^ old;
1209         mDisabled = state;
1210 
1211         if (DEBUG) {
1212             Slog.d(TAG, String.format("disable: 0x%08x -> 0x%08x (diff: 0x%08x)",
1213                 old, state, diff));
1214         }
1215 
1216         StringBuilder flagdbg = new StringBuilder();
1217         flagdbg.append("disable: < ");
1218         flagdbg.append(((state & StatusBarManager.DISABLE_EXPAND) != 0) ? "EXPAND" : "expand");
1219         flagdbg.append(((diff  & StatusBarManager.DISABLE_EXPAND) != 0) ? "* " : " ");
1220         flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "ICONS" : "icons");
1221         flagdbg.append(((diff  & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "* " : " ");
1222         flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "ALERTS" : "alerts");
1223         flagdbg.append(((diff  & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "* " : " ");
1224         flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) ? "TICKER" : "ticker");
1225         flagdbg.append(((diff  & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) ? "* " : " ");
1226         flagdbg.append(((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "SYSTEM_INFO" : "system_info");
1227         flagdbg.append(((diff  & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "* " : " ");
1228         flagdbg.append(((state & StatusBarManager.DISABLE_BACK) != 0) ? "BACK" : "back");
1229         flagdbg.append(((diff  & StatusBarManager.DISABLE_BACK) != 0) ? "* " : " ");
1230         flagdbg.append(((state & StatusBarManager.DISABLE_HOME) != 0) ? "HOME" : "home");
1231         flagdbg.append(((diff  & StatusBarManager.DISABLE_HOME) != 0) ? "* " : " ");
1232         flagdbg.append(((state & StatusBarManager.DISABLE_RECENT) != 0) ? "RECENT" : "recent");
1233         flagdbg.append(((diff  & StatusBarManager.DISABLE_RECENT) != 0) ? "* " : " ");
1234         flagdbg.append(((state & StatusBarManager.DISABLE_CLOCK) != 0) ? "CLOCK" : "clock");
1235         flagdbg.append(((diff  & StatusBarManager.DISABLE_CLOCK) != 0) ? "* " : " ");
1236         flagdbg.append(((state & StatusBarManager.DISABLE_SEARCH) != 0) ? "SEARCH" : "search");
1237         flagdbg.append(((diff  & StatusBarManager.DISABLE_SEARCH) != 0) ? "* " : " ");
1238         flagdbg.append(">");
1239         Slog.d(TAG, flagdbg.toString());
1240 
1241         if ((diff & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
1242             mSystemIconArea.animate().cancel();
1243             if ((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
1244                 mSystemIconArea.animate()
1245                     .alpha(0f)
1246                     .translationY(mNaturalBarHeight*0.5f)
1247                     .setDuration(175)
1248                     .setInterpolator(new DecelerateInterpolator(1.5f))
1249                     .setListener(mMakeIconsInvisible)
1250                     .start();
1251             } else {
1252                 mSystemIconArea.setVisibility(View.VISIBLE);
1253                 mSystemIconArea.animate()
1254                     .alpha(1f)
1255                     .translationY(0)
1256                     .setStartDelay(0)
1257                     .setInterpolator(new DecelerateInterpolator(1.5f))
1258                     .setDuration(175)
1259                     .start();
1260             }
1261         }
1262 
1263         if ((diff & StatusBarManager.DISABLE_CLOCK) != 0) {
1264             boolean show = (state & StatusBarManager.DISABLE_CLOCK) == 0;
1265             showClock(show);
1266         }
1267         if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
1268             if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {
1269                 animateCollapsePanels();
1270             }
1271         }
1272 
1273         if ((diff & (StatusBarManager.DISABLE_HOME
1274                         | StatusBarManager.DISABLE_RECENT
1275                         | StatusBarManager.DISABLE_BACK
1276                         | StatusBarManager.DISABLE_SEARCH)) != 0) {
1277             // the nav bar will take care of these
1278             if (mNavigationBarView != null) mNavigationBarView.setDisabledFlags(state);
1279 
1280             if ((state & StatusBarManager.DISABLE_RECENT) != 0) {
1281                 // close recents if it's visible
1282                 mHandler.removeMessages(MSG_CLOSE_RECENTS_PANEL);
1283                 mHandler.sendEmptyMessage(MSG_CLOSE_RECENTS_PANEL);
1284             }
1285         }
1286 
1287         if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
1288             if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
1289                 if (mTicking) {
1290                     haltTicker();
1291                 }
1292 
1293                 mNotificationIcons.animate()
1294                     .alpha(0f)
1295                     .translationY(mNaturalBarHeight*0.5f)
1296                     .setDuration(175)
1297                     .setInterpolator(new DecelerateInterpolator(1.5f))
1298                     .setListener(mMakeIconsInvisible)
1299                     .start();
1300             } else {
1301                 mNotificationIcons.setVisibility(View.VISIBLE);
1302                 mNotificationIcons.animate()
1303                     .alpha(1f)
1304                     .translationY(0)
1305                     .setStartDelay(0)
1306                     .setInterpolator(new DecelerateInterpolator(1.5f))
1307                     .setDuration(175)
1308                     .start();
1309             }
1310         } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
1311             if (mTicking && (state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
1312                 haltTicker();
1313             }
1314         }
1315     }
1316 
1317     @Override
createHandler()1318     protected BaseStatusBar.H createHandler() {
1319         return new PhoneStatusBar.H();
1320     }
1321 
1322     /**
1323      * All changes to the status bar and notifications funnel through here and are batched.
1324      */
1325     private class H extends BaseStatusBar.H {
handleMessage(Message m)1326         public void handleMessage(Message m) {
1327             super.handleMessage(m);
1328             switch (m.what) {
1329                 case MSG_OPEN_NOTIFICATION_PANEL:
1330                     animateExpandNotificationsPanel();
1331                     break;
1332                 case MSG_OPEN_SETTINGS_PANEL:
1333                     animateExpandSettingsPanel();
1334                     break;
1335                 case MSG_CLOSE_PANELS:
1336                     animateCollapsePanels();
1337                     break;
1338                 case MSG_SHOW_INTRUDER:
1339                     setIntruderAlertVisibility(true);
1340                     break;
1341                 case MSG_HIDE_INTRUDER:
1342                     setIntruderAlertVisibility(false);
1343                     mCurrentlyIntrudingNotification = null;
1344                     break;
1345             }
1346         }
1347     }
1348 
getHandler()1349     public Handler getHandler() {
1350         return mHandler;
1351     }
1352 
1353     View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() {
1354         public void onFocusChange(View v, boolean hasFocus) {
1355             // Because 'v' is a ViewGroup, all its children will be (un)selected
1356             // too, which allows marqueeing to work.
1357             v.setSelected(hasFocus);
1358         }
1359     };
1360 
makeExpandedVisible(boolean revealAfterDraw)1361     void makeExpandedVisible(boolean revealAfterDraw) {
1362         if (SPEW) Slog.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
1363         if (mExpandedVisible) {
1364             return;
1365         }
1366 
1367         mExpandedVisible = true;
1368         mPile.setLayoutTransitionsEnabled(true);
1369         if (mNavigationBarView != null)
1370             mNavigationBarView.setSlippery(true);
1371 
1372         updateCarrierLabelVisibility(true);
1373 
1374         updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
1375 
1376         // Expand the window to encompass the full screen in anticipation of the drag.
1377         // This is only possible to do atomically because the status bar is at the top of the screen!
1378         WindowManager.LayoutParams lp = (WindowManager.LayoutParams) mStatusBarWindow.getLayoutParams();
1379         lp.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
1380         lp.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
1381         lp.height = ViewGroup.LayoutParams.MATCH_PARENT;
1382         mWindowManager.updateViewLayout(mStatusBarWindow, lp);
1383 
1384         // Updating the window layout will force an expensive traversal/redraw.
1385         // Kick off the reveal animation after this is complete to avoid animation latency.
1386         if (revealAfterDraw) {
1387 //            mHandler.post(mStartRevealAnimation);
1388         }
1389 
1390         visibilityChanged(true);
1391     }
1392 
animateCollapsePanels()1393     public void animateCollapsePanels() {
1394         animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
1395     }
1396 
animateCollapsePanels(int flags)1397     public void animateCollapsePanels(int flags) {
1398         if (SPEW) {
1399             Slog.d(TAG, "animateCollapse():"
1400                     + " mExpandedVisible=" + mExpandedVisible
1401                     + " flags=" + flags);
1402         }
1403 
1404         if ((flags & CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL) == 0) {
1405             mHandler.removeMessages(MSG_CLOSE_RECENTS_PANEL);
1406             mHandler.sendEmptyMessage(MSG_CLOSE_RECENTS_PANEL);
1407         }
1408 
1409         if ((flags & CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL) == 0) {
1410             mHandler.removeMessages(MSG_CLOSE_SEARCH_PANEL);
1411             mHandler.sendEmptyMessage(MSG_CLOSE_SEARCH_PANEL);
1412         }
1413 
1414         mStatusBarWindow.cancelExpandHelper();
1415         mStatusBarView.collapseAllPanels(true);
1416     }
1417 
setVisibilityWhenDone( final ViewPropertyAnimator a, final View v, final int vis)1418     public ViewPropertyAnimator setVisibilityWhenDone(
1419             final ViewPropertyAnimator a, final View v, final int vis) {
1420         a.setListener(new AnimatorListenerAdapter() {
1421             @Override
1422             public void onAnimationEnd(Animator animation) {
1423                 v.setVisibility(vis);
1424                 a.setListener(null); // oneshot
1425             }
1426         });
1427         return a;
1428     }
1429 
setVisibilityWhenDone( final Animator a, final View v, final int vis)1430     public Animator setVisibilityWhenDone(
1431             final Animator a, final View v, final int vis) {
1432         a.addListener(new AnimatorListenerAdapter() {
1433             @Override
1434             public void onAnimationEnd(Animator animation) {
1435                 v.setVisibility(vis);
1436             }
1437         });
1438         return a;
1439     }
1440 
interpolator(TimeInterpolator ti, Animator a)1441     public Animator interpolator(TimeInterpolator ti, Animator a) {
1442         a.setInterpolator(ti);
1443         return a;
1444     }
1445 
startDelay(int d, Animator a)1446     public Animator startDelay(int d, Animator a) {
1447         a.setStartDelay(d);
1448         return a;
1449     }
1450 
start(Animator a)1451     public Animator start(Animator a) {
1452         a.start();
1453         return a;
1454     }
1455 
1456     final TimeInterpolator mAccelerateInterpolator = new AccelerateInterpolator();
1457     final TimeInterpolator mDecelerateInterpolator = new DecelerateInterpolator();
1458     final int FLIP_DURATION_OUT = 125;
1459     final int FLIP_DURATION_IN = 225;
1460     final int FLIP_DURATION = (FLIP_DURATION_IN + FLIP_DURATION_OUT);
1461 
1462     Animator mScrollViewAnim, mFlipSettingsViewAnim, mNotificationButtonAnim,
1463         mSettingsButtonAnim, mClearButtonAnim;
1464 
1465     @Override
animateExpandNotificationsPanel()1466     public void animateExpandNotificationsPanel() {
1467         if (SPEW) Slog.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible);
1468         if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
1469             return ;
1470         }
1471 
1472         mNotificationPanel.expand();
1473         if (mHasFlipSettings && mScrollView.getVisibility() != View.VISIBLE) {
1474             flipToNotifications();
1475         }
1476 
1477         if (false) postStartTracing();
1478     }
1479 
flipToNotifications()1480     public void flipToNotifications() {
1481         if (mFlipSettingsViewAnim != null) mFlipSettingsViewAnim.cancel();
1482         if (mScrollViewAnim != null) mScrollViewAnim.cancel();
1483         if (mSettingsButtonAnim != null) mSettingsButtonAnim.cancel();
1484         if (mNotificationButtonAnim != null) mNotificationButtonAnim.cancel();
1485         if (mClearButtonAnim != null) mClearButtonAnim.cancel();
1486 
1487         mScrollView.setVisibility(View.VISIBLE);
1488         mScrollViewAnim = start(
1489             startDelay(FLIP_DURATION_OUT,
1490                 interpolator(mDecelerateInterpolator,
1491                     ObjectAnimator.ofFloat(mScrollView, View.SCALE_X, 0f, 1f)
1492                         .setDuration(FLIP_DURATION_IN)
1493                     )));
1494         mFlipSettingsViewAnim = start(
1495             setVisibilityWhenDone(
1496                 interpolator(mAccelerateInterpolator,
1497                         ObjectAnimator.ofFloat(mFlipSettingsView, View.SCALE_X, 1f, 0f)
1498                         )
1499                     .setDuration(FLIP_DURATION_OUT),
1500                 mFlipSettingsView, View.INVISIBLE));
1501         mNotificationButtonAnim = start(
1502             setVisibilityWhenDone(
1503                 ObjectAnimator.ofFloat(mNotificationButton, View.ALPHA, 0f)
1504                     .setDuration(FLIP_DURATION),
1505                 mNotificationButton, View.INVISIBLE));
1506         mSettingsButton.setVisibility(View.VISIBLE);
1507         mSettingsButtonAnim = start(
1508             ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA, 1f)
1509                 .setDuration(FLIP_DURATION));
1510         mClearButton.setVisibility(View.VISIBLE);
1511         mClearButton.setAlpha(0f);
1512         setAreThereNotifications(); // this will show/hide the button as necessary
1513         mNotificationPanel.postDelayed(new Runnable() {
1514             public void run() {
1515                 updateCarrierLabelVisibility(false);
1516             }
1517         }, FLIP_DURATION - 150);
1518     }
1519 
1520     @Override
animateExpandSettingsPanel()1521     public void animateExpandSettingsPanel() {
1522         if (SPEW) Slog.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible);
1523         if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
1524             return;
1525         }
1526 
1527         // Settings are not available in setup
1528         if (!mUserSetup) return;
1529 
1530         if (mHasFlipSettings) {
1531             mNotificationPanel.expand();
1532             if (mFlipSettingsView.getVisibility() != View.VISIBLE) {
1533                 flipToSettings();
1534             }
1535         } else if (mSettingsPanel != null) {
1536             mSettingsPanel.expand();
1537         }
1538 
1539         if (false) postStartTracing();
1540     }
1541 
switchToSettings()1542     public void switchToSettings() {
1543         // Settings are not available in setup
1544         if (!mUserSetup) return;
1545 
1546         mFlipSettingsView.setScaleX(1f);
1547         mFlipSettingsView.setVisibility(View.VISIBLE);
1548         mSettingsButton.setVisibility(View.GONE);
1549         mScrollView.setVisibility(View.GONE);
1550         mScrollView.setScaleX(0f);
1551         mNotificationButton.setVisibility(View.VISIBLE);
1552         mNotificationButton.setAlpha(1f);
1553         mClearButton.setVisibility(View.GONE);
1554     }
1555 
flipToSettings()1556     public void flipToSettings() {
1557         // Settings are not available in setup
1558         if (!mUserSetup) return;
1559 
1560         if (mFlipSettingsViewAnim != null) mFlipSettingsViewAnim.cancel();
1561         if (mScrollViewAnim != null) mScrollViewAnim.cancel();
1562         if (mSettingsButtonAnim != null) mSettingsButtonAnim.cancel();
1563         if (mNotificationButtonAnim != null) mNotificationButtonAnim.cancel();
1564         if (mClearButtonAnim != null) mClearButtonAnim.cancel();
1565 
1566         mFlipSettingsView.setVisibility(View.VISIBLE);
1567         mFlipSettingsView.setScaleX(0f);
1568         mFlipSettingsViewAnim = start(
1569             startDelay(FLIP_DURATION_OUT,
1570                 interpolator(mDecelerateInterpolator,
1571                     ObjectAnimator.ofFloat(mFlipSettingsView, View.SCALE_X, 0f, 1f)
1572                         .setDuration(FLIP_DURATION_IN)
1573                     )));
1574         mScrollViewAnim = start(
1575             setVisibilityWhenDone(
1576                 interpolator(mAccelerateInterpolator,
1577                         ObjectAnimator.ofFloat(mScrollView, View.SCALE_X, 1f, 0f)
1578                         )
1579                     .setDuration(FLIP_DURATION_OUT),
1580                 mScrollView, View.INVISIBLE));
1581         mSettingsButtonAnim = start(
1582             setVisibilityWhenDone(
1583                 ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA, 0f)
1584                     .setDuration(FLIP_DURATION),
1585                     mScrollView, View.INVISIBLE));
1586         mNotificationButton.setVisibility(View.VISIBLE);
1587         mNotificationButtonAnim = start(
1588             ObjectAnimator.ofFloat(mNotificationButton, View.ALPHA, 1f)
1589                 .setDuration(FLIP_DURATION));
1590         mClearButtonAnim = start(
1591             setVisibilityWhenDone(
1592                 ObjectAnimator.ofFloat(mClearButton, View.ALPHA, 0f)
1593                 .setDuration(FLIP_DURATION),
1594                 mClearButton, View.INVISIBLE));
1595         mNotificationPanel.postDelayed(new Runnable() {
1596             public void run() {
1597                 updateCarrierLabelVisibility(false);
1598             }
1599         }, FLIP_DURATION - 150);
1600     }
1601 
flipPanels()1602     public void flipPanels() {
1603         if (mHasFlipSettings) {
1604             if (mFlipSettingsView.getVisibility() != View.VISIBLE) {
1605                 flipToSettings();
1606             } else {
1607                 flipToNotifications();
1608             }
1609         }
1610     }
1611 
animateCollapseQuickSettings()1612     public void animateCollapseQuickSettings() {
1613         mStatusBarView.collapseAllPanels(true);
1614     }
1615 
makeExpandedInvisibleSoon()1616     void makeExpandedInvisibleSoon() {
1617         mHandler.postDelayed(new Runnable() { public void run() { makeExpandedInvisible(); }}, 50);
1618     }
1619 
makeExpandedInvisible()1620     void makeExpandedInvisible() {
1621         if (SPEW) Slog.d(TAG, "makeExpandedInvisible: mExpandedVisible=" + mExpandedVisible
1622                 + " mExpandedVisible=" + mExpandedVisible);
1623 
1624         if (!mExpandedVisible) {
1625             return;
1626         }
1627 
1628         // Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868)
1629         mStatusBarView.collapseAllPanels(/*animate=*/ false);
1630 
1631         if (mHasFlipSettings) {
1632             // reset things to their proper state
1633             if (mFlipSettingsViewAnim != null) mFlipSettingsViewAnim.cancel();
1634             if (mScrollViewAnim != null) mScrollViewAnim.cancel();
1635             if (mSettingsButtonAnim != null) mSettingsButtonAnim.cancel();
1636             if (mNotificationButtonAnim != null) mNotificationButtonAnim.cancel();
1637             if (mClearButtonAnim != null) mClearButtonAnim.cancel();
1638 
1639             mScrollView.setScaleX(1f);
1640             mScrollView.setVisibility(View.VISIBLE);
1641             mSettingsButton.setAlpha(1f);
1642             mSettingsButton.setVisibility(View.VISIBLE);
1643             mNotificationPanel.setVisibility(View.GONE);
1644             mFlipSettingsView.setVisibility(View.GONE);
1645             mNotificationButton.setVisibility(View.GONE);
1646             setAreThereNotifications(); // show the clear button
1647         }
1648 
1649         mExpandedVisible = false;
1650         mPile.setLayoutTransitionsEnabled(false);
1651         if (mNavigationBarView != null)
1652             mNavigationBarView.setSlippery(false);
1653         visibilityChanged(false);
1654 
1655         // Shrink the window to the size of the status bar only
1656         WindowManager.LayoutParams lp = (WindowManager.LayoutParams) mStatusBarWindow.getLayoutParams();
1657         lp.height = getStatusBarHeight();
1658         lp.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
1659         lp.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
1660         mWindowManager.updateViewLayout(mStatusBarWindow, lp);
1661 
1662         if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) {
1663             setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
1664         }
1665 
1666         // Close any "App info" popups that might have snuck on-screen
1667         dismissPopups();
1668 
1669         if (mPostCollapseCleanup != null) {
1670             mPostCollapseCleanup.run();
1671             mPostCollapseCleanup = null;
1672         }
1673     }
1674 
1675     /**
1676      * Enables or disables layers on the children of the notifications pile.
1677      *
1678      * When layers are enabled, this method attempts to enable layers for the minimal
1679      * number of children. Only children visible when the notification area is fully
1680      * expanded will receive a layer. The technique used in this method might cause
1681      * more children than necessary to get a layer (at most one extra child with the
1682      * current UI.)
1683      *
1684      * @param layerType {@link View#LAYER_TYPE_NONE} or {@link View#LAYER_TYPE_HARDWARE}
1685      */
setPileLayers(int layerType)1686     private void setPileLayers(int layerType) {
1687         final int count = mPile.getChildCount();
1688 
1689         switch (layerType) {
1690             case View.LAYER_TYPE_NONE:
1691                 for (int i = 0; i < count; i++) {
1692                     mPile.getChildAt(i).setLayerType(layerType, null);
1693                 }
1694                 break;
1695             case View.LAYER_TYPE_HARDWARE:
1696                 final int[] location = new int[2];
1697                 mNotificationPanel.getLocationInWindow(location);
1698 
1699                 final int left = location[0];
1700                 final int top = location[1];
1701                 final int right = left + mNotificationPanel.getWidth();
1702                 final int bottom = top + getExpandedViewMaxHeight();
1703 
1704                 final Rect childBounds = new Rect();
1705 
1706                 for (int i = 0; i < count; i++) {
1707                     final View view = mPile.getChildAt(i);
1708                     view.getLocationInWindow(location);
1709 
1710                     childBounds.set(location[0], location[1],
1711                             location[0] + view.getWidth(), location[1] + view.getHeight());
1712 
1713                     if (childBounds.intersects(left, top, right, bottom)) {
1714                         view.setLayerType(layerType, null);
1715                     }
1716                 }
1717 
1718                 break;
1719         }
1720     }
1721 
isClinging()1722     public boolean isClinging() {
1723         return mCling != null && mCling.getVisibility() == View.VISIBLE;
1724     }
1725 
hideCling()1726     public void hideCling() {
1727         if (isClinging()) {
1728             mCling.animate().alpha(0f).setDuration(250).start();
1729             mCling.setVisibility(View.GONE);
1730             mSuppressStatusBarDrags = false;
1731         }
1732     }
1733 
showCling()1734     public void showCling() {
1735         // lazily inflate this to accommodate orientation change
1736         final ViewStub stub = (ViewStub) mStatusBarWindow.findViewById(R.id.status_bar_cling_stub);
1737         if (stub == null) {
1738             mClingShown = true;
1739             return; // no clings on this device
1740         }
1741 
1742         mSuppressStatusBarDrags = true;
1743 
1744         mHandler.postDelayed(new Runnable() {
1745             @Override
1746             public void run() {
1747                 mCling = (ViewGroup) stub.inflate();
1748 
1749                 mCling.setOnTouchListener(new View.OnTouchListener() {
1750                     @Override
1751                     public boolean onTouch(View v, MotionEvent event) {
1752                         return true; // e eats everything
1753                     }});
1754                 mCling.findViewById(R.id.ok).setOnClickListener(new View.OnClickListener() {
1755                     @Override
1756                     public void onClick(View v) {
1757                         hideCling();
1758                     }});
1759 
1760                 mCling.setAlpha(0f);
1761                 mCling.setVisibility(View.VISIBLE);
1762                 mCling.animate().alpha(1f);
1763 
1764                 mClingShown = true;
1765                 SharedPreferences.Editor editor = Prefs.edit(mContext);
1766                 editor.putBoolean(Prefs.SHOWN_QUICK_SETTINGS_HELP, true);
1767                 editor.apply();
1768 
1769                 makeExpandedVisible(true); // enforce visibility in case the shade is still animating closed
1770                 animateExpandNotificationsPanel();
1771 
1772                 mSuppressStatusBarDrags = false;
1773             }
1774         }, 500);
1775 
1776         animateExpandNotificationsPanel();
1777     }
1778 
interceptTouchEvent(MotionEvent event)1779     public boolean interceptTouchEvent(MotionEvent event) {
1780         if (DEBUG_GESTURES) {
1781             if (event.getActionMasked() != MotionEvent.ACTION_MOVE) {
1782                 EventLog.writeEvent(EventLogTags.SYSUI_STATUSBAR_TOUCH,
1783                         event.getActionMasked(), (int) event.getX(), (int) event.getY(), mDisabled);
1784             }
1785 
1786         }
1787 
1788         if (SPEW) {
1789             Slog.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled="
1790                 + mDisabled + " mTracking=" + mTracking);
1791         } else if (CHATTY) {
1792             if (event.getAction() != MotionEvent.ACTION_MOVE) {
1793                 Slog.d(TAG, String.format(
1794                             "panel: %s at (%f, %f) mDisabled=0x%08x",
1795                             MotionEvent.actionToString(event.getAction()),
1796                             event.getRawX(), event.getRawY(), mDisabled));
1797             }
1798         }
1799 
1800         if (DEBUG_GESTURES) {
1801             mGestureRec.add(event);
1802         }
1803 
1804         // Cling (first-run help) handling.
1805         // The cling is supposed to show the first time you drag, or even tap, the status bar.
1806         // It should show the notification panel, then fade in after half a second, giving you
1807         // an explanation of what just happened, as well as teach you how to access quick
1808         // settings (another drag). The user can dismiss the cling by clicking OK or by
1809         // dragging quick settings into view.
1810         final int act = event.getActionMasked();
1811         if (mSuppressStatusBarDrags) {
1812             return true;
1813         } else if (act == MotionEvent.ACTION_UP && !mClingShown) {
1814             showCling();
1815         } else {
1816             hideCling();
1817         }
1818 
1819         return false;
1820     }
1821 
getGestureRecorder()1822     public GestureRecorder getGestureRecorder() {
1823         return mGestureRec;
1824     }
1825 
1826     @Override // CommandQueue
setNavigationIconHints(int hints)1827     public void setNavigationIconHints(int hints) {
1828         if (hints == mNavigationIconHints) return;
1829 
1830         mNavigationIconHints = hints;
1831 
1832         if (mNavigationBarView != null) {
1833             mNavigationBarView.setNavigationIconHints(hints);
1834         }
1835     }
1836 
1837     @Override // CommandQueue
setSystemUiVisibility(int vis, int mask)1838     public void setSystemUiVisibility(int vis, int mask) {
1839         final int oldVal = mSystemUiVisibility;
1840         final int newVal = (oldVal&~mask) | (vis&mask);
1841         final int diff = newVal ^ oldVal;
1842 
1843         if (diff != 0) {
1844             mSystemUiVisibility = newVal;
1845 
1846             if (0 != (diff & View.SYSTEM_UI_FLAG_LOW_PROFILE)) {
1847                 final boolean lightsOut = (0 != (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE));
1848                 if (lightsOut) {
1849                     animateCollapsePanels();
1850                     if (mTicking) {
1851                         haltTicker();
1852                     }
1853                 }
1854 
1855                 if (mNavigationBarView != null) {
1856                     mNavigationBarView.setLowProfile(lightsOut);
1857                 }
1858 
1859                 setStatusBarLowProfile(lightsOut);
1860             }
1861 
1862             notifyUiVisibilityChanged();
1863         }
1864     }
1865 
setStatusBarLowProfile(boolean lightsOut)1866     private void setStatusBarLowProfile(boolean lightsOut) {
1867         if (mLightsOutAnimation == null) {
1868             final View notifications = mStatusBarView.findViewById(R.id.notification_icon_area);
1869             final View systemIcons = mStatusBarView.findViewById(R.id.statusIcons);
1870             final View signal = mStatusBarView.findViewById(R.id.signal_cluster);
1871             final View battery = mStatusBarView.findViewById(R.id.battery);
1872             final View clock = mStatusBarView.findViewById(R.id.clock);
1873 
1874             final AnimatorSet lightsOutAnim = new AnimatorSet();
1875             lightsOutAnim.playTogether(
1876                     ObjectAnimator.ofFloat(notifications, View.ALPHA, 0),
1877                     ObjectAnimator.ofFloat(systemIcons, View.ALPHA, 0),
1878                     ObjectAnimator.ofFloat(signal, View.ALPHA, 0),
1879                     ObjectAnimator.ofFloat(battery, View.ALPHA, 0.5f),
1880                     ObjectAnimator.ofFloat(clock, View.ALPHA, 0.5f)
1881                 );
1882             lightsOutAnim.setDuration(750);
1883 
1884             final AnimatorSet lightsOnAnim = new AnimatorSet();
1885             lightsOnAnim.playTogether(
1886                     ObjectAnimator.ofFloat(notifications, View.ALPHA, 1),
1887                     ObjectAnimator.ofFloat(systemIcons, View.ALPHA, 1),
1888                     ObjectAnimator.ofFloat(signal, View.ALPHA, 1),
1889                     ObjectAnimator.ofFloat(battery, View.ALPHA, 1),
1890                     ObjectAnimator.ofFloat(clock, View.ALPHA, 1)
1891                 );
1892             lightsOnAnim.setDuration(250);
1893 
1894             mLightsOutAnimation = lightsOutAnim;
1895             mLightsOnAnimation = lightsOnAnim;
1896         }
1897 
1898         mLightsOutAnimation.cancel();
1899         mLightsOnAnimation.cancel();
1900 
1901         final Animator a = lightsOut ? mLightsOutAnimation : mLightsOnAnimation;
1902         a.start();
1903 
1904         setAreThereNotifications();
1905     }
1906 
areLightsOn()1907     private boolean areLightsOn() {
1908         return 0 == (mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE);
1909     }
1910 
setLightsOn(boolean on)1911     public void setLightsOn(boolean on) {
1912         Log.v(TAG, "setLightsOn(" + on + ")");
1913         if (on) {
1914             setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE);
1915         } else {
1916             setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE, View.SYSTEM_UI_FLAG_LOW_PROFILE);
1917         }
1918     }
1919 
notifyUiVisibilityChanged()1920     private void notifyUiVisibilityChanged() {
1921         try {
1922             mWindowManagerService.statusBarVisibilityChanged(mSystemUiVisibility);
1923         } catch (RemoteException ex) {
1924         }
1925     }
1926 
topAppWindowChanged(boolean showMenu)1927     public void topAppWindowChanged(boolean showMenu) {
1928         if (DEBUG) {
1929             Slog.d(TAG, (showMenu?"showing":"hiding") + " the MENU button");
1930         }
1931         if (mNavigationBarView != null) {
1932             mNavigationBarView.setMenuVisibility(showMenu);
1933         }
1934 
1935         // See above re: lights-out policy for legacy apps.
1936         if (showMenu) setLightsOn(true);
1937     }
1938 
1939     @Override
setImeWindowStatus(IBinder token, int vis, int backDisposition)1940     public void setImeWindowStatus(IBinder token, int vis, int backDisposition) {
1941         boolean altBack = (backDisposition == InputMethodService.BACK_DISPOSITION_WILL_DISMISS)
1942             || ((vis & InputMethodService.IME_VISIBLE) != 0);
1943 
1944         mCommandQueue.setNavigationIconHints(
1945                 altBack ? (mNavigationIconHints | StatusBarManager.NAVIGATION_HINT_BACK_ALT)
1946                         : (mNavigationIconHints & ~StatusBarManager.NAVIGATION_HINT_BACK_ALT));
1947         if (mQS != null) mQS.setImeWindowStatus(vis > 0);
1948     }
1949 
1950     @Override
setHardKeyboardStatus(boolean available, boolean enabled)1951     public void setHardKeyboardStatus(boolean available, boolean enabled) {}
1952 
1953     @Override
tick(IBinder key, StatusBarNotification n, boolean firstTime)1954     protected void tick(IBinder key, StatusBarNotification n, boolean firstTime) {
1955         // no ticking in lights-out mode
1956         if (!areLightsOn()) return;
1957 
1958         // no ticking in Setup
1959         if (!isDeviceProvisioned()) return;
1960 
1961         // not for you
1962         if (!notificationIsForCurrentUser(n)) return;
1963 
1964         // Show the ticker if one is requested. Also don't do this
1965         // until status bar window is attached to the window manager,
1966         // because...  well, what's the point otherwise?  And trying to
1967         // run a ticker without being attached will crash!
1968         if (n.getNotification().tickerText != null && mStatusBarWindow.getWindowToken() != null) {
1969             if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS
1970                             | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
1971                 mTicker.addEntry(n);
1972             }
1973         }
1974     }
1975 
1976     private class MyTicker extends Ticker {
MyTicker(Context context, View sb)1977         MyTicker(Context context, View sb) {
1978             super(context, sb);
1979         }
1980 
1981         @Override
tickerStarting()1982         public void tickerStarting() {
1983             mTicking = true;
1984             mStatusBarContents.setVisibility(View.GONE);
1985             mTickerView.setVisibility(View.VISIBLE);
1986             mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null));
1987             mStatusBarContents.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null));
1988         }
1989 
1990         @Override
tickerDone()1991         public void tickerDone() {
1992             mStatusBarContents.setVisibility(View.VISIBLE);
1993             mTickerView.setVisibility(View.GONE);
1994             mStatusBarContents.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null));
1995             mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out,
1996                         mTickingDoneListener));
1997         }
1998 
tickerHalting()1999         public void tickerHalting() {
2000             mStatusBarContents.setVisibility(View.VISIBLE);
2001             mTickerView.setVisibility(View.GONE);
2002             mStatusBarContents.startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
2003             // we do not animate the ticker away at this point, just get rid of it (b/6992707)
2004         }
2005     }
2006 
2007     Animation.AnimationListener mTickingDoneListener = new Animation.AnimationListener() {;
2008         public void onAnimationEnd(Animation animation) {
2009             mTicking = false;
2010         }
2011         public void onAnimationRepeat(Animation animation) {
2012         }
2013         public void onAnimationStart(Animation animation) {
2014         }
2015     };
2016 
loadAnim(int id, Animation.AnimationListener listener)2017     private Animation loadAnim(int id, Animation.AnimationListener listener) {
2018         Animation anim = AnimationUtils.loadAnimation(mContext, id);
2019         if (listener != null) {
2020             anim.setAnimationListener(listener);
2021         }
2022         return anim;
2023     }
2024 
viewInfo(View v)2025     public static String viewInfo(View v) {
2026         return "[(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom()
2027                 + ") " + v.getWidth() + "x" + v.getHeight() + "]";
2028     }
2029 
dump(FileDescriptor fd, PrintWriter pw, String[] args)2030     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2031         synchronized (mQueueLock) {
2032             pw.println("Current Status Bar state:");
2033             pw.println("  mExpandedVisible=" + mExpandedVisible
2034                     + ", mTrackingPosition=" + mTrackingPosition);
2035             pw.println("  mTicking=" + mTicking);
2036             pw.println("  mTracking=" + mTracking);
2037             pw.println("  mDisplayMetrics=" + mDisplayMetrics);
2038             pw.println("  mPile: " + viewInfo(mPile));
2039             pw.println("  mTickerView: " + viewInfo(mTickerView));
2040             pw.println("  mScrollView: " + viewInfo(mScrollView)
2041                     + " scroll " + mScrollView.getScrollX() + "," + mScrollView.getScrollY());
2042         }
2043 
2044         pw.print("  mNavigationBarView=");
2045         if (mNavigationBarView == null) {
2046             pw.println("null");
2047         } else {
2048             mNavigationBarView.dump(fd, pw, args);
2049         }
2050 
2051         pw.println("  Panels: ");
2052         if (mNotificationPanel != null) {
2053             pw.println("    mNotificationPanel=" +
2054                 mNotificationPanel + " params=" + mNotificationPanel.getLayoutParams().debug(""));
2055             pw.print  ("      ");
2056             mNotificationPanel.dump(fd, pw, args);
2057         }
2058         if (mSettingsPanel != null) {
2059             pw.println("    mSettingsPanel=" +
2060                 mSettingsPanel + " params=" + mSettingsPanel.getLayoutParams().debug(""));
2061             pw.print  ("      ");
2062             mSettingsPanel.dump(fd, pw, args);
2063         }
2064 
2065         if (DUMPTRUCK) {
2066             synchronized (mNotificationData) {
2067                 int N = mNotificationData.size();
2068                 pw.println("  notification icons: " + N);
2069                 for (int i=0; i<N; i++) {
2070                     NotificationData.Entry e = mNotificationData.get(i);
2071                     pw.println("    [" + i + "] key=" + e.key + " icon=" + e.icon);
2072                     StatusBarNotification n = e.notification;
2073                     pw.println("         pkg=" + n.getPackageName() + " id=" + n.getId() + " score=" + n.getScore());
2074                     pw.println("         notification=" + n.getNotification());
2075                     pw.println("         tickerText=\"" + n.getNotification().tickerText + "\"");
2076                 }
2077             }
2078 
2079             int N = mStatusIcons.getChildCount();
2080             pw.println("  system icons: " + N);
2081             for (int i=0; i<N; i++) {
2082                 StatusBarIconView ic = (StatusBarIconView) mStatusIcons.getChildAt(i);
2083                 pw.println("    [" + i + "] icon=" + ic);
2084             }
2085 
2086             if (false) {
2087                 pw.println("see the logcat for a dump of the views we have created.");
2088                 // must happen on ui thread
2089                 mHandler.post(new Runnable() {
2090                         public void run() {
2091                             mStatusBarView.getLocationOnScreen(mAbsPos);
2092                             Slog.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
2093                                     + ") " + mStatusBarView.getWidth() + "x"
2094                                     + getStatusBarHeight());
2095                             mStatusBarView.debug();
2096                         }
2097                     });
2098             }
2099         }
2100 
2101         if (DEBUG_GESTURES) {
2102             pw.print("  status bar gestures: ");
2103             mGestureRec.dump(fd, pw, args);
2104         }
2105 
2106         mNetworkController.dump(fd, pw, args);
2107     }
2108 
2109     @Override
createAndAddWindows()2110     public void createAndAddWindows() {
2111         addStatusBarWindow();
2112     }
2113 
addStatusBarWindow()2114     private void addStatusBarWindow() {
2115         // Put up the view
2116         final int height = getStatusBarHeight();
2117 
2118         // Now that the status bar window encompasses the sliding panel and its
2119         // translucent backdrop, the entire thing is made TRANSLUCENT and is
2120         // hardware-accelerated.
2121         final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
2122                 ViewGroup.LayoutParams.MATCH_PARENT,
2123                 height,
2124                 WindowManager.LayoutParams.TYPE_STATUS_BAR,
2125                 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
2126                     | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
2127                     | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
2128                 PixelFormat.TRANSLUCENT);
2129 
2130         lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
2131 
2132         lp.gravity = getStatusBarGravity();
2133         lp.setTitle("StatusBar");
2134         lp.packageName = mContext.getPackageName();
2135 
2136         makeStatusBarView();
2137         mWindowManager.addView(mStatusBarWindow, lp);
2138     }
2139 
setNotificationIconVisibility(boolean visible, int anim)2140     void setNotificationIconVisibility(boolean visible, int anim) {
2141         int old = mNotificationIcons.getVisibility();
2142         int v = visible ? View.VISIBLE : View.INVISIBLE;
2143         if (old != v) {
2144             mNotificationIcons.setVisibility(v);
2145             mNotificationIcons.startAnimation(loadAnim(anim, null));
2146         }
2147     }
2148 
updateExpandedInvisiblePosition()2149     void updateExpandedInvisiblePosition() {
2150         mTrackingPosition = -mDisplayMetrics.heightPixels;
2151     }
2152 
saturate(float a)2153     static final float saturate(float a) {
2154         return a < 0f ? 0f : (a > 1f ? 1f : a);
2155     }
2156 
2157     @Override
getExpandedViewMaxHeight()2158     protected int getExpandedViewMaxHeight() {
2159         return mDisplayMetrics.heightPixels - mNotificationPanelMarginBottomPx;
2160     }
2161 
2162     @Override
updateExpandedViewPos(int thingy)2163     public void updateExpandedViewPos(int thingy) {
2164         if (DEBUG) Slog.v(TAG, "updateExpandedViewPos");
2165 
2166         // on larger devices, the notification panel is propped open a bit
2167         mNotificationPanel.setMinimumHeight(
2168                 (int)(mNotificationPanelMinHeightFrac * mCurrentDisplaySize.y));
2169 
2170         FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mNotificationPanel.getLayoutParams();
2171         lp.gravity = mNotificationPanelGravity;
2172         lp.setMarginStart(mNotificationPanelMarginPx);
2173         mNotificationPanel.setLayoutParams(lp);
2174 
2175         if (mSettingsPanel != null) {
2176             lp = (FrameLayout.LayoutParams) mSettingsPanel.getLayoutParams();
2177             lp.gravity = mSettingsPanelGravity;
2178             lp.setMarginEnd(mNotificationPanelMarginPx);
2179             mSettingsPanel.setLayoutParams(lp);
2180         }
2181 
2182         updateCarrierLabelVisibility(false);
2183     }
2184 
2185     // called by makeStatusbar and also by PhoneStatusBarView
updateDisplaySize()2186     void updateDisplaySize() {
2187         mDisplay.getMetrics(mDisplayMetrics);
2188         if (DEBUG_GESTURES) {
2189             mGestureRec.tag("display",
2190                     String.format("%dx%d", mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
2191         }
2192     }
2193 
2194     private View.OnClickListener mClearButtonListener = new View.OnClickListener() {
2195         public void onClick(View v) {
2196             synchronized (mNotificationData) {
2197                 // animate-swipe all dismissable notifications, then animate the shade closed
2198                 int numChildren = mPile.getChildCount();
2199 
2200                 int scrollTop = mScrollView.getScrollY();
2201                 int scrollBottom = scrollTop + mScrollView.getHeight();
2202                 final ArrayList<View> snapshot = new ArrayList<View>(numChildren);
2203                 for (int i=0; i<numChildren; i++) {
2204                     final View child = mPile.getChildAt(i);
2205                     if (mPile.canChildBeDismissed(child) && child.getBottom() > scrollTop &&
2206                             child.getTop() < scrollBottom) {
2207                         snapshot.add(child);
2208                     }
2209                 }
2210                 if (snapshot.isEmpty()) {
2211                     animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
2212                     return;
2213                 }
2214                 new Thread(new Runnable() {
2215                     @Override
2216                     public void run() {
2217                         // Decrease the delay for every row we animate to give the sense of
2218                         // accelerating the swipes
2219                         final int ROW_DELAY_DECREMENT = 10;
2220                         int currentDelay = 140;
2221                         int totalDelay = 0;
2222 
2223                         // Set the shade-animating state to avoid doing other work during
2224                         // all of these animations. In particular, avoid layout and
2225                         // redrawing when collapsing the shade.
2226                         mPile.setViewRemoval(false);
2227 
2228                         mPostCollapseCleanup = new Runnable() {
2229                             @Override
2230                             public void run() {
2231                                 if (DEBUG) {
2232                                     Slog.v(TAG, "running post-collapse cleanup");
2233                                 }
2234                                 try {
2235                                     mPile.setViewRemoval(true);
2236                                     mBarService.onClearAllNotifications();
2237                                 } catch (Exception ex) { }
2238                             }
2239                         };
2240 
2241                         View sampleView = snapshot.get(0);
2242                         int width = sampleView.getWidth();
2243                         final int dir = sampleView.isLayoutRtl() ? -1 : +1;
2244                         final int velocity = dir * width * 8; // 1000/8 = 125 ms duration
2245                         for (final View _v : snapshot) {
2246                             mHandler.postDelayed(new Runnable() {
2247                                 @Override
2248                                 public void run() {
2249                                     mPile.dismissRowAnimated(_v, velocity);
2250                                 }
2251                             }, totalDelay);
2252                             currentDelay = Math.max(50, currentDelay - ROW_DELAY_DECREMENT);
2253                             totalDelay += currentDelay;
2254                         }
2255                         // Delay the collapse animation until after all swipe animations have
2256                         // finished. Provide some buffer because there may be some extra delay
2257                         // before actually starting each swipe animation. Ideally, we'd
2258                         // synchronize the end of those animations with the start of the collaps
2259                         // exactly.
2260                         mHandler.postDelayed(new Runnable() {
2261                             @Override
2262                             public void run() {
2263                                 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
2264                             }
2265                         }, totalDelay + 225);
2266                     }
2267                 }).start();
2268             }
2269         }
2270     };
2271 
startActivityDismissingKeyguard(Intent intent, boolean onlyProvisioned)2272     public void startActivityDismissingKeyguard(Intent intent, boolean onlyProvisioned) {
2273         if (onlyProvisioned && !isDeviceProvisioned()) return;
2274         try {
2275             // Dismiss the lock screen when Settings starts.
2276             ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
2277         } catch (RemoteException e) {
2278         }
2279         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
2280         mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
2281         animateCollapsePanels();
2282     }
2283 
2284     private View.OnClickListener mSettingsButtonListener = new View.OnClickListener() {
2285         public void onClick(View v) {
2286             if (mHasSettingsPanel) {
2287                 animateExpandSettingsPanel();
2288             } else {
2289                 startActivityDismissingKeyguard(
2290                         new Intent(android.provider.Settings.ACTION_SETTINGS), true);
2291             }
2292         }
2293     };
2294 
2295     private View.OnClickListener mClockClickListener = new View.OnClickListener() {
2296         public void onClick(View v) {
2297             startActivityDismissingKeyguard(
2298                     new Intent(Intent.ACTION_QUICK_CLOCK), true); // have fun, everyone
2299         }
2300     };
2301 
2302     private View.OnClickListener mNotificationButtonListener = new View.OnClickListener() {
2303         public void onClick(View v) {
2304             animateExpandNotificationsPanel();
2305         }
2306     };
2307 
2308     private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
2309         public void onReceive(Context context, Intent intent) {
2310             if (DEBUG) Slog.v(TAG, "onReceive: " + intent);
2311             String action = intent.getAction();
2312             if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
2313                 int flags = CommandQueue.FLAG_EXCLUDE_NONE;
2314                 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
2315                     String reason = intent.getStringExtra("reason");
2316                     if (reason != null && reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) {
2317                         flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL;
2318                     }
2319                 }
2320                 animateCollapsePanels(flags);
2321             }
2322             else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
2323                 // no waiting!
2324                 makeExpandedInvisible();
2325                 notifyNavigationBarScreenOn(false);
2326             }
2327             else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
2328                 if (DEBUG) {
2329                     Slog.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration());
2330                 }
2331                 mDisplay.getSize(mCurrentDisplaySize);
2332 
2333                 updateResources();
2334                 repositionNavigationBar();
2335                 updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
2336                 updateShowSearchHoldoff();
2337             }
2338             else if (Intent.ACTION_SCREEN_ON.equals(action)) {
2339                 // work around problem where mDisplay.getRotation() is not stable while screen is off (bug 7086018)
2340                 repositionNavigationBar();
2341                 notifyNavigationBarScreenOn(true);
2342             }
2343         }
2344     };
2345 
2346     @Override
userSwitched(int newUserId)2347     public void userSwitched(int newUserId) {
2348         if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId);
2349         animateCollapsePanels();
2350         updateNotificationIcons();
2351         resetUserSetupObserver();
2352     }
2353 
resetUserSetupObserver()2354     private void resetUserSetupObserver() {
2355         mContext.getContentResolver().unregisterContentObserver(mUserSetupObserver);
2356         mUserSetupObserver.onChange(false);
2357         mContext.getContentResolver().registerContentObserver(
2358                 Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), true,
2359                 mUserSetupObserver,
2360                 mCurrentUserId);
2361     }
2362 
setIntruderAlertVisibility(boolean vis)2363     private void setIntruderAlertVisibility(boolean vis) {
2364         if (!ENABLE_INTRUDERS) return;
2365         if (DEBUG) {
2366             Slog.v(TAG, (vis ? "showing" : "hiding") + " intruder alert window");
2367         }
2368         mIntruderAlertView.setVisibility(vis ? View.VISIBLE : View.GONE);
2369     }
2370 
dismissIntruder()2371     public void dismissIntruder() {
2372         if (mCurrentlyIntrudingNotification == null) return;
2373 
2374         try {
2375             mBarService.onNotificationClear(
2376                     mCurrentlyIntrudingNotification.getPackageName(),
2377                     mCurrentlyIntrudingNotification.getTag(),
2378                     mCurrentlyIntrudingNotification.getId());
2379         } catch (android.os.RemoteException ex) {
2380             // oh well
2381         }
2382     }
2383 
2384     /**
2385      * Reload some of our resources when the configuration changes.
2386      *
2387      * We don't reload everything when the configuration changes -- we probably
2388      * should, but getting that smooth is tough.  Someday we'll fix that.  In the
2389      * meantime, just update the things that we know change.
2390      */
updateResources()2391     void updateResources() {
2392         final Context context = mContext;
2393         final Resources res = context.getResources();
2394 
2395         if (mClearButton instanceof TextView) {
2396             ((TextView)mClearButton).setText(context.getText(R.string.status_bar_clear_all_button));
2397         }
2398 
2399         // Update the QuickSettings container
2400         if (mQS != null) mQS.updateResources();
2401 
2402         loadDimens();
2403     }
2404 
loadDimens()2405     protected void loadDimens() {
2406         final Resources res = mContext.getResources();
2407 
2408         mNaturalBarHeight = res.getDimensionPixelSize(
2409                 com.android.internal.R.dimen.status_bar_height);
2410 
2411         int newIconSize = res.getDimensionPixelSize(
2412             com.android.internal.R.dimen.status_bar_icon_size);
2413         int newIconHPadding = res.getDimensionPixelSize(
2414             R.dimen.status_bar_icon_padding);
2415 
2416         if (newIconHPadding != mIconHPadding || newIconSize != mIconSize) {
2417 //            Slog.d(TAG, "size=" + newIconSize + " padding=" + newIconHPadding);
2418             mIconHPadding = newIconHPadding;
2419             mIconSize = newIconSize;
2420             //reloadAllNotificationIcons(); // reload the tray
2421         }
2422 
2423         mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
2424 
2425         mSelfExpandVelocityPx = res.getDimension(R.dimen.self_expand_velocity);
2426         mSelfCollapseVelocityPx = res.getDimension(R.dimen.self_collapse_velocity);
2427         mFlingExpandMinVelocityPx = res.getDimension(R.dimen.fling_expand_min_velocity);
2428         mFlingCollapseMinVelocityPx = res.getDimension(R.dimen.fling_collapse_min_velocity);
2429 
2430         mCollapseMinDisplayFraction = res.getFraction(R.dimen.collapse_min_display_fraction, 1, 1);
2431         mExpandMinDisplayFraction = res.getFraction(R.dimen.expand_min_display_fraction, 1, 1);
2432 
2433         mExpandAccelPx = res.getDimension(R.dimen.expand_accel);
2434         mCollapseAccelPx = res.getDimension(R.dimen.collapse_accel);
2435 
2436         mFlingGestureMaxXVelocityPx = res.getDimension(R.dimen.fling_gesture_max_x_velocity);
2437 
2438         mFlingGestureMaxOutputVelocityPx = res.getDimension(R.dimen.fling_gesture_max_output_velocity);
2439 
2440         mNotificationPanelMarginBottomPx
2441             = (int) res.getDimension(R.dimen.notification_panel_margin_bottom);
2442         mNotificationPanelMarginPx
2443             = (int) res.getDimension(R.dimen.notification_panel_margin_left);
2444         mNotificationPanelGravity = res.getInteger(R.integer.notification_panel_layout_gravity);
2445         if (mNotificationPanelGravity <= 0) {
2446             mNotificationPanelGravity = Gravity.START | Gravity.TOP;
2447         }
2448         mSettingsPanelGravity = res.getInteger(R.integer.settings_panel_layout_gravity);
2449         Log.d(TAG, "mSettingsPanelGravity = " + mSettingsPanelGravity);
2450         if (mSettingsPanelGravity <= 0) {
2451             mSettingsPanelGravity = Gravity.END | Gravity.TOP;
2452         }
2453 
2454         mCarrierLabelHeight = res.getDimensionPixelSize(R.dimen.carrier_label_height);
2455         mNotificationHeaderHeight = res.getDimensionPixelSize(R.dimen.notification_panel_header_height);
2456 
2457         mNotificationPanelMinHeightFrac = res.getFraction(R.dimen.notification_panel_min_height_frac, 1, 1);
2458         if (mNotificationPanelMinHeightFrac < 0f || mNotificationPanelMinHeightFrac > 1f) {
2459             mNotificationPanelMinHeightFrac = 0f;
2460         }
2461 
2462         if (false) Slog.v(TAG, "updateResources");
2463     }
2464 
2465     //
2466     // tracing
2467     //
2468 
postStartTracing()2469     void postStartTracing() {
2470         mHandler.postDelayed(mStartTracing, 3000);
2471     }
2472 
vibrate()2473     void vibrate() {
2474         android.os.Vibrator vib = (android.os.Vibrator)mContext.getSystemService(
2475                 Context.VIBRATOR_SERVICE);
2476         vib.vibrate(250);
2477     }
2478 
2479     Runnable mStartTracing = new Runnable() {
2480         public void run() {
2481             vibrate();
2482             SystemClock.sleep(250);
2483             Slog.d(TAG, "startTracing");
2484             android.os.Debug.startMethodTracing("/data/statusbar-traces/trace");
2485             mHandler.postDelayed(mStopTracing, 10000);
2486         }
2487     };
2488 
2489     Runnable mStopTracing = new Runnable() {
2490         public void run() {
2491             android.os.Debug.stopMethodTracing();
2492             Slog.d(TAG, "stopTracing");
2493             vibrate();
2494         }
2495     };
2496 
2497     @Override
haltTicker()2498     protected void haltTicker() {
2499         mTicker.halt();
2500     }
2501 
2502     @Override
shouldDisableNavbarGestures()2503     protected boolean shouldDisableNavbarGestures() {
2504         return !isDeviceProvisioned()
2505                 || mExpandedVisible
2506                 || (mDisabled & StatusBarManager.DISABLE_SEARCH) != 0;
2507     }
2508 
2509     private static class FastColorDrawable extends Drawable {
2510         private final int mColor;
2511 
FastColorDrawable(int color)2512         public FastColorDrawable(int color) {
2513             mColor = 0xff000000 | color;
2514         }
2515 
2516         @Override
draw(Canvas canvas)2517         public void draw(Canvas canvas) {
2518             canvas.drawColor(mColor, PorterDuff.Mode.SRC);
2519         }
2520 
2521         @Override
setAlpha(int alpha)2522         public void setAlpha(int alpha) {
2523         }
2524 
2525         @Override
setColorFilter(ColorFilter cf)2526         public void setColorFilter(ColorFilter cf) {
2527         }
2528 
2529         @Override
getOpacity()2530         public int getOpacity() {
2531             return PixelFormat.OPAQUE;
2532         }
2533 
2534         @Override
setBounds(int left, int top, int right, int bottom)2535         public void setBounds(int left, int top, int right, int bottom) {
2536         }
2537 
2538         @Override
setBounds(Rect bounds)2539         public void setBounds(Rect bounds) {
2540         }
2541     }
2542 }
2543