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