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