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