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