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