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