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