PhoneStatusBar.java revision 723632ea1ae30b33e9c2055194a4464f00c48c61
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.keyguard.KeyguardHostView.OnDismissAction; 26import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT; 27import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE; 28import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT; 29import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCENT; 30import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT; 31import static com.android.systemui.statusbar.phone.BarTransitions.MODE_WARNING; 32 33import android.animation.Animator; 34import android.animation.AnimatorListenerAdapter; 35import android.animation.TimeInterpolator; 36import android.app.ActivityManager; 37import android.app.ActivityManagerNative; 38import android.app.IActivityManager; 39import android.app.Notification; 40import android.app.PendingIntent; 41import android.app.StatusBarManager; 42import android.content.BroadcastReceiver; 43import android.content.Context; 44import android.content.Intent; 45import android.content.IntentFilter; 46import android.content.res.Configuration; 47import android.content.res.Resources; 48import android.database.ContentObserver; 49import android.graphics.Bitmap; 50import android.graphics.Canvas; 51import android.graphics.ColorFilter; 52import android.graphics.PixelFormat; 53import android.graphics.Point; 54import android.graphics.PorterDuff; 55import android.graphics.Rect; 56import android.graphics.drawable.ColorDrawable; 57import android.graphics.drawable.Drawable; 58import android.inputmethodservice.InputMethodService; 59import android.media.AudioAttributes; 60import android.media.MediaMetadata; 61import android.media.session.MediaController; 62import android.media.session.MediaSession; 63import android.media.session.MediaSessionManager; 64import android.media.session.PlaybackState; 65import android.os.AsyncTask; 66import android.os.Bundle; 67import android.os.Handler; 68import android.os.IBinder; 69import android.os.Message; 70import android.os.PowerManager; 71import android.os.RemoteException; 72import android.os.SystemClock; 73import android.os.Trace; 74import android.os.UserHandle; 75import android.provider.Settings; 76import android.service.notification.NotificationListenerService.RankingMap; 77import android.service.notification.StatusBarNotification; 78import android.util.ArraySet; 79import android.util.DisplayMetrics; 80import android.util.EventLog; 81import android.util.Log; 82import android.view.Display; 83import android.view.Gravity; 84import android.view.HardwareCanvas; 85import android.view.KeyEvent; 86import android.view.LayoutInflater; 87import android.view.MotionEvent; 88import android.view.VelocityTracker; 89import android.view.View; 90import android.view.ViewGroup; 91import android.view.ViewGroup.LayoutParams; 92import android.view.ViewPropertyAnimator; 93import android.view.ViewStub; 94import android.view.ViewTreeObserver; 95import android.view.WindowManager; 96import android.view.animation.AccelerateInterpolator; 97import android.view.animation.Animation; 98import android.view.animation.AnimationUtils; 99import android.view.animation.DecelerateInterpolator; 100import android.view.animation.Interpolator; 101import android.view.animation.PathInterpolator; 102import android.widget.FrameLayout; 103import android.widget.ImageView; 104import android.widget.LinearLayout; 105import android.widget.TextView; 106 107import com.android.internal.statusbar.StatusBarIcon; 108import com.android.keyguard.ViewMediatorCallback; 109import com.android.systemui.DemoMode; 110import com.android.systemui.EventLogTags; 111import com.android.systemui.R; 112import com.android.systemui.doze.DozeService; 113import com.android.systemui.keyguard.KeyguardViewMediator; 114import com.android.systemui.qs.QSPanel; 115import com.android.systemui.qs.QSTile; 116import com.android.systemui.statusbar.ActivatableNotificationView; 117import com.android.systemui.statusbar.BaseStatusBar; 118import com.android.systemui.statusbar.CommandQueue; 119import com.android.systemui.statusbar.DragDownHelper; 120import com.android.systemui.statusbar.ExpandableNotificationRow; 121import com.android.systemui.statusbar.GestureRecorder; 122import com.android.systemui.statusbar.KeyguardIndicationController; 123import com.android.systemui.statusbar.NotificationData; 124import com.android.systemui.statusbar.NotificationData.Entry; 125import com.android.systemui.statusbar.NotificationOverflowContainer; 126import com.android.systemui.statusbar.SignalClusterView; 127import com.android.systemui.statusbar.SpeedBumpView; 128import com.android.systemui.statusbar.StatusBarIconView; 129import com.android.systemui.statusbar.StatusBarState; 130import com.android.systemui.statusbar.policy.BatteryController; 131import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback; 132import com.android.systemui.statusbar.policy.BluetoothControllerImpl; 133import com.android.systemui.statusbar.policy.CastControllerImpl; 134import com.android.systemui.statusbar.policy.FlashlightController; 135import com.android.systemui.statusbar.policy.HeadsUpNotificationView; 136import com.android.systemui.statusbar.policy.KeyguardMonitor; 137import com.android.systemui.statusbar.policy.KeyguardUserSwitcher; 138import com.android.systemui.statusbar.policy.HotspotControllerImpl; 139import com.android.systemui.statusbar.policy.UserInfoController; 140import com.android.systemui.statusbar.policy.LocationControllerImpl; 141import com.android.systemui.statusbar.policy.NetworkControllerImpl; 142import com.android.systemui.statusbar.policy.RotationLockControllerImpl; 143import com.android.systemui.statusbar.policy.UserSwitcherController; 144import com.android.systemui.statusbar.policy.ZenModeController; 145import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; 146import com.android.systemui.statusbar.stack.NotificationStackScrollLayout.OnChildLocationsChangedListener; 147import com.android.systemui.statusbar.stack.StackScrollAlgorithm; 148import com.android.systemui.statusbar.stack.StackScrollState.ViewState; 149import com.android.systemui.volume.VolumeComponent; 150 151import java.io.FileDescriptor; 152import java.io.PrintWriter; 153import java.util.ArrayList; 154import java.util.Collection; 155import java.util.Collections; 156import java.util.List; 157 158public class PhoneStatusBar extends BaseStatusBar implements DemoMode, 159 DragDownHelper.OnDragDownListener, ActivityStarter { 160 static final String TAG = "PhoneStatusBar"; 161 public static final boolean DEBUG = BaseStatusBar.DEBUG; 162 public static final boolean SPEW = false; 163 public static final boolean DUMPTRUCK = true; // extra dumpsys info 164 public static final boolean DEBUG_GESTURES = false; 165 public static final boolean DEBUG_MEDIA = false; 166 public static final boolean DEBUG_MEDIA_FAKE_ARTWORK = false; 167 168 public static final boolean DEBUG_WINDOW_STATE = false; 169 170 // additional instrumentation for testing purposes; intended to be left on during development 171 public static final boolean CHATTY = DEBUG; 172 173 public static final String ACTION_STATUSBAR_START 174 = "com.android.internal.policy.statusbar.START"; 175 176 public static final boolean SHOW_LOCKSCREEN_MEDIA_ARTWORK = true; 177 178 private static final int MSG_OPEN_NOTIFICATION_PANEL = 1000; 179 private static final int MSG_CLOSE_PANELS = 1001; 180 private static final int MSG_OPEN_SETTINGS_PANEL = 1002; 181 // 1020-1030 reserved for BaseStatusBar 182 183 private static final boolean CLOSE_PANEL_WHEN_EMPTIED = true; 184 185 private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10; // see NotificationManagerService 186 private static final int HIDE_ICONS_BELOW_SCORE = Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER; 187 188 private static final int STATUS_OR_NAV_TRANSIENT = 189 View.STATUS_BAR_TRANSIENT | View.NAVIGATION_BAR_TRANSIENT; 190 private static final long AUTOHIDE_TIMEOUT_MS = 3000; 191 192 /** The minimum delay in ms between reports of notification visibility. */ 193 private static final int VISIBILITY_REPORT_MIN_DELAY_MS = 500; 194 195 /** 196 * The delay to reset the hint text when the hint animation is finished running. 197 */ 198 private static final int HINT_RESET_DELAY_MS = 1200; 199 200 private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder() 201 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) 202 .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) 203 .build(); 204 205 public static final int FADE_KEYGUARD_START_DELAY = 100; 206 public static final int FADE_KEYGUARD_DURATION = 300; 207 208 PhoneStatusBarPolicy mIconPolicy; 209 210 // These are no longer handled by the policy, because we need custom strategies for them 211 BluetoothControllerImpl mBluetoothController; 212 BatteryController mBatteryController; 213 LocationControllerImpl mLocationController; 214 NetworkControllerImpl mNetworkController; 215 HotspotControllerImpl mHotspotController; 216 RotationLockControllerImpl mRotationLockController; 217 UserInfoController mUserInfoController; 218 ZenModeController mZenModeController; 219 CastControllerImpl mCastController; 220 VolumeComponent mVolumeComponent; 221 KeyguardUserSwitcher mKeyguardUserSwitcher; 222 FlashlightController mFlashlightController; 223 UserSwitcherController mUserSwitcherController; 224 KeyguardMonitor mKeyguardMonitor; 225 226 int mNaturalBarHeight = -1; 227 int mIconSize = -1; 228 int mIconHPadding = -1; 229 Display mDisplay; 230 Point mCurrentDisplaySize = new Point(); 231 232 StatusBarWindowView mStatusBarWindow; 233 PhoneStatusBarView mStatusBarView; 234 private int mStatusBarWindowState = WINDOW_STATE_SHOWING; 235 private StatusBarWindowManager mStatusBarWindowManager; 236 private UnlockMethodCache mUnlockMethodCache; 237 private DozeServiceHost mDozeServiceHost; 238 239 int mPixelFormat; 240 Object mQueueLock = new Object(); 241 242 // viewgroup containing the normal contents of the statusbar 243 LinearLayout mStatusBarContents; 244 245 // right-hand icons 246 LinearLayout mSystemIconArea; 247 LinearLayout mSystemIcons; 248 249 // left-hand icons 250 LinearLayout mStatusIcons; 251 // the icons themselves 252 IconMerger mNotificationIcons; 253 View mNotificationIconArea; 254 255 // [+> 256 View mMoreIcon; 257 258 // expanded notifications 259 NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window 260 View mExpandedContents; 261 int mNotificationPanelGravity; 262 int mNotificationPanelMarginBottomPx; 263 float mNotificationPanelMinHeightFrac; 264 TextView mNotificationPanelDebugText; 265 266 // settings 267 View mFlipSettingsView; 268 private QSPanel mQSPanel; 269 270 // top bar 271 StatusBarHeaderView mHeader; 272 View mKeyguardStatusView; 273 KeyguardBottomAreaView mKeyguardBottomArea; 274 boolean mLeaveOpenOnKeyguardHide; 275 KeyguardIndicationController mKeyguardIndicationController; 276 277 private boolean mKeyguardFadingAway; 278 private long mKeyguardFadingAwayDelay; 279 private long mKeyguardFadingAwayDuration; 280 281 int mKeyguardMaxNotificationCount; 282 View mDateTimeView; 283 284 // carrier/wifi label 285 private TextView mCarrierLabel; 286 private boolean mCarrierLabelVisible = false; 287 private int mCarrierLabelHeight; 288 private int mStatusBarHeaderHeight; 289 290 private boolean mShowCarrierInPanel = false; 291 292 // position 293 int[] mPositionTmp = new int[2]; 294 boolean mExpandedVisible; 295 296 // on-screen navigation buttons 297 private NavigationBarView mNavigationBarView = null; 298 private int mNavigationBarWindowState = WINDOW_STATE_SHOWING; 299 300 // the tracker view 301 int mTrackingPosition; // the position of the top of the tracking view. 302 303 // ticker 304 private boolean mTickerEnabled; 305 private Ticker mTicker; 306 private View mTickerView; 307 private boolean mTicking; 308 309 // Tracking finger for opening/closing. 310 int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore 311 boolean mTracking; 312 VelocityTracker mVelocityTracker; 313 314 int[] mAbsPos = new int[2]; 315 Runnable mPostCollapseCleanup = null; 316 317 // for disabling the status bar 318 int mDisabled = 0; 319 320 // tracking calls to View.setSystemUiVisibility() 321 int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE; 322 323 DisplayMetrics mDisplayMetrics = new DisplayMetrics(); 324 325 // XXX: gesture research 326 private final GestureRecorder mGestureRec = DEBUG_GESTURES 327 ? new GestureRecorder("/sdcard/statusbar_gestures.dat") 328 : null; 329 330 private int mNavigationIconHints = 0; 331 332 // ensure quick settings is disabled until the current user makes it through the setup wizard 333 private boolean mUserSetup = false; 334 private ContentObserver mUserSetupObserver = new ContentObserver(new Handler()) { 335 @Override 336 public void onChange(boolean selfChange) { 337 final boolean userSetup = 0 != Settings.Secure.getIntForUser( 338 mContext.getContentResolver(), 339 Settings.Secure.USER_SETUP_COMPLETE, 340 0 /*default */, 341 mCurrentUserId); 342 if (MULTIUSER_DEBUG) Log.d(TAG, String.format("User setup changed: " + 343 "selfChange=%s userSetup=%s mUserSetup=%s", 344 selfChange, userSetup, mUserSetup)); 345 346 if (userSetup != mUserSetup) { 347 mUserSetup = userSetup; 348 if (mNotificationPanel != null) { 349 mNotificationPanel.setQsExpansionEnabled(isDeviceProvisioned() && userSetup); 350 } 351 if (!mUserSetup && mStatusBarView != null) 352 animateCollapseQuickSettings(); 353 } 354 } 355 }; 356 357 final private ContentObserver mHeadsUpObserver = new ContentObserver(mHandler) { 358 @Override 359 public void onChange(boolean selfChange) { 360 boolean wasUsing = mUseHeadsUp; 361 mUseHeadsUp = ENABLE_HEADS_UP && Settings.Global.HEADS_UP_OFF != Settings.Global.getInt( 362 mContext.getContentResolver(), Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED, 363 Settings.Global.HEADS_UP_OFF); 364 mHeadsUpTicker = mUseHeadsUp && 0 != Settings.Global.getInt( 365 mContext.getContentResolver(), SETTING_HEADS_UP_TICKER, 0); 366 Log.d(TAG, "heads up is " + (mUseHeadsUp ? "enabled" : "disabled")); 367 if (wasUsing != mUseHeadsUp) { 368 if (!mUseHeadsUp) { 369 Log.d(TAG, "dismissing any existing heads up notification on disable event"); 370 setHeadsUpVisibility(false); 371 mHeadsUpNotificationView.release(); 372 removeHeadsUpView(); 373 } else { 374 addHeadsUpView(); 375 } 376 } 377 } 378 }; 379 380 private int mInteractingWindows; 381 private boolean mAutohideSuspended; 382 private int mStatusBarMode; 383 private int mNavigationBarMode; 384 private Boolean mScreenOn; 385 386 private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; 387 private ViewMediatorCallback mKeyguardViewMediatorCallback; 388 private ScrimController mScrimController; 389 390 private final Runnable mAutohide = new Runnable() { 391 @Override 392 public void run() { 393 int requested = mSystemUiVisibility & ~STATUS_OR_NAV_TRANSIENT; 394 if (mSystemUiVisibility != requested) { 395 notifyUiVisibilityChanged(requested); 396 } 397 }}; 398 399 private boolean mVisible; 400 private boolean mWaitingForKeyguardExit; 401 private boolean mDozing; 402 403 private Interpolator mLinearOutSlowIn; 404 private Interpolator mAlphaIn = new PathInterpolator(0f, 0.2f, 1f, 1f); 405 private Interpolator mAlphaOut = new PathInterpolator(0f, 0f, 0.8f, 1f); 406 407 private FrameLayout mBackdrop; 408 private ImageView mBackdropFront, mBackdropBack; 409 410 private MediaSessionManager mMediaSessionManager; 411 private MediaController mMediaController; 412 private String mMediaNotificationKey; 413 private MediaMetadata mMediaMetadata; 414 private MediaController.Callback mMediaListener 415 = new MediaController.Callback() { 416 @Override 417 public void onPlaybackStateChanged(PlaybackState state) { 418 super.onPlaybackStateChanged(state); 419 if (DEBUG_MEDIA) Log.v(TAG, "DEBUG_MEDIA: onPlaybackStateChanged: " + state); 420 } 421 422 @Override 423 public void onMetadataChanged(MediaMetadata metadata) { 424 super.onMetadataChanged(metadata); 425 if (DEBUG_MEDIA) Log.v(TAG, "DEBUG_MEDIA: onMetadataChanged: " + metadata); 426 mMediaMetadata = metadata; 427 updateMediaMetaData(true); 428 } 429 }; 430 431 private final OnChildLocationsChangedListener mOnChildLocationsChangedListener = 432 new OnChildLocationsChangedListener() { 433 @Override 434 public void onChildLocationsChanged(NotificationStackScrollLayout stackScrollLayout) { 435 userActivity(); 436 } 437 }; 438 439 private int mDisabledUnmodified; 440 441 /** Keys of notifications currently visible to the user. */ 442 private final ArraySet<String> mCurrentlyVisibleNotifications = new ArraySet<String>(); 443 private long mLastVisibilityReportUptimeMs; 444 445 private final ShadeUpdates mShadeUpdates = new ShadeUpdates(); 446 447 private int mDrawCount; 448 private Runnable mLaunchTransitionEndRunnable; 449 private boolean mLaunchTransitionFadingAway; 450 451 private static final int VISIBLE_LOCATIONS = ViewState.LOCATION_FIRST_CARD 452 | ViewState.LOCATION_TOP_STACK_PEEKING 453 | ViewState.LOCATION_MAIN_AREA 454 | ViewState.LOCATION_BOTTOM_STACK_PEEKING; 455 456 private final OnChildLocationsChangedListener mNotificationLocationsChangedListener = 457 new OnChildLocationsChangedListener() { 458 @Override 459 public void onChildLocationsChanged( 460 NotificationStackScrollLayout stackScrollLayout) { 461 if (mHandler.hasCallbacks(mVisibilityReporter)) { 462 // Visibilities will be reported when the existing 463 // callback is executed. 464 return; 465 } 466 // Calculate when we're allowed to run the visibility 467 // reporter. Note that this timestamp might already have 468 // passed. That's OK, the callback will just be executed 469 // ASAP. 470 long nextReportUptimeMs = 471 mLastVisibilityReportUptimeMs + VISIBILITY_REPORT_MIN_DELAY_MS; 472 mHandler.postAtTime(mVisibilityReporter, nextReportUptimeMs); 473 } 474 }; 475 476 // Tracks notifications currently visible in mNotificationStackScroller and 477 // emits visibility events via NoMan on changes. 478 private final Runnable mVisibilityReporter = new Runnable() { 479 private final ArrayList<String> mTmpNewlyVisibleNotifications = new ArrayList<String>(); 480 private final ArrayList<String> mTmpCurrentlyVisibleNotifications = new ArrayList<String>(); 481 482 @Override 483 public void run() { 484 mLastVisibilityReportUptimeMs = SystemClock.uptimeMillis(); 485 486 // 1. Loop over mNotificationData entries: 487 // A. Keep list of visible notifications. 488 // B. Keep list of previously hidden, now visible notifications. 489 // 2. Compute no-longer visible notifications by removing currently 490 // visible notifications from the set of previously visible 491 // notifications. 492 // 3. Report newly visible and no-longer visible notifications. 493 // 4. Keep currently visible notifications for next report. 494 int N = mNotificationData.size(); 495 for (int i = 0; i < N; i++) { 496 Entry entry = mNotificationData.get(i); 497 String key = entry.notification.getKey(); 498 boolean previouslyVisible = mCurrentlyVisibleNotifications.contains(key); 499 boolean currentlyVisible = 500 (mStackScroller.getChildLocation(entry.row) & VISIBLE_LOCATIONS) != 0; 501 if (currentlyVisible) { 502 // Build new set of visible notifications. 503 mTmpCurrentlyVisibleNotifications.add(key); 504 } 505 if (!previouslyVisible && currentlyVisible) { 506 mTmpNewlyVisibleNotifications.add(key); 507 } 508 } 509 ArraySet<String> noLongerVisibleNotifications = mCurrentlyVisibleNotifications; 510 noLongerVisibleNotifications.removeAll(mTmpCurrentlyVisibleNotifications); 511 512 logNotificationVisibilityChanges( 513 mTmpNewlyVisibleNotifications, noLongerVisibleNotifications); 514 515 mCurrentlyVisibleNotifications.clear(); 516 mCurrentlyVisibleNotifications.addAll(mTmpCurrentlyVisibleNotifications); 517 518 mTmpNewlyVisibleNotifications.clear(); 519 mTmpCurrentlyVisibleNotifications.clear(); 520 } 521 }; 522 523 private final View.OnClickListener mOverflowClickListener = new View.OnClickListener() { 524 @Override 525 public void onClick(View v) { 526 goToLockedShade(null); 527 } 528 }; 529 530 @Override 531 protected void setShowLockscreenNotifications(boolean show) { 532 super.setShowLockscreenNotifications(show); 533 updateStackScrollerState(); 534 } 535 536 @Override 537 public void start() { 538 mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE)) 539 .getDefaultDisplay(); 540 updateDisplaySize(); 541 super.start(); // calls createAndAddWindows() 542 543 mMediaSessionManager 544 = (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE); 545 // TODO: use MediaSessionManager.SessionListener to hook us up to future updates 546 // in session state 547 548 addNavigationBar(); 549 550 // Lastly, call to the icon policy to install/update all the icons. 551 mIconPolicy = new PhoneStatusBarPolicy(mContext); 552 mSettingsObserver.onChange(false); // set up 553 554 mHeadsUpObserver.onChange(true); // set up 555 if (ENABLE_HEADS_UP) { 556 mContext.getContentResolver().registerContentObserver( 557 Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED), true, 558 mHeadsUpObserver); 559 mContext.getContentResolver().registerContentObserver( 560 Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true, 561 mHeadsUpObserver); 562 } 563 mUnlockMethodCache = UnlockMethodCache.getInstance(mContext); 564 startKeyguard(); 565 566 mDozeServiceHost = new DozeServiceHost(); 567 putComponent(DozeService.Host.class, mDozeServiceHost); 568 569 setControllerUsers(); 570 } 571 572 // ================================================================================ 573 // Constructing the view 574 // ================================================================================ 575 protected PhoneStatusBarView makeStatusBarView() { 576 final Context context = mContext; 577 578 Resources res = context.getResources(); 579 580 updateDisplaySize(); // populates mDisplayMetrics 581 updateResources(); 582 583 mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size); 584 585 mStatusBarWindow = (StatusBarWindowView) View.inflate(context, 586 R.layout.super_status_bar, null); 587 mStatusBarWindow.mService = this; 588 mStatusBarWindow.setOnTouchListener(new View.OnTouchListener() { 589 @Override 590 public boolean onTouch(View v, MotionEvent event) { 591 checkUserAutohide(v, event); 592 if (event.getAction() == MotionEvent.ACTION_DOWN) { 593 if (mExpandedVisible) { 594 animateCollapsePanels(); 595 } 596 } 597 return mStatusBarWindow.onTouchEvent(event); 598 }}); 599 600 mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar); 601 mStatusBarView.setBar(this); 602 603 PanelHolder holder = (PanelHolder) mStatusBarWindow.findViewById(R.id.panel_holder); 604 mStatusBarView.setPanelHolder(holder); 605 606 mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById( 607 R.id.notification_panel); 608 mNotificationPanel.setStatusBar(this); 609 610 if (!ActivityManager.isHighEndGfx()) { 611 mStatusBarWindow.setBackground(null); 612 mNotificationPanel.setBackground(new FastColorDrawable(context.getResources().getColor( 613 R.color.notification_panel_solid_background))); 614 } 615 if (ENABLE_HEADS_UP) { 616 mHeadsUpNotificationView = 617 (HeadsUpNotificationView) View.inflate(context, R.layout.heads_up, null); 618 mHeadsUpNotificationView.setVisibility(View.GONE); 619 mHeadsUpNotificationView.setBar(this); 620 } 621 if (MULTIUSER_DEBUG) { 622 mNotificationPanelDebugText = (TextView) mNotificationPanel.findViewById( 623 R.id.header_debug_info); 624 mNotificationPanelDebugText.setVisibility(View.VISIBLE); 625 } 626 627 updateShowSearchHoldoff(); 628 629 try { 630 boolean showNav = mWindowManagerService.hasNavigationBar(); 631 if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav); 632 if (showNav) { 633 mNavigationBarView = 634 (NavigationBarView) View.inflate(context, R.layout.navigation_bar, null); 635 636 mNavigationBarView.setDisabledFlags(mDisabled); 637 mNavigationBarView.setBar(this); 638 mNavigationBarView.setOnTouchListener(new View.OnTouchListener() { 639 @Override 640 public boolean onTouch(View v, MotionEvent event) { 641 checkUserAutohide(v, event); 642 return false; 643 }}); 644 } 645 } catch (RemoteException ex) { 646 // no window manager? good luck with that 647 } 648 649 // figure out which pixel-format to use for the status bar. 650 mPixelFormat = PixelFormat.OPAQUE; 651 652 mSystemIconArea = (LinearLayout) mStatusBarView.findViewById(R.id.system_icon_area); 653 mSystemIcons = (LinearLayout) mStatusBarView.findViewById(R.id.system_icons); 654 mStatusIcons = (LinearLayout)mStatusBarView.findViewById(R.id.statusIcons); 655 mNotificationIconArea = mStatusBarView.findViewById(R.id.notification_icon_area_inner); 656 mNotificationIcons = (IconMerger)mStatusBarView.findViewById(R.id.notificationIcons); 657 mMoreIcon = mStatusBarView.findViewById(R.id.moreIcon); 658 mNotificationIcons.setOverflowIndicator(mMoreIcon); 659 mStatusBarContents = (LinearLayout)mStatusBarView.findViewById(R.id.status_bar_contents); 660 661 mStackScroller = (NotificationStackScrollLayout) mStatusBarWindow.findViewById( 662 R.id.notification_stack_scroller); 663 mStackScroller.setLongPressListener(getNotificationLongClicker()); 664 665 mKeyguardIconOverflowContainer = 666 (NotificationOverflowContainer) LayoutInflater.from(mContext).inflate( 667 R.layout.status_bar_notification_keyguard_overflow, mStackScroller, false); 668 mKeyguardIconOverflowContainer.setOnActivatedListener(this); 669 mKeyguardIconOverflowContainer.setOnClickListener(mOverflowClickListener); 670 mStackScroller.addView(mKeyguardIconOverflowContainer); 671 672 SpeedBumpView speedBump = (SpeedBumpView) LayoutInflater.from(mContext).inflate( 673 R.layout.status_bar_notification_speed_bump, mStackScroller, false); 674 mStackScroller.setSpeedBumpView(speedBump); 675 mExpandedContents = mStackScroller; 676 677 mScrimController = new ScrimController(mStatusBarWindow.findViewById(R.id.scrim_behind), 678 mStatusBarWindow.findViewById(R.id.scrim_in_front)); 679 mStatusBarView.setScrimController(mScrimController); 680 681 mHeader = (StatusBarHeaderView) mStatusBarWindow.findViewById(R.id.header); 682 mHeader.setActivityStarter(this); 683 mKeyguardStatusView = mStatusBarWindow.findViewById(R.id.keyguard_status_view); 684 mKeyguardBottomArea = 685 (KeyguardBottomAreaView) mStatusBarWindow.findViewById(R.id.keyguard_bottom_area); 686 mKeyguardBottomArea.setActivityStarter(this); 687 mKeyguardIndicationController = new KeyguardIndicationController(mContext, 688 (KeyguardIndicationTextView) mStatusBarWindow.findViewById( 689 R.id.keyguard_indication_text)); 690 691 mDateTimeView = mHeader.findViewById(R.id.datetime); 692 if (mDateTimeView != null) { 693 mDateTimeView.setOnClickListener(mClockClickListener); 694 mDateTimeView.setEnabled(true); 695 } 696 697 mTickerEnabled = res.getBoolean(R.bool.enable_ticker); 698 if (mTickerEnabled) { 699 final ViewStub tickerStub = (ViewStub) mStatusBarView.findViewById(R.id.ticker_stub); 700 if (tickerStub != null) { 701 mTickerView = tickerStub.inflate(); 702 mTicker = new MyTicker(context, mStatusBarView); 703 704 TickerView tickerView = (TickerView) mStatusBarView.findViewById(R.id.tickerText); 705 tickerView.mTicker = mTicker; 706 } 707 } 708 709 mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore); 710 711 // set the inital view visibility 712 setAreThereNotifications(); 713 714 // Other icons 715 mLocationController = new LocationControllerImpl(mContext); // will post a notification 716 mBatteryController = new BatteryController(mContext); 717 mBatteryController.addStateChangedCallback(new BatteryStateChangeCallback() { 718 @Override 719 public void onPowerSaveChanged() { 720 mHandler.post(mCheckBarModes); 721 } 722 @Override 723 public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) { 724 // noop 725 } 726 }); 727 mNetworkController = new NetworkControllerImpl(mContext); 728 mHotspotController = new HotspotControllerImpl(mContext); 729 mBluetoothController = new BluetoothControllerImpl(mContext); 730 if (mContext.getResources().getBoolean(R.bool.config_showRotationLock)) { 731 mRotationLockController = new RotationLockControllerImpl(mContext); 732 } 733 mUserInfoController = new UserInfoController(mContext); 734 mVolumeComponent = getComponent(VolumeComponent.class); 735 mZenModeController = mVolumeComponent.getZenController(); 736 mCastController = new CastControllerImpl(mContext); 737 final SignalClusterView signalCluster = 738 (SignalClusterView)mStatusBarView.findViewById(R.id.signal_cluster); 739 740 mNetworkController.addSignalCluster(signalCluster); 741 signalCluster.setNetworkController(mNetworkController); 742 final boolean isAPhone = mNetworkController.hasVoiceCallingFeature(); 743 if (isAPhone) { 744 mNetworkController.addEmergencyLabelView(mHeader); 745 } 746 747 mCarrierLabel = (TextView)mStatusBarWindow.findViewById(R.id.carrier_label); 748 mShowCarrierInPanel = (mCarrierLabel != null); 749 if (DEBUG) Log.v(TAG, "carrierlabel=" + mCarrierLabel + " show=" + mShowCarrierInPanel); 750 if (mShowCarrierInPanel) { 751 mCarrierLabel.setVisibility(mCarrierLabelVisible ? View.VISIBLE : View.INVISIBLE); 752 753 // for mobile devices, we always show mobile connection info here (SPN/PLMN) 754 // for other devices, we show whatever network is connected 755 if (mNetworkController.hasMobileDataFeature()) { 756 mNetworkController.addMobileLabelView(mCarrierLabel); 757 } else { 758 mNetworkController.addCombinedLabelView(mCarrierLabel); 759 } 760 761 // set up the dynamic hide/show of the label 762 // TODO: uncomment, handle this for the Stack scroller aswell 763// ((NotificationRowLayout) mStackScroller) 764// .setOnSizeChangedListener(new OnSizeChangedListener() { 765// @Override 766// public void onSizeChanged(View view, int w, int h, int oldw, int oldh) { 767// updateCarrierLabelVisibility(false); 768 } 769 770 mFlashlightController = new FlashlightController(mContext); 771 mKeyguardBottomArea.setFlashlightController(mFlashlightController); 772 mUserSwitcherController = new UserSwitcherController(mContext); 773 mKeyguardMonitor = new KeyguardMonitor(); 774 775 mKeyguardUserSwitcher = new KeyguardUserSwitcher(mContext, 776 (ViewStub) mStatusBarWindow.findViewById(R.id.keyguard_user_switcher), mHeader, 777 mUserSwitcherController); 778 779 780 // Set up the quick settings tile panel 781 mQSPanel = (QSPanel) mStatusBarWindow.findViewById(R.id.quick_settings_panel); 782 if (mQSPanel != null) { 783 final QSTileHost qsh = new QSTileHost(mContext, this, 784 mBluetoothController, mLocationController, mRotationLockController, 785 mNetworkController, mZenModeController, mHotspotController, 786 mCastController, mFlashlightController, 787 mUserSwitcherController, mKeyguardMonitor); 788 mQSPanel.setHost(qsh); 789 for (QSTile<?> tile : qsh.getTiles()) { 790 mQSPanel.addTile(tile); 791 } 792 mHeader.setQSPanel(mQSPanel); 793 } 794 795 mBackdrop = (FrameLayout) mStatusBarWindow.findViewById(R.id.backdrop); 796 mBackdropFront = (ImageView) mBackdrop.findViewById(R.id.backdrop_front); 797 mBackdropBack = (ImageView) mBackdrop.findViewById(R.id.backdrop_back); 798 799 // User info. Trigger first load. 800 mHeader.setUserInfoController(mUserInfoController); 801 mUserInfoController.reloadUserInfo(); 802 803 mHeader.setBatteryController(mBatteryController); 804 805 PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); 806 mBroadcastReceiver.onReceive(mContext, 807 new Intent(pm.isScreenOn() ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF)); 808 809 // receive broadcasts 810 IntentFilter filter = new IntentFilter(); 811 filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); 812 filter.addAction(Intent.ACTION_SCREEN_OFF); 813 filter.addAction(Intent.ACTION_SCREEN_ON); 814 if (DEBUG_MEDIA_FAKE_ARTWORK) { 815 filter.addAction("fake_artwork"); 816 } 817 filter.addAction(ACTION_DEMO); 818 context.registerReceiver(mBroadcastReceiver, filter); 819 820 // listen for USER_SETUP_COMPLETE setting (per-user) 821 resetUserSetupObserver(); 822 823 startGlyphRasterizeHack(); 824 return mStatusBarView; 825 } 826 827 /** 828 * Hack to improve glyph rasterization for scaled text views. 829 */ 830 private void startGlyphRasterizeHack() { 831 mStatusBarView.getViewTreeObserver().addOnPreDrawListener( 832 new ViewTreeObserver.OnPreDrawListener() { 833 @Override 834 public boolean onPreDraw() { 835 if (mDrawCount == 1) { 836 mStatusBarView.getViewTreeObserver().removeOnPreDrawListener(this); 837 HardwareCanvas.setProperty("extraRasterBucket", 838 Float.toString(StackScrollAlgorithm.DIMMED_SCALE)); 839 HardwareCanvas.setProperty("extraRasterBucket", Float.toString( 840 mContext.getResources().getDimensionPixelSize( 841 R.dimen.qs_time_collapsed_size) 842 / mContext.getResources().getDimensionPixelSize( 843 R.dimen.qs_time_expanded_size))); 844 } 845 mDrawCount++; 846 return true; 847 } 848 }); 849 } 850 851 @Override 852 protected void setZenMode(int mode) { 853 super.setZenMode(mode); 854 if (mIconPolicy != null) { 855 mIconPolicy.setZenMode(mode); 856 } 857 } 858 859 private void startKeyguard() { 860 KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class); 861 mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this, 862 mStatusBarWindow, mStatusBarWindowManager, mScrimController); 863 mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback(); 864 } 865 866 @Override 867 protected void onShowSearchPanel() { 868 if (mNavigationBarView != null) { 869 mNavigationBarView.getBarTransitions().setContentVisible(false); 870 } 871 } 872 873 @Override 874 protected void onHideSearchPanel() { 875 if (mNavigationBarView != null) { 876 mNavigationBarView.getBarTransitions().setContentVisible(true); 877 } 878 } 879 880 @Override 881 protected View getStatusBarView() { 882 return mStatusBarView; 883 } 884 885 public StatusBarWindowView getStatusBarWindow() { 886 return mStatusBarWindow; 887 } 888 889 @Override 890 protected WindowManager.LayoutParams getSearchLayoutParams(LayoutParams layoutParams) { 891 boolean opaque = false; 892 WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 893 LayoutParams.MATCH_PARENT, 894 LayoutParams.MATCH_PARENT, 895 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, 896 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 897 | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM 898 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, 899 (opaque ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT)); 900 if (ActivityManager.isHighEndGfx()) { 901 lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; 902 } 903 lp.gravity = Gravity.BOTTOM | Gravity.START; 904 lp.setTitle("SearchPanel"); 905 // TODO: Define custom animation for Search panel 906 lp.windowAnimations = com.android.internal.R.style.Animation_RecentApplications; 907 lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED 908 | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; 909 return lp; 910 } 911 912 @Override 913 protected void updateSearchPanel() { 914 super.updateSearchPanel(); 915 if (mNavigationBarView != null) { 916 mNavigationBarView.setDelegateView(mSearchPanelView); 917 } 918 } 919 920 @Override 921 public void showSearchPanel() { 922 super.showSearchPanel(); 923 mHandler.removeCallbacks(mShowSearchPanel); 924 925 // we want to freeze the sysui state wherever it is 926 mSearchPanelView.setSystemUiVisibility(mSystemUiVisibility); 927 928 if (mNavigationBarView != null) { 929 WindowManager.LayoutParams lp = 930 (android.view.WindowManager.LayoutParams) mNavigationBarView.getLayoutParams(); 931 lp.flags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; 932 mWindowManager.updateViewLayout(mNavigationBarView, lp); 933 } 934 } 935 936 @Override 937 public void hideSearchPanel() { 938 super.hideSearchPanel(); 939 if (mNavigationBarView != null) { 940 WindowManager.LayoutParams lp = 941 (android.view.WindowManager.LayoutParams) mNavigationBarView.getLayoutParams(); 942 lp.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; 943 mWindowManager.updateViewLayout(mNavigationBarView, lp); 944 } 945 } 946 947 public int getStatusBarHeight() { 948 if (mNaturalBarHeight < 0) { 949 final Resources res = mContext.getResources(); 950 mNaturalBarHeight = 951 res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); 952 } 953 return mNaturalBarHeight; 954 } 955 956 private View.OnClickListener mRecentsClickListener = new View.OnClickListener() { 957 public void onClick(View v) { 958 awakenDreams(); 959 toggleRecentApps(); 960 } 961 }; 962 963 private View.OnLongClickListener mLockToAppClickListener = new View.OnLongClickListener() { 964 @Override 965 public boolean onLongClick(View v) { 966 toggleLockedApp(); 967 return true; 968 } 969 }; 970 971 private int mShowSearchHoldoff = 0; 972 private Runnable mShowSearchPanel = new Runnable() { 973 public void run() { 974 showSearchPanel(); 975 awakenDreams(); 976 } 977 }; 978 979 View.OnTouchListener mHomeActionListener = new View.OnTouchListener() { 980 public boolean onTouch(View v, MotionEvent event) { 981 switch(event.getAction()) { 982 case MotionEvent.ACTION_DOWN: 983 if (!shouldDisableNavbarGestures()) { 984 mHandler.removeCallbacks(mShowSearchPanel); 985 mHandler.postDelayed(mShowSearchPanel, mShowSearchHoldoff); 986 } 987 break; 988 989 case MotionEvent.ACTION_UP: 990 case MotionEvent.ACTION_CANCEL: 991 mHandler.removeCallbacks(mShowSearchPanel); 992 awakenDreams(); 993 break; 994 } 995 return false; 996 } 997 }; 998 999 private void awakenDreams() { 1000 if (mDreamManager != null) { 1001 try { 1002 mDreamManager.awaken(); 1003 } catch (RemoteException e) { 1004 // fine, stay asleep then 1005 } 1006 } 1007 } 1008 1009 private void prepareNavigationBarView() { 1010 mNavigationBarView.reorient(); 1011 1012 mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener); 1013 mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPreloadOnTouchListener); 1014 mNavigationBarView.getRecentsButton().setLongClickable(true); 1015 mNavigationBarView.getRecentsButton().setOnLongClickListener(mLockToAppClickListener); 1016 mNavigationBarView.getHomeButton().setOnTouchListener(mHomeActionListener); 1017 updateSearchPanel(); 1018 } 1019 1020 // For small-screen devices (read: phones) that lack hardware navigation buttons 1021 private void addNavigationBar() { 1022 if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + mNavigationBarView); 1023 if (mNavigationBarView == null) return; 1024 1025 prepareNavigationBarView(); 1026 1027 mWindowManager.addView(mNavigationBarView, getNavigationBarLayoutParams()); 1028 } 1029 1030 private void repositionNavigationBar() { 1031 if (mNavigationBarView == null || !mNavigationBarView.isAttachedToWindow()) return; 1032 1033 prepareNavigationBarView(); 1034 1035 mWindowManager.updateViewLayout(mNavigationBarView, getNavigationBarLayoutParams()); 1036 } 1037 1038 private void notifyNavigationBarScreenOn(boolean screenOn) { 1039 if (mNavigationBarView == null) return; 1040 mNavigationBarView.notifyScreenOn(screenOn); 1041 } 1042 1043 private WindowManager.LayoutParams getNavigationBarLayoutParams() { 1044 WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 1045 LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, 1046 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR, 1047 0 1048 | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING 1049 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 1050 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL 1051 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH 1052 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, 1053 PixelFormat.TRANSLUCENT); 1054 // this will allow the navbar to run in an overlay on devices that support this 1055 if (ActivityManager.isHighEndGfx()) { 1056 lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; 1057 } 1058 1059 lp.setTitle("NavigationBar"); 1060 lp.windowAnimations = 0; 1061 return lp; 1062 } 1063 1064 private void addHeadsUpView() { 1065 int headsUpHeight = mContext.getResources() 1066 .getDimensionPixelSize(R.dimen.heads_up_window_height); 1067 WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 1068 LayoutParams.MATCH_PARENT, headsUpHeight, 1069 WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, // above the status bar! 1070 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 1071 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS 1072 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL 1073 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 1074 | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM 1075 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, 1076 PixelFormat.TRANSLUCENT); 1077 lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; 1078 lp.gravity = Gravity.TOP; 1079 lp.setTitle("Heads Up"); 1080 lp.packageName = mContext.getPackageName(); 1081 lp.windowAnimations = R.style.Animation_StatusBar_HeadsUp; 1082 1083 mWindowManager.addView(mHeadsUpNotificationView, lp); 1084 } 1085 1086 private void removeHeadsUpView() { 1087 mWindowManager.removeView(mHeadsUpNotificationView); 1088 } 1089 1090 public void refreshAllStatusBarIcons() { 1091 refreshAllIconsForLayout(mStatusIcons); 1092 refreshAllIconsForLayout(mNotificationIcons); 1093 } 1094 1095 private void refreshAllIconsForLayout(LinearLayout ll) { 1096 final int count = ll.getChildCount(); 1097 for (int n = 0; n < count; n++) { 1098 View child = ll.getChildAt(n); 1099 if (child instanceof StatusBarIconView) { 1100 ((StatusBarIconView) child).updateDrawable(); 1101 } 1102 } 1103 } 1104 1105 public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) { 1106 if (SPEW) Log.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex 1107 + " icon=" + icon); 1108 StatusBarIconView view = new StatusBarIconView(mContext, slot, null); 1109 view.set(icon); 1110 mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(mIconSize, mIconSize)); 1111 } 1112 1113 public void updateIcon(String slot, int index, int viewIndex, 1114 StatusBarIcon old, StatusBarIcon icon) { 1115 if (SPEW) Log.d(TAG, "updateIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex 1116 + " old=" + old + " icon=" + icon); 1117 StatusBarIconView view = (StatusBarIconView)mStatusIcons.getChildAt(viewIndex); 1118 view.set(icon); 1119 } 1120 1121 public void removeIcon(String slot, int index, int viewIndex) { 1122 if (SPEW) Log.d(TAG, "removeIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex); 1123 mStatusIcons.removeViewAt(viewIndex); 1124 } 1125 1126 public UserHandle getCurrentUserHandle() { 1127 return new UserHandle(mCurrentUserId); 1128 } 1129 1130 @Override 1131 public void addNotification(StatusBarNotification notification, RankingMap ranking) { 1132 if (DEBUG) Log.d(TAG, "addNotification key=" + notification.getKey()); 1133 if (mUseHeadsUp && shouldInterrupt(notification)) { 1134 if (DEBUG) Log.d(TAG, "launching notification in heads up mode"); 1135 Entry interruptionCandidate = new Entry(notification, null); 1136 ViewGroup holder = mHeadsUpNotificationView.getHolder(); 1137 if (inflateViewsForHeadsUp(interruptionCandidate, holder)) { 1138 // 1. Populate mHeadsUpNotificationView 1139 mHeadsUpNotificationView.showNotification(interruptionCandidate); 1140 1141 // do not show the notification in the shade, yet. 1142 return; 1143 } 1144 } 1145 1146 Entry shadeEntry = createNotificationViews(notification); 1147 if (shadeEntry == null) { 1148 return; 1149 } 1150 1151 if (notification.getNotification().fullScreenIntent != null) { 1152 // Stop screensaver if the notification has a full-screen intent. 1153 // (like an incoming phone call) 1154 awakenDreams(); 1155 1156 // not immersive & a full-screen alert should be shown 1157 if (DEBUG) Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent"); 1158 try { 1159 notification.getNotification().fullScreenIntent.send(); 1160 } catch (PendingIntent.CanceledException e) { 1161 } 1162 } else { 1163 // usual case: status bar visible & not immersive 1164 1165 // show the ticker if there isn't already a heads up 1166 if (mHeadsUpNotificationView.getEntry() == null) { 1167 tick(notification, true); 1168 } 1169 } 1170 addNotificationViews(shadeEntry, ranking); 1171 // Recalculate the position of the sliding windows and the titles. 1172 setAreThereNotifications(); 1173 updateExpandedViewPos(EXPANDED_LEAVE_ALONE); 1174 } 1175 1176 public void displayNotificationFromHeadsUp(StatusBarNotification notification) { 1177 NotificationData.Entry shadeEntry = createNotificationViews(notification); 1178 if (shadeEntry == null) { 1179 return; 1180 } 1181 shadeEntry.setInterruption(); 1182 1183 addNotificationViews(shadeEntry, null); 1184 // Recalculate the position of the sliding windows and the titles. 1185 setAreThereNotifications(); 1186 updateExpandedViewPos(EXPANDED_LEAVE_ALONE); 1187 } 1188 1189 @Override 1190 public void resetHeadsUpDecayTimer() { 1191 mHandler.removeMessages(MSG_DECAY_HEADS_UP); 1192 if (mUseHeadsUp && mHeadsUpNotificationDecay > 0 1193 && mHeadsUpNotificationView.isClearable()) { 1194 mHandler.sendEmptyMessageDelayed(MSG_DECAY_HEADS_UP, mHeadsUpNotificationDecay); 1195 } 1196 } 1197 1198 @Override 1199 public void scheduleHeadsUpOpen() { 1200 mHandler.sendEmptyMessage(MSG_SHOW_HEADS_UP); 1201 } 1202 1203 @Override 1204 public void scheduleHeadsUpClose() { 1205 mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP); 1206 } 1207 1208 @Override 1209 public void scheduleHeadsUpEscalation() { 1210 mHandler.sendEmptyMessage(MSG_ESCALATE_HEADS_UP); 1211 } 1212 1213 @Override 1214 protected void updateNotificationRanking(RankingMap ranking) { 1215 mNotificationData.updateRanking(ranking); 1216 updateNotifications(); 1217 } 1218 1219 @Override 1220 public void removeNotification(String key, RankingMap ranking) { 1221 if (ENABLE_HEADS_UP && mHeadsUpNotificationView.getEntry() != null 1222 && key.equals(mHeadsUpNotificationView.getEntry().notification.getKey())) { 1223 mHeadsUpNotificationView.clear(); 1224 } 1225 1226 StatusBarNotification old = removeNotificationViews(key, ranking); 1227 if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old); 1228 1229 if (old != null) { 1230 // Cancel the ticker if it's still running 1231 if (mTickerEnabled) { 1232 mTicker.removeEntry(old); 1233 } 1234 1235 // Recalculate the position of the sliding windows and the titles. 1236 updateExpandedViewPos(EXPANDED_LEAVE_ALONE); 1237 1238 if (CLOSE_PANEL_WHEN_EMPTIED && mNotificationData.size() == 0 1239 && !mNotificationPanel.isTracking() && mState != StatusBarState.KEYGUARD) { 1240 animateCollapsePanels(); 1241 } 1242 } 1243 setAreThereNotifications(); 1244 } 1245 1246 @Override 1247 protected void refreshLayout(int layoutDirection) { 1248 if (mNavigationBarView != null) { 1249 mNavigationBarView.setLayoutDirection(layoutDirection); 1250 } 1251 refreshAllStatusBarIcons(); 1252 } 1253 1254 private void updateShowSearchHoldoff() { 1255 mShowSearchHoldoff = mContext.getResources().getInteger( 1256 R.integer.config_show_search_delay); 1257 } 1258 1259 private void updateNotificationShade() { 1260 if (mStackScroller == null) return; 1261 1262 int N = mNotificationData.size(); 1263 1264 ArrayList<View> toShow = new ArrayList<View>(); 1265 1266 final boolean provisioned = isDeviceProvisioned(); 1267 // If the device hasn't been through Setup, we only show system notifications 1268 for (int i=0; i<N; i++) { 1269 Entry ent = mNotificationData.get(i); 1270 if (!(provisioned || showNotificationEvenIfUnprovisioned(ent.notification))) continue; 1271 1272 if (!notificationIsForCurrentProfiles(ent.notification)) continue; 1273 1274 final boolean hideSensitive = shouldHideSensitiveContents(ent.notification.getUserId()); 1275 final int vis = ent.notification.getNotification().visibility; 1276 1277 // when isLockscreenPublicMode() we suppress VISIBILITY_SECRET notifications 1278 if (vis == Notification.VISIBILITY_SECRET && hideSensitive) { 1279 continue; 1280 } 1281 1282 // when isLockscreenPublicMode() we show the public form of VISIBILITY_PRIVATE notifications 1283 boolean showingPublic = vis == Notification.VISIBILITY_PRIVATE && hideSensitive; 1284 ent.row.setShowingPublic(showingPublic); 1285 if (ent.autoRedacted && ent.legacy) { 1286 if (showingPublic) { 1287 ent.row.setShowingLegacyBackground(false); 1288 } else { 1289 ent.row.setShowingLegacyBackground(true); 1290 } 1291 } 1292 toShow.add(ent.row); 1293 } 1294 1295 ArrayList<View> toRemove = new ArrayList<View>(); 1296 for (int i=0; i< mStackScroller.getChildCount(); i++) { 1297 View child = mStackScroller.getChildAt(i); 1298 if (!toShow.contains(child) && child instanceof ExpandableNotificationRow) { 1299 toRemove.add(child); 1300 } 1301 } 1302 1303 for (View remove : toRemove) { 1304 mStackScroller.removeView(remove); 1305 } 1306 for (int i=0; i<toShow.size(); i++) { 1307 View v = toShow.get(i); 1308 if (v.getParent() == null) { 1309 mStackScroller.addView(v); 1310 } 1311 } 1312 1313 // So after all this work notifications still aren't sorted correctly. 1314 // Let's do that now by advancing through toShow and mStackScroller in 1315 // lock-step, making sure mStackScroller matches what we see in toShow. 1316 int j = 0; 1317 for (int i = 0; i < mStackScroller.getChildCount(); i++) { 1318 View child = mStackScroller.getChildAt(i); 1319 if (!(child instanceof ExpandableNotificationRow)) { 1320 // We don't care about non-notification views. 1321 continue; 1322 } 1323 1324 if (child == toShow.get(j)) { 1325 // Everything is well, advance both lists. 1326 j++; 1327 continue; 1328 } 1329 1330 // Oops, wrong notification at this position. Put the right one 1331 // here and advance both lists. 1332 mStackScroller.changeViewPosition(toShow.get(j), i); 1333 j++; 1334 } 1335 updateRowStates(); 1336 updateSpeedbump(); 1337 mNotificationPanel.setQsExpansionEnabled(provisioned && mUserSetup); 1338 mShadeUpdates.check(); 1339 } 1340 1341 private void updateSpeedbump() { 1342 int speedbumpIndex = -1; 1343 int currentIndex = 0; 1344 for (int i = 0; i < mNotificationData.size(); i++) { 1345 Entry entry = mNotificationData.get(i); 1346 if (entry.row.getParent() == null) { 1347 // This view isn't even added, so the stack scroller doesn't 1348 // know about it. Ignore completely. 1349 continue; 1350 } 1351 if (entry.row.getVisibility() != View.GONE && 1352 mNotificationData.isAmbient(entry.key)) { 1353 speedbumpIndex = currentIndex; 1354 break; 1355 } 1356 currentIndex++; 1357 } 1358 mStackScroller.updateSpeedBumpIndex(speedbumpIndex); 1359 } 1360 1361 @Override 1362 protected void updateNotifications() { 1363 // TODO: Move this into updateNotificationIcons()? 1364 if (mNotificationIcons == null) return; 1365 1366 updateNotificationShade(); 1367 updateNotificationIcons(); 1368 } 1369 1370 /** 1371 * Returns true if we're on a secure lockscreen and the user wants to hide "sensitive" 1372 * notification data. If so, private notifications should show their (possibly 1373 * auto-generated) publicVersion, and secret notifications should be totally invisible. 1374 */ 1375 private boolean shouldHideSensitiveContents(int userid) { 1376 return isLockscreenPublicMode() && !userAllowsPrivateNotificationsInPublic(userid); 1377 } 1378 1379 private void updateNotificationIcons() { 1380 final LinearLayout.LayoutParams params 1381 = new LinearLayout.LayoutParams(mIconSize + 2*mIconHPadding, mNaturalBarHeight); 1382 1383 int N = mNotificationData.size(); 1384 1385 if (DEBUG) { 1386 Log.d(TAG, "refreshing icons: " + N + " notifications, mNotificationIcons=" + 1387 mNotificationIcons); 1388 } 1389 1390 ArrayList<View> toShow = new ArrayList<View>(); 1391 1392 final boolean provisioned = isDeviceProvisioned(); 1393 // If the device hasn't been through Setup, we only show system notifications 1394 for (int i=0; i<N; i++) { 1395 Entry ent = mNotificationData.get(i); 1396 if (!((provisioned && ent.notification.getScore() >= HIDE_ICONS_BELOW_SCORE) 1397 || showNotificationEvenIfUnprovisioned(ent.notification))) continue; 1398 if (!notificationIsForCurrentProfiles(ent.notification)) continue; 1399 if (ent.notification.getNotification().visibility == Notification.VISIBILITY_SECRET 1400 && shouldHideSensitiveContents(ent.notification.getUserId())) { 1401 // in "public" mode (atop a secure keyguard), secret notifs are totally hidden 1402 continue; 1403 } 1404 toShow.add(ent.icon); 1405 } 1406 1407 ArrayList<View> toRemove = new ArrayList<View>(); 1408 for (int i=0; i<mNotificationIcons.getChildCount(); i++) { 1409 View child = mNotificationIcons.getChildAt(i); 1410 if (!toShow.contains(child)) { 1411 toRemove.add(child); 1412 } 1413 } 1414 1415 for (View remove : toRemove) { 1416 mNotificationIcons.removeView(remove); 1417 } 1418 1419 for (int i=0; i<toShow.size(); i++) { 1420 View v = toShow.get(i); 1421 if (v.getParent() == null) { 1422 mNotificationIcons.addView(v, i, params); 1423 } 1424 } 1425 } 1426 1427 @Override 1428 protected void updateRowStates() { 1429 super.updateRowStates(); 1430 mNotificationPanel.notifyVisibleChildrenChanged(); 1431 } 1432 1433 protected void updateCarrierLabelVisibility(boolean force) { 1434 // TODO: Handle this for the notification stack scroller as well 1435 if (!mShowCarrierInPanel) return; 1436 // The idea here is to only show the carrier label when there is enough room to see it, 1437 // i.e. when there aren't enough notifications to fill the panel. 1438 if (SPEW) { 1439 Log.d(TAG, String.format("stackScrollerh=%d scrollh=%d carrierh=%d", 1440 mStackScroller.getHeight(), mStackScroller.getHeight(), 1441 mCarrierLabelHeight)); 1442 } 1443 1444 // Emergency calls only is shown in the expanded header now. 1445 final boolean emergencyCallsShownElsewhere = true; 1446 final boolean makeVisible = 1447 !(emergencyCallsShownElsewhere && mNetworkController.isEmergencyOnly()) 1448 && mStackScroller.getHeight() < (mNotificationPanel.getHeight() 1449 - mCarrierLabelHeight - mStatusBarHeaderHeight) 1450 && mStackScroller.getVisibility() == View.VISIBLE 1451 && mState != StatusBarState.KEYGUARD; 1452 1453 if (force || mCarrierLabelVisible != makeVisible) { 1454 mCarrierLabelVisible = makeVisible; 1455 if (DEBUG) { 1456 Log.d(TAG, "making carrier label " + (makeVisible?"visible":"invisible")); 1457 } 1458 mCarrierLabel.animate().cancel(); 1459 if (makeVisible) { 1460 mCarrierLabel.setVisibility(View.VISIBLE); 1461 } 1462 mCarrierLabel.animate() 1463 .alpha(makeVisible ? 1f : 0f) 1464 //.setStartDelay(makeVisible ? 500 : 0) 1465 //.setDuration(makeVisible ? 750 : 100) 1466 .setDuration(150) 1467 .setListener(makeVisible ? null : new AnimatorListenerAdapter() { 1468 @Override 1469 public void onAnimationEnd(Animator animation) { 1470 if (!mCarrierLabelVisible) { // race 1471 mCarrierLabel.setVisibility(View.INVISIBLE); 1472 mCarrierLabel.setAlpha(0f); 1473 } 1474 } 1475 }) 1476 .start(); 1477 } 1478 } 1479 1480 @Override 1481 protected void setAreThereNotifications() { 1482 final boolean any = mNotificationData.size() > 0; 1483 1484 final boolean clearable = any && mNotificationData.hasClearableItems(); 1485 1486 if (SPEW) { 1487 Log.d(TAG, "setAreThereNotifications: N=" + mNotificationData.size() 1488 + " any=" + any + " clearable=" + clearable); 1489 } 1490 1491 final View nlo = mStatusBarView.findViewById(R.id.notification_lights_out); 1492 final boolean showDot = (any&&!areLightsOn()); 1493 if (showDot != (nlo.getAlpha() == 1.0f)) { 1494 if (showDot) { 1495 nlo.setAlpha(0f); 1496 nlo.setVisibility(View.VISIBLE); 1497 } 1498 nlo.animate() 1499 .alpha(showDot?1:0) 1500 .setDuration(showDot?750:250) 1501 .setInterpolator(new AccelerateInterpolator(2.0f)) 1502 .setListener(showDot ? null : new AnimatorListenerAdapter() { 1503 @Override 1504 public void onAnimationEnd(Animator _a) { 1505 nlo.setVisibility(View.GONE); 1506 } 1507 }) 1508 .start(); 1509 } 1510 1511 findAndUpdateMediaNotifications(); 1512 1513 updateCarrierLabelVisibility(false); 1514 } 1515 1516 public void findAndUpdateMediaNotifications() { 1517 boolean metaDataChanged = false; 1518 1519 synchronized (mNotificationData) { 1520 final int N = mNotificationData.size(); 1521 Entry mediaNotification = null; 1522 MediaController controller = null; 1523 for (int i=0; i<N; i++) { 1524 final Entry entry = mNotificationData.get(i); 1525 if (isMediaNotification(entry)) { 1526 final MediaSession.Token token = entry.notification.getNotification().extras 1527 .getParcelable(Notification.EXTRA_MEDIA_SESSION); 1528 if (token != null) { 1529 controller = new MediaController(token); 1530 if (controller != null) { 1531 // we've got a live one, here 1532 mediaNotification = entry; 1533 } 1534 } 1535 } 1536 } 1537 1538 if (mediaNotification == null) { 1539 // Still nothing? OK, let's just look for live media sessions and see if they match 1540 // one of our notifications. This will catch apps that aren't (yet!) using media 1541 // notifications. 1542 1543 if (mMediaSessionManager != null) { 1544 final List<MediaController> sessions 1545 = mMediaSessionManager.getActiveSessionsForUser( 1546 null, 1547 UserHandle.USER_ALL); 1548 1549 for (MediaController aController : sessions) { 1550 if (aController == null) continue; 1551 final PlaybackState state = aController.getPlaybackState(); 1552 if (state == null) continue; 1553 switch (state.getState()) { 1554 case PlaybackState.STATE_STOPPED: 1555 case PlaybackState.STATE_ERROR: 1556 continue; 1557 default: 1558 // now to see if we have one like this 1559 final String pkg = aController.getSessionInfo().getPackageName(); 1560 1561 for (int i = 0; i < N; i++) { 1562 final Entry entry = mNotificationData.get(i); 1563 if (entry.notification.getPackageName().equals(pkg)) { 1564 if (DEBUG_MEDIA) { 1565 Log.v(TAG, "DEBUG_MEDIA: found controller matching " 1566 + entry.notification.getKey()); 1567 } 1568 controller = aController; 1569 mediaNotification = entry; 1570 break; 1571 } 1572 } 1573 } 1574 } 1575 } 1576 } 1577 1578 if (controller != mMediaController) { 1579 // We have a new media session 1580 1581 if (mMediaController != null) { 1582 // something old was playing 1583 Log.v(TAG, "DEBUG_MEDIA: Disconnecting from old controller: " 1584 + mMediaController); 1585 mMediaController.removeCallback(mMediaListener); 1586 } 1587 mMediaController = controller; 1588 1589 if (mMediaController != null) { 1590 mMediaController.addCallback(mMediaListener); 1591 mMediaMetadata = mMediaController.getMetadata(); 1592 if (DEBUG_MEDIA) { 1593 Log.v(TAG, "DEBUG_MEDIA: insert listener, receive metadata: " 1594 + mMediaMetadata); 1595 } 1596 1597 final String notificationKey = mediaNotification == null 1598 ? null 1599 : mediaNotification.notification.getKey(); 1600 1601 if (notificationKey == null || !notificationKey.equals(mMediaNotificationKey)) { 1602 // we have a new notification! 1603 if (DEBUG_MEDIA) { 1604 Log.v(TAG, "DEBUG_MEDIA: Found new media notification: key=" 1605 + notificationKey + " controller=" + controller); 1606 } 1607 mMediaNotificationKey = notificationKey; 1608 } 1609 } else { 1610 mMediaMetadata = null; 1611 mMediaNotificationKey = null; 1612 } 1613 1614 metaDataChanged = true; 1615 } else { 1616 // Media session unchanged 1617 1618 if (DEBUG_MEDIA) { 1619 Log.v(TAG, "DEBUG_MEDIA: Continuing media notification: key=" + mMediaNotificationKey); 1620 } 1621 } 1622 } 1623 1624 updateMediaMetaData(metaDataChanged); 1625 } 1626 1627 /** 1628 * Hide the album artwork that is fading out and release its bitmap. 1629 */ 1630 private Runnable mHideBackdropFront = new Runnable() { 1631 @Override 1632 public void run() { 1633 if (DEBUG_MEDIA) { 1634 Log.v(TAG, "DEBUG_MEDIA: removing fade layer"); 1635 } 1636 mBackdropFront.setVisibility(View.INVISIBLE); 1637 mBackdropFront.animate().cancel(); 1638 mBackdropFront.setImageDrawable(null); 1639 } 1640 }; 1641 1642 /** 1643 * Refresh or remove lockscreen artwork from media metadata. 1644 */ 1645 public void updateMediaMetaData(boolean metaDataChanged) { 1646 if (!SHOW_LOCKSCREEN_MEDIA_ARTWORK) return; 1647 1648 if (mBackdrop == null) return; // called too early 1649 1650 if (DEBUG_MEDIA) { 1651 Log.v(TAG, "DEBUG_MEDIA: updating album art for notification " + mMediaNotificationKey 1652 + " metadata=" + mMediaMetadata 1653 + " metaDataChanged=" + metaDataChanged 1654 + " state=" + mState); 1655 } 1656 1657 Bitmap artworkBitmap = null; 1658 if (mMediaMetadata != null) { 1659 artworkBitmap = mMediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART); 1660 if (artworkBitmap == null) { 1661 artworkBitmap = mMediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART); 1662 // might still be null 1663 } 1664 } 1665 1666 final boolean hasArtwork = artworkBitmap != null; 1667 1668 if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK) 1669 && (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)) { 1670 // time to show some art! 1671 if (mBackdrop.getVisibility() != View.VISIBLE) { 1672 mBackdrop.setVisibility(View.VISIBLE); 1673 mBackdrop.animate().alpha(1f); 1674 metaDataChanged = true; 1675 if (DEBUG_MEDIA) { 1676 Log.v(TAG, "DEBUG_MEDIA: Fading in album artwork"); 1677 } 1678 } 1679 if (metaDataChanged) { 1680 if (mBackdropBack.getDrawable() != null) { 1681 mBackdropFront.setImageDrawable(mBackdropBack.getDrawable()); 1682 mBackdropFront.setAlpha(1f); 1683 mBackdropFront.setVisibility(View.VISIBLE); 1684 } else { 1685 mBackdropFront.setVisibility(View.INVISIBLE); 1686 } 1687 1688 if (DEBUG_MEDIA_FAKE_ARTWORK) { 1689 final int c = 0xFF000000 | (int)(Math.random() * 0xFFFFFF); 1690 Log.v(TAG, String.format("DEBUG_MEDIA: setting new color: 0x%08x", c)); 1691 mBackdropBack.setBackgroundColor(0xFFFFFFFF); 1692 mBackdropBack.setImageDrawable(new ColorDrawable(c)); 1693 } else { 1694 mBackdropBack.setImageBitmap(artworkBitmap); 1695 } 1696 1697 if (mBackdropFront.getVisibility() == View.VISIBLE) { 1698 if (DEBUG_MEDIA) { 1699 Log.v(TAG, "DEBUG_MEDIA: Crossfading album artwork from " 1700 + mBackdropFront.getDrawable() 1701 + " to " 1702 + mBackdropBack.getDrawable()); 1703 } 1704 mBackdropFront.animate().withLayer() 1705 .setDuration(250) 1706 .alpha(0f).withEndAction(mHideBackdropFront); 1707 } 1708 } 1709 } else { 1710 // need to hide the album art, either because we are unlocked or because 1711 // the metadata isn't there to support it 1712 if (mBackdrop.getVisibility() != View.GONE) { 1713 if (DEBUG_MEDIA) { 1714 Log.v(TAG, "DEBUG_MEDIA: Fading out album artwork"); 1715 } 1716 mBackdrop.animate().withLayer() 1717 .alpha(0f).withEndAction(new Runnable() { 1718 @Override 1719 public void run() { 1720 mBackdrop.setVisibility(View.GONE); 1721 mBackdropFront.animate().cancel(); 1722 mBackdropBack.animate().cancel(); 1723 mHandler.post(mHideBackdropFront); 1724 } 1725 }); 1726 } 1727 } 1728 } 1729 1730 public void showClock(boolean show) { 1731 if (mStatusBarView == null) return; 1732 View clock = mStatusBarView.findViewById(R.id.clock); 1733 if (clock != null) { 1734 clock.setVisibility(show ? View.VISIBLE : View.GONE); 1735 } 1736 } 1737 1738 private int adjustDisableFlags(int state) { 1739 if (!mLaunchTransitionFadingAway 1740 && (mExpandedVisible || mBouncerShowing || mWaitingForKeyguardExit)) { 1741 state |= StatusBarManager.DISABLE_NOTIFICATION_ICONS; 1742 state |= StatusBarManager.DISABLE_SYSTEM_INFO; 1743 } 1744 return state; 1745 } 1746 1747 /** 1748 * State is one or more of the DISABLE constants from StatusBarManager. 1749 */ 1750 public void disable(int state, boolean animate) { 1751 mDisabledUnmodified = state; 1752 state = adjustDisableFlags(state); 1753 final int old = mDisabled; 1754 final int diff = state ^ old; 1755 mDisabled = state; 1756 1757 if (DEBUG) { 1758 Log.d(TAG, String.format("disable: 0x%08x -> 0x%08x (diff: 0x%08x)", 1759 old, state, diff)); 1760 } 1761 1762 StringBuilder flagdbg = new StringBuilder(); 1763 flagdbg.append("disable: < "); 1764 flagdbg.append(((state & StatusBarManager.DISABLE_EXPAND) != 0) ? "EXPAND" : "expand"); 1765 flagdbg.append(((diff & StatusBarManager.DISABLE_EXPAND) != 0) ? "* " : " "); 1766 flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "ICONS" : "icons"); 1767 flagdbg.append(((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "* " : " "); 1768 flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "ALERTS" : "alerts"); 1769 flagdbg.append(((diff & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "* " : " "); 1770 flagdbg.append(((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "SYSTEM_INFO" : "system_info"); 1771 flagdbg.append(((diff & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "* " : " "); 1772 flagdbg.append(((state & StatusBarManager.DISABLE_BACK) != 0) ? "BACK" : "back"); 1773 flagdbg.append(((diff & StatusBarManager.DISABLE_BACK) != 0) ? "* " : " "); 1774 flagdbg.append(((state & StatusBarManager.DISABLE_HOME) != 0) ? "HOME" : "home"); 1775 flagdbg.append(((diff & StatusBarManager.DISABLE_HOME) != 0) ? "* " : " "); 1776 flagdbg.append(((state & StatusBarManager.DISABLE_RECENT) != 0) ? "RECENT" : "recent"); 1777 flagdbg.append(((diff & StatusBarManager.DISABLE_RECENT) != 0) ? "* " : " "); 1778 flagdbg.append(((state & StatusBarManager.DISABLE_CLOCK) != 0) ? "CLOCK" : "clock"); 1779 flagdbg.append(((diff & StatusBarManager.DISABLE_CLOCK) != 0) ? "* " : " "); 1780 flagdbg.append(((state & StatusBarManager.DISABLE_SEARCH) != 0) ? "SEARCH" : "search"); 1781 flagdbg.append(((diff & StatusBarManager.DISABLE_SEARCH) != 0) ? "* " : " "); 1782 flagdbg.append(">"); 1783 Log.d(TAG, flagdbg.toString()); 1784 1785 if ((diff & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) { 1786 mSystemIconArea.animate().cancel(); 1787 if ((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) { 1788 animateStatusBarHide(mSystemIconArea, animate); 1789 } else { 1790 animateStatusBarShow(mSystemIconArea, animate); 1791 } 1792 } 1793 1794 if ((diff & StatusBarManager.DISABLE_CLOCK) != 0) { 1795 boolean show = (state & StatusBarManager.DISABLE_CLOCK) == 0; 1796 showClock(show); 1797 } 1798 if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) { 1799 if ((state & StatusBarManager.DISABLE_EXPAND) != 0) { 1800 animateCollapsePanels(); 1801 } 1802 } 1803 1804 if ((diff & (StatusBarManager.DISABLE_HOME 1805 | StatusBarManager.DISABLE_RECENT 1806 | StatusBarManager.DISABLE_BACK 1807 | StatusBarManager.DISABLE_SEARCH)) != 0) { 1808 // the nav bar will take care of these 1809 if (mNavigationBarView != null) mNavigationBarView.setDisabledFlags(state); 1810 1811 if ((state & StatusBarManager.DISABLE_RECENT) != 0) { 1812 // close recents if it's visible 1813 mHandler.removeMessages(MSG_HIDE_RECENT_APPS); 1814 mHandler.sendEmptyMessage(MSG_HIDE_RECENT_APPS); 1815 } 1816 } 1817 1818 if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) { 1819 if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) { 1820 if (mTicking) { 1821 haltTicker(); 1822 } 1823 animateStatusBarHide(mNotificationIconArea, animate); 1824 } else { 1825 animateStatusBarShow(mNotificationIconArea, animate); 1826 } 1827 } 1828 } 1829 1830 /** 1831 * Animates {@code v}, a view that is part of the status bar, out. 1832 */ 1833 private void animateStatusBarHide(final View v, boolean animate) { 1834 v.animate().cancel(); 1835 if (!animate) { 1836 v.setAlpha(0f); 1837 v.setVisibility(View.INVISIBLE); 1838 return; 1839 } 1840 v.animate() 1841 .alpha(0f) 1842 .setDuration(160) 1843 .setStartDelay(0) 1844 .setInterpolator(mAlphaOut) 1845 .withEndAction(new Runnable() { 1846 @Override 1847 public void run() { 1848 v.setVisibility(View.INVISIBLE); 1849 } 1850 }); 1851 } 1852 1853 /** 1854 * Animates {@code v}, a view that is part of the status bar, in. 1855 */ 1856 private void animateStatusBarShow(View v, boolean animate) { 1857 v.animate().cancel(); 1858 v.setVisibility(View.VISIBLE); 1859 if (!animate) { 1860 v.setAlpha(1f); 1861 return; 1862 } 1863 v.animate() 1864 .alpha(1f) 1865 .setDuration(320) 1866 .setInterpolator(mAlphaIn) 1867 .setStartDelay(50); 1868 1869 // Synchronize the motion with the Keyguard fading if necessary. 1870 if (mKeyguardFadingAway) { 1871 v.animate() 1872 .setDuration(mKeyguardFadingAwayDuration) 1873 .setInterpolator(mLinearOutSlowIn) 1874 .setStartDelay(mKeyguardFadingAwayDelay) 1875 .start(); 1876 } 1877 } 1878 1879 @Override 1880 protected BaseStatusBar.H createHandler() { 1881 return new PhoneStatusBar.H(); 1882 } 1883 1884 @Override 1885 public void startActivity(Intent intent) { 1886 startActivityDismissingKeyguard(intent, false); 1887 } 1888 1889 public ScrimController getScrimController() { 1890 return mScrimController; 1891 } 1892 1893 public void setQsExpanded(boolean expanded) { 1894 mStatusBarWindowManager.setQsExpanded(expanded); 1895 } 1896 1897 /** 1898 * All changes to the status bar and notifications funnel through here and are batched. 1899 */ 1900 private class H extends BaseStatusBar.H { 1901 public void handleMessage(Message m) { 1902 super.handleMessage(m); 1903 switch (m.what) { 1904 case MSG_OPEN_NOTIFICATION_PANEL: 1905 animateExpandNotificationsPanel(); 1906 break; 1907 case MSG_OPEN_SETTINGS_PANEL: 1908 animateExpandSettingsPanel(); 1909 break; 1910 case MSG_CLOSE_PANELS: 1911 animateCollapsePanels(); 1912 break; 1913 case MSG_SHOW_HEADS_UP: 1914 setHeadsUpVisibility(true); 1915 break; 1916 case MSG_DECAY_HEADS_UP: 1917 mHeadsUpNotificationView.release(); 1918 setHeadsUpVisibility(false); 1919 break; 1920 case MSG_HIDE_HEADS_UP: 1921 mHeadsUpNotificationView.release(); 1922 setHeadsUpVisibility(false); 1923 break; 1924 case MSG_ESCALATE_HEADS_UP: 1925 escalateHeadsUp(); 1926 setHeadsUpVisibility(false); 1927 break; 1928 } 1929 } 1930 } 1931 1932 /** if the interrupting notification had a fullscreen intent, fire it now. */ 1933 private void escalateHeadsUp() { 1934 if (mHeadsUpNotificationView.getEntry() != null) { 1935 final StatusBarNotification sbn = mHeadsUpNotificationView.getEntry().notification; 1936 mHeadsUpNotificationView.release(); 1937 final Notification notification = sbn.getNotification(); 1938 if (notification.fullScreenIntent != null) { 1939 if (DEBUG) 1940 Log.d(TAG, "converting a heads up to fullScreen"); 1941 try { 1942 notification.fullScreenIntent.send(); 1943 } catch (PendingIntent.CanceledException e) { 1944 } 1945 } 1946 } 1947 } 1948 1949 View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() { 1950 public void onFocusChange(View v, boolean hasFocus) { 1951 // Because 'v' is a ViewGroup, all its children will be (un)selected 1952 // too, which allows marqueeing to work. 1953 v.setSelected(hasFocus); 1954 } 1955 }; 1956 1957 boolean panelsEnabled() { 1958 return (mDisabled & StatusBarManager.DISABLE_EXPAND) == 0; 1959 } 1960 1961 void makeExpandedVisible(boolean force) { 1962 if (SPEW) Log.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible); 1963 if (!force && (mExpandedVisible || !panelsEnabled())) { 1964 return; 1965 } 1966 1967 mExpandedVisible = true; 1968 if (mNavigationBarView != null) 1969 mNavigationBarView.setSlippery(true); 1970 1971 updateCarrierLabelVisibility(true); 1972 1973 updateExpandedViewPos(EXPANDED_LEAVE_ALONE); 1974 1975 // Expand the window to encompass the full screen in anticipation of the drag. 1976 // This is only possible to do atomically because the status bar is at the top of the screen! 1977 mStatusBarWindowManager.setStatusBarExpanded(true); 1978 1979 visibilityChanged(true); 1980 mWaitingForKeyguardExit = false; 1981 disable(mDisabledUnmodified, !force /* animate */); 1982 setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true); 1983 } 1984 1985 public void animateCollapsePanels() { 1986 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); 1987 } 1988 1989 private final Runnable mAnimateCollapsePanels = new Runnable() { 1990 @Override 1991 public void run() { 1992 animateCollapsePanels(); 1993 } 1994 }; 1995 1996 public void postAnimateCollapsePanels() { 1997 mHandler.post(mAnimateCollapsePanels); 1998 } 1999 2000 public void animateCollapsePanels(int flags) { 2001 animateCollapsePanels(flags, false /* force */); 2002 } 2003 2004 public void animateCollapsePanels(int flags, boolean force) { 2005 if (!force && 2006 (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)) { 2007 return; 2008 } 2009 if (SPEW) { 2010 Log.d(TAG, "animateCollapse():" 2011 + " mExpandedVisible=" + mExpandedVisible 2012 + " flags=" + flags); 2013 } 2014 2015 if ((flags & CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL) == 0) { 2016 mHandler.removeMessages(MSG_HIDE_RECENT_APPS); 2017 mHandler.sendEmptyMessage(MSG_HIDE_RECENT_APPS); 2018 } 2019 2020 if ((flags & CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL) == 0) { 2021 mHandler.removeMessages(MSG_CLOSE_SEARCH_PANEL); 2022 mHandler.sendEmptyMessage(MSG_CLOSE_SEARCH_PANEL); 2023 } 2024 2025 if (mStatusBarWindow != null) { 2026 // release focus immediately to kick off focus change transition 2027 mStatusBarWindowManager.setStatusBarFocusable(false); 2028 2029 mStatusBarWindow.cancelExpandHelper(); 2030 mStatusBarView.collapseAllPanels(true); 2031 } 2032 } 2033 2034 public ViewPropertyAnimator setVisibilityWhenDone( 2035 final ViewPropertyAnimator a, final View v, final int vis) { 2036 a.setListener(new AnimatorListenerAdapter() { 2037 @Override 2038 public void onAnimationEnd(Animator animation) { 2039 v.setVisibility(vis); 2040 a.setListener(null); // oneshot 2041 } 2042 }); 2043 return a; 2044 } 2045 2046 public Animator setVisibilityWhenDone( 2047 final Animator a, final View v, final int vis) { 2048 a.addListener(new AnimatorListenerAdapter() { 2049 @Override 2050 public void onAnimationEnd(Animator animation) { 2051 v.setVisibility(vis); 2052 } 2053 }); 2054 return a; 2055 } 2056 2057 public Animator interpolator(TimeInterpolator ti, Animator a) { 2058 a.setInterpolator(ti); 2059 return a; 2060 } 2061 2062 public Animator startDelay(int d, Animator a) { 2063 a.setStartDelay(d); 2064 return a; 2065 } 2066 2067 public Animator start(Animator a) { 2068 a.start(); 2069 return a; 2070 } 2071 2072 final TimeInterpolator mAccelerateInterpolator = new AccelerateInterpolator(); 2073 final TimeInterpolator mDecelerateInterpolator = new DecelerateInterpolator(); 2074 final int FLIP_DURATION_OUT = 125; 2075 final int FLIP_DURATION_IN = 225; 2076 final int FLIP_DURATION = (FLIP_DURATION_IN + FLIP_DURATION_OUT); 2077 2078 Animator mScrollViewAnim, mClearButtonAnim; 2079 2080 @Override 2081 public void animateExpandNotificationsPanel() { 2082 if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible); 2083 if (!panelsEnabled()) { 2084 return ; 2085 } 2086 2087 mNotificationPanel.expand(); 2088 2089 if (false) postStartTracing(); 2090 } 2091 2092 @Override 2093 public void animateExpandSettingsPanel() { 2094 if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible); 2095 if (!panelsEnabled()) { 2096 return; 2097 } 2098 2099 // Settings are not available in setup 2100 if (!mUserSetup) return; 2101 2102 mNotificationPanel.expand(); 2103 mNotificationPanel.openQs(); 2104 2105 if (false) postStartTracing(); 2106 } 2107 2108 public void animateCollapseQuickSettings() { 2109 mStatusBarView.collapseAllPanels(true); 2110 } 2111 2112 void makeExpandedInvisible() { 2113 if (SPEW) Log.d(TAG, "makeExpandedInvisible: mExpandedVisible=" + mExpandedVisible 2114 + " mExpandedVisible=" + mExpandedVisible); 2115 2116 if (!mExpandedVisible || mStatusBarWindow == null) { 2117 return; 2118 } 2119 2120 // Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868) 2121 mStatusBarView.collapseAllPanels(/*animate=*/ false); 2122 2123 // reset things to their proper state 2124 if (mScrollViewAnim != null) mScrollViewAnim.cancel(); 2125 if (mClearButtonAnim != null) mClearButtonAnim.cancel(); 2126 2127 mStackScroller.setVisibility(View.VISIBLE); 2128 mNotificationPanel.setVisibility(View.GONE); 2129 2130 setAreThereNotifications(); // show the clear button 2131 2132 mNotificationPanel.closeQs(); 2133 2134 mExpandedVisible = false; 2135 if (mNavigationBarView != null) 2136 mNavigationBarView.setSlippery(false); 2137 visibilityChanged(false); 2138 2139 // Shrink the window to the size of the status bar only 2140 mStatusBarWindowManager.setStatusBarExpanded(false); 2141 2142 // Close any "App info" popups that might have snuck on-screen 2143 dismissPopups(); 2144 2145 if (mPostCollapseCleanup != null) { 2146 mPostCollapseCleanup.run(); 2147 mPostCollapseCleanup = null; 2148 } 2149 2150 setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false); 2151 showBouncer(); 2152 disable(mDisabledUnmodified, true /* animate */); 2153 } 2154 2155 public boolean interceptTouchEvent(MotionEvent event) { 2156 if (DEBUG_GESTURES) { 2157 if (event.getActionMasked() != MotionEvent.ACTION_MOVE) { 2158 EventLog.writeEvent(EventLogTags.SYSUI_STATUSBAR_TOUCH, 2159 event.getActionMasked(), (int) event.getX(), (int) event.getY(), mDisabled); 2160 } 2161 2162 } 2163 2164 if (SPEW) { 2165 Log.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled=" 2166 + mDisabled + " mTracking=" + mTracking); 2167 } else if (CHATTY) { 2168 if (event.getAction() != MotionEvent.ACTION_MOVE) { 2169 Log.d(TAG, String.format( 2170 "panel: %s at (%f, %f) mDisabled=0x%08x", 2171 MotionEvent.actionToString(event.getAction()), 2172 event.getRawX(), event.getRawY(), mDisabled)); 2173 } 2174 } 2175 2176 if (DEBUG_GESTURES) { 2177 mGestureRec.add(event); 2178 } 2179 2180 if (mStatusBarWindowState == WINDOW_STATE_SHOWING) { 2181 final boolean upOrCancel = 2182 event.getAction() == MotionEvent.ACTION_UP || 2183 event.getAction() == MotionEvent.ACTION_CANCEL; 2184 if (upOrCancel && !mExpandedVisible) { 2185 setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false); 2186 } else { 2187 setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true); 2188 } 2189 } 2190 return false; 2191 } 2192 2193 public GestureRecorder getGestureRecorder() { 2194 return mGestureRec; 2195 } 2196 2197 private void setNavigationIconHints(int hints) { 2198 if (hints == mNavigationIconHints) return; 2199 2200 mNavigationIconHints = hints; 2201 2202 if (mNavigationBarView != null) { 2203 mNavigationBarView.setNavigationIconHints(hints); 2204 } 2205 checkBarModes(); 2206 } 2207 2208 @Override // CommandQueue 2209 public void setWindowState(int window, int state) { 2210 boolean showing = state == WINDOW_STATE_SHOWING; 2211 if (mStatusBarWindow != null 2212 && window == StatusBarManager.WINDOW_STATUS_BAR 2213 && mStatusBarWindowState != state) { 2214 mStatusBarWindowState = state; 2215 if (DEBUG_WINDOW_STATE) Log.d(TAG, "Status bar " + windowStateToString(state)); 2216 if (!showing) { 2217 mStatusBarView.collapseAllPanels(false); 2218 } 2219 } 2220 if (mNavigationBarView != null 2221 && window == StatusBarManager.WINDOW_NAVIGATION_BAR 2222 && mNavigationBarWindowState != state) { 2223 mNavigationBarWindowState = state; 2224 if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state)); 2225 } 2226 } 2227 2228 @Override // CommandQueue 2229 public void setSystemUiVisibility(int vis, int mask) { 2230 final int oldVal = mSystemUiVisibility; 2231 final int newVal = (oldVal&~mask) | (vis&mask); 2232 final int diff = newVal ^ oldVal; 2233 if (DEBUG) Log.d(TAG, String.format( 2234 "setSystemUiVisibility vis=%s mask=%s oldVal=%s newVal=%s diff=%s", 2235 Integer.toHexString(vis), Integer.toHexString(mask), 2236 Integer.toHexString(oldVal), Integer.toHexString(newVal), 2237 Integer.toHexString(diff))); 2238 if (diff != 0) { 2239 // we never set the recents bit via this method, so save the prior state to prevent 2240 // clobbering the bit below 2241 final boolean wasRecentsVisible = (mSystemUiVisibility & View.RECENT_APPS_VISIBLE) > 0; 2242 2243 mSystemUiVisibility = newVal; 2244 2245 // update low profile 2246 if ((diff & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) { 2247 final boolean lightsOut = (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0; 2248 if (lightsOut) { 2249 animateCollapsePanels(); 2250 if (mTicking) { 2251 haltTicker(); 2252 } 2253 } 2254 2255 setAreThereNotifications(); 2256 } 2257 2258 // update status bar mode 2259 final int sbMode = computeBarMode(oldVal, newVal, mStatusBarView.getBarTransitions(), 2260 View.STATUS_BAR_TRANSIENT, View.STATUS_BAR_TRANSLUCENT); 2261 2262 // update navigation bar mode 2263 final int nbMode = mNavigationBarView == null ? -1 : computeBarMode( 2264 oldVal, newVal, mNavigationBarView.getBarTransitions(), 2265 View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_TRANSLUCENT); 2266 final boolean sbModeChanged = sbMode != -1; 2267 final boolean nbModeChanged = nbMode != -1; 2268 boolean checkBarModes = false; 2269 if (sbModeChanged && sbMode != mStatusBarMode) { 2270 mStatusBarMode = sbMode; 2271 checkBarModes = true; 2272 } 2273 if (nbModeChanged && nbMode != mNavigationBarMode) { 2274 mNavigationBarMode = nbMode; 2275 checkBarModes = true; 2276 } 2277 if (checkBarModes) { 2278 checkBarModes(); 2279 } 2280 if (sbModeChanged || nbModeChanged) { 2281 // update transient bar autohide 2282 if (mStatusBarMode == MODE_SEMI_TRANSPARENT || mNavigationBarMode == MODE_SEMI_TRANSPARENT) { 2283 scheduleAutohide(); 2284 } else { 2285 cancelAutohide(); 2286 } 2287 } 2288 2289 // ready to unhide 2290 if ((vis & View.STATUS_BAR_UNHIDE) != 0) { 2291 mSystemUiVisibility &= ~View.STATUS_BAR_UNHIDE; 2292 } 2293 if ((vis & View.NAVIGATION_BAR_UNHIDE) != 0) { 2294 mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE; 2295 } 2296 2297 // restore the recents bit 2298 if (wasRecentsVisible) { 2299 mSystemUiVisibility |= View.RECENT_APPS_VISIBLE; 2300 } 2301 2302 // send updated sysui visibility to window manager 2303 notifyUiVisibilityChanged(mSystemUiVisibility); 2304 } 2305 } 2306 2307 private int computeBarMode(int oldVis, int newVis, BarTransitions transitions, 2308 int transientFlag, int translucentFlag) { 2309 final int oldMode = barMode(oldVis, transientFlag, translucentFlag); 2310 final int newMode = barMode(newVis, transientFlag, translucentFlag); 2311 if (oldMode == newMode) { 2312 return -1; // no mode change 2313 } 2314 return newMode; 2315 } 2316 2317 private int barMode(int vis, int transientFlag, int translucentFlag) { 2318 return (vis & transientFlag) != 0 ? MODE_SEMI_TRANSPARENT 2319 : (vis & translucentFlag) != 0 ? MODE_TRANSLUCENT 2320 : (vis & View.SYSTEM_UI_TRANSPARENT) != 0 ? MODE_TRANSPARENT 2321 : (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0 ? MODE_LIGHTS_OUT 2322 : MODE_OPAQUE; 2323 } 2324 2325 private void checkBarModes() { 2326 if (mDemoMode) return; 2327 checkBarMode(mStatusBarMode, mStatusBarWindowState, mStatusBarView.getBarTransitions()); 2328 if (mNavigationBarView != null) { 2329 checkBarMode(mNavigationBarMode, 2330 mNavigationBarWindowState, mNavigationBarView.getBarTransitions()); 2331 } 2332 } 2333 2334 private void checkBarMode(int mode, int windowState, BarTransitions transitions) { 2335 final boolean powerSave = mBatteryController.isPowerSave(); 2336 final boolean anim = (mScreenOn == null || mScreenOn) && windowState != WINDOW_STATE_HIDDEN 2337 && !powerSave; 2338 if (powerSave && getBarState() != StatusBarState.KEYGUARD) { 2339 mode = MODE_WARNING; 2340 } 2341 transitions.transitionTo(mode, anim); 2342 } 2343 2344 private void finishBarAnimations() { 2345 mStatusBarView.getBarTransitions().finishAnimations(); 2346 if (mNavigationBarView != null) { 2347 mNavigationBarView.getBarTransitions().finishAnimations(); 2348 } 2349 } 2350 2351 private final Runnable mCheckBarModes = new Runnable() { 2352 @Override 2353 public void run() { 2354 checkBarModes(); 2355 } 2356 }; 2357 2358 @Override 2359 public void setInteracting(int barWindow, boolean interacting) { 2360 mInteractingWindows = interacting 2361 ? (mInteractingWindows | barWindow) 2362 : (mInteractingWindows & ~barWindow); 2363 if (mInteractingWindows != 0) { 2364 suspendAutohide(); 2365 } else { 2366 resumeSuspendedAutohide(); 2367 } 2368 checkBarModes(); 2369 } 2370 2371 private void resumeSuspendedAutohide() { 2372 if (mAutohideSuspended) { 2373 scheduleAutohide(); 2374 mHandler.postDelayed(mCheckBarModes, 500); // longer than home -> launcher 2375 } 2376 } 2377 2378 private void suspendAutohide() { 2379 mHandler.removeCallbacks(mAutohide); 2380 mHandler.removeCallbacks(mCheckBarModes); 2381 mAutohideSuspended = (mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0; 2382 } 2383 2384 private void cancelAutohide() { 2385 mAutohideSuspended = false; 2386 mHandler.removeCallbacks(mAutohide); 2387 } 2388 2389 private void scheduleAutohide() { 2390 cancelAutohide(); 2391 mHandler.postDelayed(mAutohide, AUTOHIDE_TIMEOUT_MS); 2392 } 2393 2394 private void checkUserAutohide(View v, MotionEvent event) { 2395 if ((mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0 // a transient bar is revealed 2396 && event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar 2397 && event.getX() == 0 && event.getY() == 0 // a touch outside both bars 2398 ) { 2399 userAutohide(); 2400 } 2401 } 2402 2403 private void userAutohide() { 2404 cancelAutohide(); 2405 mHandler.postDelayed(mAutohide, 350); // longer than app gesture -> flag clear 2406 } 2407 2408 private boolean areLightsOn() { 2409 return 0 == (mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE); 2410 } 2411 2412 public void setLightsOn(boolean on) { 2413 Log.v(TAG, "setLightsOn(" + on + ")"); 2414 if (on) { 2415 setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE); 2416 } else { 2417 setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE, View.SYSTEM_UI_FLAG_LOW_PROFILE); 2418 } 2419 } 2420 2421 private void notifyUiVisibilityChanged(int vis) { 2422 try { 2423 mWindowManagerService.statusBarVisibilityChanged(vis); 2424 } catch (RemoteException ex) { 2425 } 2426 } 2427 2428 public void topAppWindowChanged(boolean showMenu) { 2429 if (DEBUG) { 2430 Log.d(TAG, (showMenu?"showing":"hiding") + " the MENU button"); 2431 } 2432 if (mNavigationBarView != null) { 2433 mNavigationBarView.setMenuVisibility(showMenu); 2434 } 2435 2436 // See above re: lights-out policy for legacy apps. 2437 if (showMenu) setLightsOn(true); 2438 } 2439 2440 @Override 2441 public void setImeWindowStatus(IBinder token, int vis, int backDisposition, 2442 boolean showImeSwitcher) { 2443 boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0; 2444 int flags = mNavigationIconHints; 2445 if ((backDisposition == InputMethodService.BACK_DISPOSITION_WILL_DISMISS) || imeShown) { 2446 flags |= NAVIGATION_HINT_BACK_ALT; 2447 } else { 2448 flags &= ~NAVIGATION_HINT_BACK_ALT; 2449 } 2450 if (showImeSwitcher) { 2451 flags |= NAVIGATION_HINT_IME_SHOWN; 2452 } else { 2453 flags &= ~NAVIGATION_HINT_IME_SHOWN; 2454 } 2455 2456 setNavigationIconHints(flags); 2457 } 2458 2459 @Override 2460 public void setHardKeyboardStatus(boolean available, boolean enabled) {} 2461 2462 @Override 2463 protected void tick(StatusBarNotification n, boolean firstTime) { 2464 if (!mTickerEnabled) return; 2465 2466 // no ticking in lights-out mode 2467 if (!areLightsOn()) return; 2468 2469 // no ticking in Setup 2470 if (!isDeviceProvisioned()) return; 2471 2472 // not for you 2473 if (!notificationIsForCurrentProfiles(n)) return; 2474 2475 // Show the ticker if one is requested. Also don't do this 2476 // until status bar window is attached to the window manager, 2477 // because... well, what's the point otherwise? And trying to 2478 // run a ticker without being attached will crash! 2479 if (n.getNotification().tickerText != null && mStatusBarWindow != null 2480 && mStatusBarWindow.getWindowToken() != null) { 2481 if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS 2482 | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) { 2483 mTicker.addEntry(n); 2484 } 2485 } 2486 } 2487 2488 private class MyTicker extends Ticker { 2489 MyTicker(Context context, View sb) { 2490 super(context, sb); 2491 if (!mTickerEnabled) { 2492 Log.w(TAG, "MyTicker instantiated with mTickerEnabled=false", new Throwable()); 2493 } 2494 } 2495 2496 @Override 2497 public void tickerStarting() { 2498 if (!mTickerEnabled) return; 2499 mTicking = true; 2500 mStatusBarContents.setVisibility(View.GONE); 2501 mTickerView.setVisibility(View.VISIBLE); 2502 mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null)); 2503 mStatusBarContents.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null)); 2504 } 2505 2506 @Override 2507 public void tickerDone() { 2508 if (!mTickerEnabled) return; 2509 mStatusBarContents.setVisibility(View.VISIBLE); 2510 mTickerView.setVisibility(View.GONE); 2511 mStatusBarContents.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null)); 2512 mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out, 2513 mTickingDoneListener)); 2514 } 2515 2516 public void tickerHalting() { 2517 if (!mTickerEnabled) return; 2518 if (mStatusBarContents.getVisibility() != View.VISIBLE) { 2519 mStatusBarContents.setVisibility(View.VISIBLE); 2520 mStatusBarContents 2521 .startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null)); 2522 } 2523 mTickerView.setVisibility(View.GONE); 2524 // we do not animate the ticker away at this point, just get rid of it (b/6992707) 2525 } 2526 } 2527 2528 Animation.AnimationListener mTickingDoneListener = new Animation.AnimationListener() {; 2529 public void onAnimationEnd(Animation animation) { 2530 mTicking = false; 2531 } 2532 public void onAnimationRepeat(Animation animation) { 2533 } 2534 public void onAnimationStart(Animation animation) { 2535 } 2536 }; 2537 2538 private Animation loadAnim(int id, Animation.AnimationListener listener) { 2539 Animation anim = AnimationUtils.loadAnimation(mContext, id); 2540 if (listener != null) { 2541 anim.setAnimationListener(listener); 2542 } 2543 return anim; 2544 } 2545 2546 public static String viewInfo(View v) { 2547 return "[(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom() 2548 + ") " + v.getWidth() + "x" + v.getHeight() + "]"; 2549 } 2550 2551 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 2552 synchronized (mQueueLock) { 2553 pw.println("Current Status Bar state:"); 2554 pw.println(" mExpandedVisible=" + mExpandedVisible 2555 + ", mTrackingPosition=" + mTrackingPosition); 2556 pw.println(" mTickerEnabled=" + mTickerEnabled); 2557 if (mTickerEnabled) { 2558 pw.println(" mTicking=" + mTicking); 2559 pw.println(" mTickerView: " + viewInfo(mTickerView)); 2560 } 2561 pw.println(" mTracking=" + mTracking); 2562 pw.println(" mDisplayMetrics=" + mDisplayMetrics); 2563 pw.println(" mStackScroller: " + viewInfo(mStackScroller)); 2564 pw.println(" mStackScroller: " + viewInfo(mStackScroller) 2565 + " scroll " + mStackScroller.getScrollX() 2566 + "," + mStackScroller.getScrollY()); 2567 } 2568 2569 pw.print(" mInteractingWindows="); pw.println(mInteractingWindows); 2570 pw.print(" mStatusBarWindowState="); 2571 pw.println(windowStateToString(mStatusBarWindowState)); 2572 pw.print(" mStatusBarMode="); 2573 pw.println(BarTransitions.modeToString(mStatusBarMode)); 2574 pw.print(" mDozing="); pw.println(mDozing); 2575 pw.print(" mZenMode="); 2576 pw.println(Settings.Global.zenModeToString(mZenMode)); 2577 pw.print(" mUseHeadsUp="); 2578 pw.println(mUseHeadsUp); 2579 pw.print(" interrupting package: "); 2580 pw.println(hunStateToString(mHeadsUpNotificationView.getEntry())); 2581 dumpBarTransitions(pw, "mStatusBarView", mStatusBarView.getBarTransitions()); 2582 if (mNavigationBarView != null) { 2583 pw.print(" mNavigationBarWindowState="); 2584 pw.println(windowStateToString(mNavigationBarWindowState)); 2585 pw.print(" mNavigationBarMode="); 2586 pw.println(BarTransitions.modeToString(mNavigationBarMode)); 2587 dumpBarTransitions(pw, "mNavigationBarView", mNavigationBarView.getBarTransitions()); 2588 } 2589 2590 pw.print(" mNavigationBarView="); 2591 if (mNavigationBarView == null) { 2592 pw.println("null"); 2593 } else { 2594 mNavigationBarView.dump(fd, pw, args); 2595 } 2596 2597 pw.print(" mMediaSessionManager="); 2598 pw.println(mMediaSessionManager); 2599 pw.print(" mMediaNotificationKey="); 2600 pw.println(mMediaNotificationKey); 2601 pw.print(" mMediaController="); 2602 pw.print(mMediaController); 2603 if (mMediaController != null) { 2604 pw.print(" state=" + mMediaController.getPlaybackState()); 2605 } 2606 pw.println(); 2607 pw.print(" mMediaMetadata="); 2608 pw.print(mMediaMetadata); 2609 if (mMediaMetadata != null) { 2610 pw.print(" title=" + mMediaMetadata.getString(MediaMetadata.METADATA_KEY_TITLE)); 2611 } 2612 pw.println(); 2613 2614 pw.println(" Panels: "); 2615 if (mNotificationPanel != null) { 2616 pw.println(" mNotificationPanel=" + 2617 mNotificationPanel + " params=" + mNotificationPanel.getLayoutParams().debug("")); 2618 pw.print (" "); 2619 mNotificationPanel.dump(fd, pw, args); 2620 } 2621 2622 if (DUMPTRUCK) { 2623 synchronized (mNotificationData) { 2624 int N = mNotificationData.size(); 2625 pw.println(" notification icons: " + N); 2626 for (int i=0; i<N; i++) { 2627 NotificationData.Entry e = mNotificationData.get(i); 2628 pw.println(" [" + i + "] key=" + e.key + " icon=" + e.icon); 2629 StatusBarNotification n = e.notification; 2630 pw.println(" pkg=" + n.getPackageName() + " id=" + n.getId() + " score=" + n.getScore()); 2631 pw.println(" notification=" + n.getNotification()); 2632 pw.println(" tickerText=\"" + n.getNotification().tickerText + "\""); 2633 } 2634 } 2635 2636 int N = mStatusIcons.getChildCount(); 2637 pw.println(" system icons: " + N); 2638 for (int i=0; i<N; i++) { 2639 StatusBarIconView ic = (StatusBarIconView) mStatusIcons.getChildAt(i); 2640 pw.println(" [" + i + "] icon=" + ic); 2641 } 2642 2643 if (false) { 2644 pw.println("see the logcat for a dump of the views we have created."); 2645 // must happen on ui thread 2646 mHandler.post(new Runnable() { 2647 public void run() { 2648 mStatusBarView.getLocationOnScreen(mAbsPos); 2649 Log.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1] 2650 + ") " + mStatusBarView.getWidth() + "x" 2651 + getStatusBarHeight()); 2652 mStatusBarView.debug(); 2653 } 2654 }); 2655 } 2656 } 2657 2658 if (DEBUG_GESTURES) { 2659 pw.print(" status bar gestures: "); 2660 mGestureRec.dump(fd, pw, args); 2661 } 2662 2663 if (mNetworkController != null) { 2664 mNetworkController.dump(fd, pw, args); 2665 } 2666 if (mBluetoothController != null) { 2667 mBluetoothController.dump(fd, pw, args); 2668 } 2669 if (mCastController != null) { 2670 mCastController.dump(fd, pw, args); 2671 } 2672 if (mUserSwitcherController != null) { 2673 mUserSwitcherController.dump(fd, pw, args); 2674 } 2675 if (mBatteryController != null) { 2676 mBatteryController.dump(fd, pw, args); 2677 } 2678 } 2679 2680 private String hunStateToString(Entry entry) { 2681 if (entry == null) return "null"; 2682 if (entry.notification == null) return "corrupt"; 2683 return entry.notification.getPackageName(); 2684 } 2685 2686 private static void dumpBarTransitions(PrintWriter pw, String var, BarTransitions transitions) { 2687 pw.print(" "); pw.print(var); pw.print(".BarTransitions.mMode="); 2688 pw.println(BarTransitions.modeToString(transitions.getMode())); 2689 } 2690 2691 @Override 2692 public void createAndAddWindows() { 2693 addStatusBarWindow(); 2694 } 2695 2696 private void addStatusBarWindow() { 2697 makeStatusBarView(); 2698 mStatusBarWindowManager = new StatusBarWindowManager(mContext); 2699 mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight()); 2700 } 2701 2702 static final float saturate(float a) { 2703 return a < 0f ? 0f : (a > 1f ? 1f : a); 2704 } 2705 2706 @Override 2707 public void updateExpandedViewPos(int thingy) { 2708 if (SPEW) Log.v(TAG, "updateExpandedViewPos"); 2709 2710 // on larger devices, the notification panel is propped open a bit 2711 mNotificationPanel.setMinimumHeight( 2712 (int)(mNotificationPanelMinHeightFrac * mCurrentDisplaySize.y)); 2713 2714 FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mNotificationPanel.getLayoutParams(); 2715 lp.gravity = mNotificationPanelGravity; 2716 mNotificationPanel.setLayoutParams(lp); 2717 2718 updateCarrierLabelVisibility(false); 2719 } 2720 2721 // called by makeStatusbar and also by PhoneStatusBarView 2722 void updateDisplaySize() { 2723 mDisplay.getMetrics(mDisplayMetrics); 2724 mDisplay.getSize(mCurrentDisplaySize); 2725 if (DEBUG_GESTURES) { 2726 mGestureRec.tag("display", 2727 String.format("%dx%d", mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels)); 2728 } 2729 } 2730 2731 public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned) { 2732 if (onlyProvisioned && !isDeviceProvisioned()) return; 2733 2734 dismissKeyguardThenExecute(new OnDismissAction() { 2735 @Override 2736 public boolean onDismiss() { 2737 AsyncTask.execute(new Runnable() { 2738 public void run() { 2739 try { 2740 intent.setFlags( 2741 Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); 2742 mContext.startActivityAsUser( 2743 intent, new UserHandle(UserHandle.USER_CURRENT)); 2744 mWindowManagerService.overridePendingAppTransition(null, 0, 0, null); 2745 } catch (RemoteException e) { 2746 } 2747 } 2748 }); 2749 animateCollapsePanels(); 2750 2751 return DELAY_DISMISS_TO_ACTIVITY_LAUNCH; 2752 } 2753 }); 2754 } 2755 2756 private View.OnClickListener mClockClickListener = new View.OnClickListener() { 2757 public void onClick(View v) { 2758 startActivityDismissingKeyguard( 2759 new Intent(Intent.ACTION_QUICK_CLOCK), true); // have fun, everyone 2760 } 2761 }; 2762 2763 private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 2764 public void onReceive(Context context, Intent intent) { 2765 if (DEBUG) Log.v(TAG, "onReceive: " + intent); 2766 String action = intent.getAction(); 2767 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) { 2768 int flags = CommandQueue.FLAG_EXCLUDE_NONE; 2769 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) { 2770 String reason = intent.getStringExtra("reason"); 2771 if (reason != null && reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) { 2772 flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL; 2773 } 2774 } 2775 animateCollapsePanels(flags); 2776 } 2777 else if (Intent.ACTION_SCREEN_OFF.equals(action)) { 2778 mScreenOn = false; 2779 notifyNavigationBarScreenOn(false); 2780 notifyHeadsUpScreenOn(false); 2781 finishBarAnimations(); 2782 stopNotificationLogging(); 2783 } 2784 else if (Intent.ACTION_SCREEN_ON.equals(action)) { 2785 mScreenOn = true; 2786 // work around problem where mDisplay.getRotation() is not stable while screen is off (bug 7086018) 2787 repositionNavigationBar(); 2788 notifyNavigationBarScreenOn(true); 2789 startNotificationLoggingIfScreenOnAndVisible(); 2790 } 2791 else if (ACTION_DEMO.equals(action)) { 2792 Bundle bundle = intent.getExtras(); 2793 if (bundle != null) { 2794 String command = bundle.getString("command", "").trim().toLowerCase(); 2795 if (command.length() > 0) { 2796 try { 2797 dispatchDemoCommand(command, bundle); 2798 } catch (Throwable t) { 2799 Log.w(TAG, "Error running demo command, intent=" + intent, t); 2800 } 2801 } 2802 } 2803 } else if ("fake_artwork".equals(action)) { 2804 if (DEBUG_MEDIA_FAKE_ARTWORK) { 2805 updateMediaMetaData(true); 2806 } 2807 } 2808 } 2809 }; 2810 2811 @Override 2812 protected void dismissKeyguardThenExecute(final OnDismissAction action) { 2813 if (mStatusBarKeyguardViewManager.isShowing()) { 2814 if (UnlockMethodCache.getInstance(mContext).isMethodInsecure() 2815 && mNotificationPanel.isLaunchTransitionRunning()) { 2816 action.onDismiss(); 2817 mNotificationPanel.setLaunchTransitionEndRunnable(new Runnable() { 2818 @Override 2819 public void run() { 2820 mStatusBarKeyguardViewManager.dismiss(); 2821 } 2822 }); 2823 } else { 2824 mStatusBarKeyguardViewManager.dismissWithAction(action); 2825 } 2826 } else { 2827 action.onDismiss(); 2828 } 2829 } 2830 2831 // SystemUIService notifies SystemBars of configuration changes, which then calls down here 2832 @Override 2833 protected void onConfigurationChanged(Configuration newConfig) { 2834 super.onConfigurationChanged(newConfig); // calls refreshLayout 2835 2836 if (DEBUG) { 2837 Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration()); 2838 } 2839 updateDisplaySize(); // populates mDisplayMetrics 2840 2841 updateResources(); 2842 repositionNavigationBar(); 2843 updateExpandedViewPos(EXPANDED_LEAVE_ALONE); 2844 updateShowSearchHoldoff(); 2845 updateRowStates(); 2846 } 2847 2848 @Override 2849 public void userSwitched(int newUserId) { 2850 if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId); 2851 animateCollapsePanels(); 2852 updateNotifications(); 2853 resetUserSetupObserver(); 2854 setControllerUsers(); 2855 } 2856 2857 private void setControllerUsers() { 2858 if (mZenModeController != null) { 2859 mZenModeController.setUserId(mCurrentUserId); 2860 } 2861 } 2862 2863 private void resetUserSetupObserver() { 2864 mContext.getContentResolver().unregisterContentObserver(mUserSetupObserver); 2865 mUserSetupObserver.onChange(false); 2866 mContext.getContentResolver().registerContentObserver( 2867 Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), true, 2868 mUserSetupObserver, 2869 mCurrentUserId); 2870 } 2871 2872 private void setHeadsUpVisibility(boolean vis) { 2873 if (!ENABLE_HEADS_UP) return; 2874 if (DEBUG) Log.v(TAG, (vis ? "showing" : "hiding") + " heads up window"); 2875 mHeadsUpNotificationView.setVisibility(vis ? View.VISIBLE : View.GONE); 2876 } 2877 2878 public void onHeadsUpDismissed() { 2879 mHeadsUpNotificationView.dismiss(); 2880 } 2881 2882 /** 2883 * Reload some of our resources when the configuration changes. 2884 * 2885 * We don't reload everything when the configuration changes -- we probably 2886 * should, but getting that smooth is tough. Someday we'll fix that. In the 2887 * meantime, just update the things that we know change. 2888 */ 2889 void updateResources() { 2890 // Update the quick setting tiles 2891 if (mQSPanel != null) mQSPanel.updateResources(); 2892 2893 loadDimens(); 2894 mLinearOutSlowIn = AnimationUtils.loadInterpolator( 2895 mContext, android.R.interpolator.linear_out_slow_in); 2896 } 2897 2898 protected void loadDimens() { 2899 final Resources res = mContext.getResources(); 2900 2901 mNaturalBarHeight = res.getDimensionPixelSize( 2902 com.android.internal.R.dimen.status_bar_height); 2903 2904 int newIconSize = res.getDimensionPixelSize( 2905 com.android.internal.R.dimen.status_bar_icon_size); 2906 int newIconHPadding = res.getDimensionPixelSize( 2907 R.dimen.status_bar_icon_padding); 2908 2909 if (newIconHPadding != mIconHPadding || newIconSize != mIconSize) { 2910// Log.d(TAG, "size=" + newIconSize + " padding=" + newIconHPadding); 2911 mIconHPadding = newIconHPadding; 2912 mIconSize = newIconSize; 2913 //reloadAllNotificationIcons(); // reload the tray 2914 } 2915 2916 mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore); 2917 2918 mNotificationPanelGravity = res.getInteger(R.integer.notification_panel_layout_gravity); 2919 if (mNotificationPanelGravity <= 0) { 2920 mNotificationPanelGravity = Gravity.START | Gravity.TOP; 2921 } 2922 2923 mCarrierLabelHeight = res.getDimensionPixelSize(R.dimen.carrier_label_height); 2924 mStatusBarHeaderHeight = res.getDimensionPixelSize(R.dimen.status_bar_header_height); 2925 2926 mNotificationPanelMinHeightFrac = res.getFraction(R.dimen.notification_panel_min_height_frac, 1, 1); 2927 if (mNotificationPanelMinHeightFrac < 0f || mNotificationPanelMinHeightFrac > 1f) { 2928 mNotificationPanelMinHeightFrac = 0f; 2929 } 2930 2931 mHeadsUpNotificationDecay = res.getInteger(R.integer.heads_up_notification_decay); 2932 mRowMinHeight = res.getDimensionPixelSize(R.dimen.notification_min_height); 2933 mRowMaxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height); 2934 2935 mKeyguardMaxNotificationCount = res.getInteger(R.integer.keyguard_max_notification_count); 2936 2937 if (false) Log.v(TAG, "updateResources"); 2938 } 2939 2940 // Visibility reporting 2941 2942 @Override 2943 protected void visibilityChanged(boolean visible) { 2944 mVisible = visible; 2945 if (visible) { 2946 startNotificationLoggingIfScreenOnAndVisible(); 2947 } else { 2948 stopNotificationLogging(); 2949 } 2950 super.visibilityChanged(visible); 2951 } 2952 2953 private void stopNotificationLogging() { 2954 // Report all notifications as invisible and turn down the 2955 // reporter. 2956 if (!mCurrentlyVisibleNotifications.isEmpty()) { 2957 logNotificationVisibilityChanges( 2958 Collections.<String>emptyList(), mCurrentlyVisibleNotifications); 2959 mCurrentlyVisibleNotifications.clear(); 2960 } 2961 mHandler.removeCallbacks(mVisibilityReporter); 2962 mStackScroller.setChildLocationsChangedListener(null); 2963 } 2964 2965 private void startNotificationLoggingIfScreenOnAndVisible() { 2966 if (mVisible && mScreenOn) { 2967 mStackScroller.setChildLocationsChangedListener(mNotificationLocationsChangedListener); 2968 // Some transitions like mScreenOn=false -> mScreenOn=true don't 2969 // cause the scroller to emit child location events. Hence generate 2970 // one ourselves to guarantee that we're reporting visible 2971 // notifications. 2972 // (Note that in cases where the scroller does emit events, this 2973 // additional event doesn't break anything.) 2974 mNotificationLocationsChangedListener.onChildLocationsChanged(mStackScroller); 2975 } 2976 } 2977 2978 private void logNotificationVisibilityChanges( 2979 Collection<String> newlyVisible, Collection<String> noLongerVisible) { 2980 if (newlyVisible.isEmpty() && noLongerVisible.isEmpty()) { 2981 return; 2982 } 2983 String[] newlyVisibleAr = newlyVisible.toArray(new String[newlyVisible.size()]); 2984 String[] noLongerVisibleAr = noLongerVisible.toArray(new String[noLongerVisible.size()]); 2985 try { 2986 mBarService.onNotificationVisibilityChanged(newlyVisibleAr, noLongerVisibleAr); 2987 } catch (RemoteException e) { 2988 // Ignore. 2989 } 2990 } 2991 2992 // 2993 // tracing 2994 // 2995 2996 void postStartTracing() { 2997 mHandler.postDelayed(mStartTracing, 3000); 2998 } 2999 3000 void vibrate() { 3001 android.os.Vibrator vib = (android.os.Vibrator)mContext.getSystemService( 3002 Context.VIBRATOR_SERVICE); 3003 vib.vibrate(250, VIBRATION_ATTRIBUTES); 3004 } 3005 3006 Runnable mStartTracing = new Runnable() { 3007 public void run() { 3008 vibrate(); 3009 SystemClock.sleep(250); 3010 Log.d(TAG, "startTracing"); 3011 android.os.Debug.startMethodTracing("/data/statusbar-traces/trace"); 3012 mHandler.postDelayed(mStopTracing, 10000); 3013 } 3014 }; 3015 3016 Runnable mStopTracing = new Runnable() { 3017 public void run() { 3018 android.os.Debug.stopMethodTracing(); 3019 Log.d(TAG, "stopTracing"); 3020 vibrate(); 3021 } 3022 }; 3023 3024 @Override 3025 protected void haltTicker() { 3026 if (mTickerEnabled) { 3027 mTicker.halt(); 3028 } 3029 } 3030 3031 @Override 3032 protected boolean shouldDisableNavbarGestures() { 3033 return !isDeviceProvisioned() 3034 || mExpandedVisible 3035 || (mDisabled & StatusBarManager.DISABLE_SEARCH) != 0; 3036 } 3037 3038 public void postStartSettingsActivity(final Intent intent, int delay) { 3039 mHandler.postDelayed(new Runnable() { 3040 @Override 3041 public void run() { 3042 handleStartSettingsActivity(intent, true /*onlyProvisioned*/); 3043 } 3044 }, delay); 3045 } 3046 3047 private void handleStartSettingsActivity(Intent intent, boolean onlyProvisioned) { 3048 if (onlyProvisioned && !isDeviceProvisioned()) return; 3049 try { 3050 // Dismiss the lock screen when Settings starts. 3051 ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity(); 3052 } catch (RemoteException e) { 3053 } 3054 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); 3055 mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT)); 3056 animateCollapsePanels(); 3057 } 3058 3059 public void startSettingsActivity(String action) { 3060 postStartSettingsActivity(new Intent(action), 0); 3061 } 3062 3063 private static class FastColorDrawable extends Drawable { 3064 private final int mColor; 3065 3066 public FastColorDrawable(int color) { 3067 mColor = 0xff000000 | color; 3068 } 3069 3070 @Override 3071 public void draw(Canvas canvas) { 3072 canvas.drawColor(mColor, PorterDuff.Mode.SRC); 3073 } 3074 3075 @Override 3076 public void setAlpha(int alpha) { 3077 } 3078 3079 @Override 3080 public void setColorFilter(ColorFilter cf) { 3081 } 3082 3083 @Override 3084 public int getOpacity() { 3085 return PixelFormat.OPAQUE; 3086 } 3087 3088 @Override 3089 public void setBounds(int left, int top, int right, int bottom) { 3090 } 3091 3092 @Override 3093 public void setBounds(Rect bounds) { 3094 } 3095 } 3096 3097 @Override 3098 public void destroy() { 3099 super.destroy(); 3100 if (mStatusBarWindow != null) { 3101 mWindowManager.removeViewImmediate(mStatusBarWindow); 3102 mStatusBarWindow = null; 3103 } 3104 if (mNavigationBarView != null) { 3105 mWindowManager.removeViewImmediate(mNavigationBarView); 3106 mNavigationBarView = null; 3107 } 3108 mContext.unregisterReceiver(mBroadcastReceiver); 3109 } 3110 3111 private boolean mDemoModeAllowed; 3112 private boolean mDemoMode; 3113 private DemoStatusIcons mDemoStatusIcons; 3114 3115 @Override 3116 public void dispatchDemoCommand(String command, Bundle args) { 3117 if (!mDemoModeAllowed) { 3118 mDemoModeAllowed = Settings.Global.getInt(mContext.getContentResolver(), 3119 "sysui_demo_allowed", 0) != 0; 3120 } 3121 if (!mDemoModeAllowed) return; 3122 if (command.equals(COMMAND_ENTER)) { 3123 mDemoMode = true; 3124 } else if (command.equals(COMMAND_EXIT)) { 3125 mDemoMode = false; 3126 checkBarModes(); 3127 } else if (!mDemoMode) { 3128 // automatically enter demo mode on first demo command 3129 dispatchDemoCommand(COMMAND_ENTER, new Bundle()); 3130 } 3131 boolean modeChange = command.equals(COMMAND_ENTER) || command.equals(COMMAND_EXIT); 3132 if (modeChange || command.equals(COMMAND_CLOCK)) { 3133 dispatchDemoCommandToView(command, args, R.id.clock); 3134 } 3135 if (modeChange || command.equals(COMMAND_BATTERY)) { 3136 dispatchDemoCommandToView(command, args, R.id.battery); 3137 } 3138 if (modeChange || command.equals(COMMAND_STATUS)) { 3139 if (mDemoStatusIcons == null) { 3140 mDemoStatusIcons = new DemoStatusIcons(mStatusIcons, mIconSize); 3141 } 3142 mDemoStatusIcons.dispatchDemoCommand(command, args); 3143 } 3144 if (mNetworkController != null && (modeChange || command.equals(COMMAND_NETWORK))) { 3145 mNetworkController.dispatchDemoCommand(command, args); 3146 } 3147 if (modeChange || command.equals(COMMAND_NOTIFICATIONS)) { 3148 View notifications = mStatusBarView == null ? null 3149 : mStatusBarView.findViewById(R.id.notification_icon_area); 3150 if (notifications != null) { 3151 String visible = args.getString("visible"); 3152 int vis = mDemoMode && "false".equals(visible) ? View.INVISIBLE : View.VISIBLE; 3153 notifications.setVisibility(vis); 3154 } 3155 } 3156 if (command.equals(COMMAND_BARS)) { 3157 String mode = args.getString("mode"); 3158 int barMode = "opaque".equals(mode) ? MODE_OPAQUE : 3159 "translucent".equals(mode) ? MODE_TRANSLUCENT : 3160 "semi-transparent".equals(mode) ? MODE_SEMI_TRANSPARENT : 3161 "transparent".equals(mode) ? MODE_TRANSPARENT : 3162 "warning".equals(mode) ? MODE_WARNING : 3163 -1; 3164 if (barMode != -1) { 3165 boolean animate = true; 3166 if (mStatusBarView != null) { 3167 mStatusBarView.getBarTransitions().transitionTo(barMode, animate); 3168 } 3169 if (mNavigationBarView != null) { 3170 mNavigationBarView.getBarTransitions().transitionTo(barMode, animate); 3171 } 3172 } 3173 } 3174 } 3175 3176 private void dispatchDemoCommandToView(String command, Bundle args, int id) { 3177 if (mStatusBarView == null) return; 3178 View v = mStatusBarView.findViewById(id); 3179 if (v instanceof DemoMode) { 3180 ((DemoMode)v).dispatchDemoCommand(command, args); 3181 } 3182 } 3183 3184 /** 3185 * @return The {@link StatusBarState} the status bar is in. 3186 */ 3187 public int getBarState() { 3188 return mState; 3189 } 3190 3191 public void showKeyguard() { 3192 setBarState(StatusBarState.KEYGUARD); 3193 updateKeyguardState(); 3194 instantExpandNotificationsPanel(); 3195 mLeaveOpenOnKeyguardHide = false; 3196 } 3197 3198 public boolean isInLaunchTransition() { 3199 return mNotificationPanel.isLaunchTransitionRunning() 3200 || mNotificationPanel.isLaunchTransitionFinished(); 3201 } 3202 3203 /** 3204 * Fades the content of the keyguard away after the launch transition is done. 3205 * 3206 * @param beforeFading the runnable to be run when the circle is fully expanded and the fading 3207 * starts 3208 * @param endRunnable the runnable to be run when the transition is done 3209 */ 3210 public void fadeKeyguardAfterLaunchTransition(final Runnable beforeFading, 3211 final Runnable endRunnable) { 3212 Runnable hideRunnable = new Runnable() { 3213 @Override 3214 public void run() { 3215 mLaunchTransitionFadingAway = true; 3216 if (beforeFading != null) { 3217 beforeFading.run(); 3218 } 3219 mNotificationPanel.setAlpha(1); 3220 mNotificationPanel.animate() 3221 .alpha(0) 3222 .setStartDelay(FADE_KEYGUARD_START_DELAY) 3223 .setDuration(FADE_KEYGUARD_DURATION) 3224 .withLayer() 3225 .withEndAction(new Runnable() { 3226 @Override 3227 public void run() { 3228 mNotificationPanel.setAlpha(1); 3229 if (endRunnable != null) { 3230 endRunnable.run(); 3231 } 3232 mLaunchTransitionFadingAway = false; 3233 } 3234 }); 3235 } 3236 }; 3237 if (mNotificationPanel.isLaunchTransitionRunning()) { 3238 mNotificationPanel.setLaunchTransitionEndRunnable(hideRunnable); 3239 } else { 3240 hideRunnable.run(); 3241 } 3242 } 3243 3244 public void hideKeyguard() { 3245 setBarState(StatusBarState.SHADE); 3246 if (mLeaveOpenOnKeyguardHide) { 3247 mLeaveOpenOnKeyguardHide = false; 3248 mNotificationPanel.animateToFullShade(); 3249 } else { 3250 instantCollapseNotificationPanel(); 3251 } 3252 updateKeyguardState(); 3253 } 3254 3255 /** 3256 * Notifies the status bar the Keyguard is fading away with the specified timings. 3257 * 3258 * @param delay the animation delay in miliseconds 3259 * @param fadeoutDuration the duration of the exit animation, in milliseconds 3260 */ 3261 public void setKeyguardFadingAway(long delay, long fadeoutDuration) { 3262 mKeyguardFadingAway = true; 3263 mKeyguardFadingAwayDelay = delay; 3264 mKeyguardFadingAwayDuration = fadeoutDuration; 3265 mWaitingForKeyguardExit = false; 3266 disable(mDisabledUnmodified, true /* animate */); 3267 } 3268 3269 /** 3270 * Notifies that the Keyguard fading away animation is done. 3271 */ 3272 public void finishKeyguardFadingAway() { 3273 mKeyguardFadingAway = false; 3274 } 3275 3276 private void updatePublicMode() { 3277 setLockscreenPublicMode( 3278 (mStatusBarKeyguardViewManager.isShowing() || 3279 mStatusBarKeyguardViewManager.isOccluded()) 3280 && mStatusBarKeyguardViewManager.isSecure()); 3281 } 3282 3283 private void updateKeyguardState() { 3284 if (mState == StatusBarState.KEYGUARD) { 3285 mKeyguardStatusView.setVisibility(View.VISIBLE); 3286 mKeyguardIndicationController.setVisible(true); 3287 mNotificationPanel.resetViews(); 3288 mKeyguardUserSwitcher.setKeyguard(true); 3289 } else { 3290 mKeyguardStatusView.setVisibility(View.GONE); 3291 mKeyguardIndicationController.setVisible(false); 3292 mKeyguardUserSwitcher.setKeyguard(false); 3293 } 3294 if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) { 3295 mKeyguardBottomArea.setVisibility(View.VISIBLE); 3296 mHeader.setKeyguardShowing(true); 3297 mScrimController.setKeyguardShowing(true); 3298 } else { 3299 mKeyguardBottomArea.setVisibility(View.GONE); 3300 mHeader.setKeyguardShowing(false); 3301 mScrimController.setKeyguardShowing(false); 3302 } 3303 mNotificationPanel.setBarState(mState); 3304 updateDozingState(); 3305 updateStackScrollerState(); 3306 updatePublicMode(); 3307 updateNotifications(); 3308 checkBarModes(); 3309 updateCarrierLabelVisibility(false); 3310 updateMediaMetaData(false); 3311 mKeyguardMonitor.notifyKeyguardState(mStatusBarKeyguardViewManager.isShowing(), 3312 mStatusBarKeyguardViewManager.isSecure()); 3313 } 3314 3315 private void updateDozingState() { 3316 final boolean bottomGone = mKeyguardBottomArea.getVisibility() == View.GONE; 3317 if (mDozing) { 3318 mNotificationPanel.setBackgroundColor(0xff000000); 3319 mHeader.setVisibility(View.INVISIBLE); 3320 if (!bottomGone) { 3321 mKeyguardBottomArea.setVisibility(View.INVISIBLE); 3322 } 3323 mStackScroller.setDark(true, false /*animate*/); 3324 } else { 3325 mNotificationPanel.setBackground(null); 3326 mHeader.setVisibility(View.VISIBLE); 3327 if (!bottomGone) { 3328 mKeyguardBottomArea.setVisibility(View.VISIBLE); 3329 } 3330 mStackScroller.setDark(false, false /*animate*/); 3331 } 3332 mScrimController.setDozing(mDozing); 3333 } 3334 3335 public void updateStackScrollerState() { 3336 if (mStackScroller == null) return; 3337 boolean onKeyguard = mState == StatusBarState.KEYGUARD; 3338 mStackScroller.setDimmed(onKeyguard, false /* animate */); 3339 mStackScroller.setVisibility(!mShowLockscreenNotifications && onKeyguard 3340 ? View.INVISIBLE : View.VISIBLE); 3341 mStackScroller.setExpandingEnabled(!onKeyguard); 3342 ActivatableNotificationView activatedChild = mStackScroller.getActivatedChild(); 3343 mStackScroller.setActivatedChild(null); 3344 if (activatedChild != null) { 3345 activatedChild.makeInactive(false /* animate */); 3346 } 3347 } 3348 3349 public void userActivity() { 3350 if (mState == StatusBarState.KEYGUARD) { 3351 mKeyguardViewMediatorCallback.userActivity(); 3352 } 3353 } 3354 3355 public boolean interceptMediaKey(KeyEvent event) { 3356 return mState == StatusBarState.KEYGUARD 3357 && mStatusBarKeyguardViewManager.interceptMediaKey(event); 3358 } 3359 3360 public boolean onMenuPressed() { 3361 return mState == StatusBarState.KEYGUARD && mStatusBarKeyguardViewManager.onMenuPressed(); 3362 } 3363 3364 public boolean onBackPressed() { 3365 if (mStatusBarKeyguardViewManager.onBackPressed()) { 3366 return true; 3367 } 3368 if (mNotificationPanel.isQsExpanded()) { 3369 mNotificationPanel.animateCloseQs(); 3370 return true; 3371 } 3372 if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED) { 3373 animateCollapsePanels(); 3374 return true; 3375 } 3376 return false; 3377 } 3378 3379 public boolean onSpacePressed() { 3380 if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) { 3381 animateCollapsePanels(0 /* flags */, true /* force */); 3382 return true; 3383 } 3384 return false; 3385 } 3386 3387 private void showBouncer() { 3388 if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) { 3389 mWaitingForKeyguardExit = mStatusBarKeyguardViewManager.isShowing(); 3390 mStatusBarKeyguardViewManager.dismiss(); 3391 } 3392 } 3393 3394 private void instantExpandNotificationsPanel() { 3395 3396 // Make our window larger and the panel expanded. 3397 makeExpandedVisible(true); 3398 mNotificationPanel.instantExpand(); 3399 } 3400 3401 private void instantCollapseNotificationPanel() { 3402 mNotificationPanel.setExpandedFraction(0); 3403 } 3404 3405 @Override 3406 public void onActivated(ActivatableNotificationView view) { 3407 mKeyguardIndicationController.showTransientIndication(R.string.notification_tap_again); 3408 ActivatableNotificationView previousView = mStackScroller.getActivatedChild(); 3409 if (previousView != null) { 3410 previousView.makeInactive(true /* animate */); 3411 } 3412 mStackScroller.setActivatedChild(view); 3413 } 3414 3415 /** 3416 * @param state The {@link StatusBarState} to set. 3417 */ 3418 public void setBarState(int state) { 3419 mState = state; 3420 mStatusBarWindowManager.setStatusBarState(state); 3421 } 3422 3423 @Override 3424 public void onActivationReset(ActivatableNotificationView view) { 3425 if (view == mStackScroller.getActivatedChild()) { 3426 mKeyguardIndicationController.hideTransientIndication(); 3427 mStackScroller.setActivatedChild(null); 3428 } 3429 } 3430 3431 public void onTrackingStarted() { 3432 } 3433 3434 public void onUnlockHintStarted() { 3435 mKeyguardIndicationController.showTransientIndication(R.string.keyguard_unlock); 3436 } 3437 3438 public void onHintFinished() { 3439 // Delay the reset a bit so the user can read the text. 3440 mKeyguardIndicationController.hideTransientIndicationDelayed(HINT_RESET_DELAY_MS); 3441 } 3442 3443 public void onCameraHintStarted() { 3444 mKeyguardIndicationController.showTransientIndication(R.string.camera_hint); 3445 } 3446 3447 public void onPhoneHintStarted() { 3448 mKeyguardIndicationController.showTransientIndication(R.string.phone_hint); 3449 } 3450 3451 public void onTrackingStopped(boolean expand) { 3452 if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) { 3453 if (!expand && !mUnlockMethodCache.isMethodInsecure()) { 3454 showBouncer(); 3455 } 3456 } 3457 } 3458 3459 @Override 3460 protected int getMaxKeyguardNotifications() { 3461 return mKeyguardMaxNotificationCount; 3462 } 3463 3464 public NavigationBarView getNavigationBarView() { 3465 return mNavigationBarView; 3466 } 3467 3468 // ---------------------- DragDownHelper.OnDragDownListener ------------------------------------ 3469 3470 @Override 3471 public void onDraggedDown(View startingChild) { 3472 goToLockedShade(startingChild); 3473 } 3474 3475 @Override 3476 public void onDragDownReset() { 3477 mStackScroller.setDimmed(true /* dimmed */, true /* animated */); 3478 } 3479 3480 public void onThresholdReached() { 3481 mStackScroller.setDimmed(false /* dimmed */, true /* animate */); 3482 } 3483 3484 @Override 3485 public void onTouchSlopExceeded() { 3486 mStackScroller.removeLongPressCallback(); 3487 } 3488 3489 /** 3490 * If secure with redaction: Show bouncer, go to unlocked shade. 3491 * 3492 * <p>If secure without redaction or no security: Go to {@link StatusBarState#SHADE_LOCKED}.</p> 3493 * 3494 * @param expandView The view to expand after going to the shade. 3495 */ 3496 public void goToLockedShade(View expandView) { 3497 if (expandView instanceof ExpandableNotificationRow) { 3498 ExpandableNotificationRow row = (ExpandableNotificationRow) expandView; 3499 row.setUserExpanded(true); 3500 } 3501 if (isLockscreenPublicMode() && !userAllowsPrivateNotificationsInPublic(mCurrentUserId)) { 3502 mLeaveOpenOnKeyguardHide = true; 3503 showBouncer(); 3504 } else { 3505 mNotificationPanel.animateToFullShade(); 3506 setBarState(StatusBarState.SHADE_LOCKED); 3507 updateKeyguardState(); 3508 } 3509 } 3510 3511 /** 3512 * Goes back to the keyguard after hanging around in {@link StatusBarState#SHADE_LOCKED}. 3513 */ 3514 public void goToKeyguard() { 3515 if (mState == StatusBarState.SHADE_LOCKED) { 3516 setBarState(StatusBarState.KEYGUARD); 3517 updateKeyguardState(); 3518 } 3519 } 3520 3521 /** 3522 * @return a ViewGroup that spans the entire panel which contains the quick settings 3523 */ 3524 public ViewGroup getQuickSettingsOverlayParent() { 3525 return mNotificationPanel; 3526 } 3527 3528 public LinearLayout getSystemIcons() { 3529 return mSystemIcons; 3530 } 3531 3532 public LinearLayout getSystemIconArea() { 3533 return mSystemIconArea; 3534 } 3535 3536 @Override 3537 public void setBouncerShowing(boolean bouncerShowing) { 3538 super.setBouncerShowing(bouncerShowing); 3539 disable(mDisabledUnmodified, true /* animate */); 3540 } 3541 3542 public void onScreenTurnedOff() { 3543 mStackScroller.setAnimationsEnabled(false); 3544 } 3545 3546 public void onScreenTurnedOn() { 3547 mStackScroller.setAnimationsEnabled(true); 3548 } 3549 3550 public void toggleLockedApp() { 3551 Log.d(TAG, "Trying to toggle lock-to-app"); 3552 try { 3553 IActivityManager activityManager = ActivityManagerNative.getDefault(); 3554 if (activityManager.isInLockTaskMode()) { 3555 activityManager.stopLockTaskModeOnCurrent(); 3556 } 3557 } catch (RemoteException e) { 3558 Log.d(TAG, "Unable to toggle Lock-to-app", e); 3559 } 3560 } 3561 3562 // Recents 3563 3564 @Override 3565 protected void showRecents(boolean triggeredFromAltTab) { 3566 // Set the recents visibility flag 3567 mSystemUiVisibility |= View.RECENT_APPS_VISIBLE; 3568 notifyUiVisibilityChanged(mSystemUiVisibility); 3569 super.showRecents(triggeredFromAltTab); 3570 } 3571 3572 @Override 3573 protected void hideRecents(boolean triggeredFromAltTab) { 3574 // Unset the recents visibility flag 3575 mSystemUiVisibility &= ~View.RECENT_APPS_VISIBLE; 3576 notifyUiVisibilityChanged(mSystemUiVisibility); 3577 super.hideRecents(triggeredFromAltTab); 3578 } 3579 3580 @Override 3581 protected void toggleRecents() { 3582 // Toggle the recents visibility flag 3583 mSystemUiVisibility ^= View.RECENT_APPS_VISIBLE; 3584 notifyUiVisibilityChanged(mSystemUiVisibility); 3585 super.toggleRecents(); 3586 } 3587 3588 @Override 3589 public void onVisibilityChanged(boolean visible) { 3590 // Update the recents visibility flag 3591 if (visible) { 3592 mSystemUiVisibility |= View.RECENT_APPS_VISIBLE; 3593 } else { 3594 mSystemUiVisibility &= ~View.RECENT_APPS_VISIBLE; 3595 } 3596 notifyUiVisibilityChanged(mSystemUiVisibility); 3597 } 3598 3599 private final class ShadeUpdates { 3600 private final ArraySet<String> mVisibleNotifications = new ArraySet<String>(); 3601 private final ArraySet<String> mNewVisibleNotifications = new ArraySet<String>(); 3602 3603 public void check() { 3604 mNewVisibleNotifications.clear(); 3605 for (int i = 0; i < mNotificationData.size(); i++) { 3606 final Entry entry = mNotificationData.get(i); 3607 final boolean visible = entry.row != null 3608 && entry.row.getVisibility() == View.VISIBLE; 3609 if (visible) { 3610 mNewVisibleNotifications.add(entry.key + entry.notification.getPostTime()); 3611 } 3612 } 3613 final boolean updates = !mVisibleNotifications.containsAll(mNewVisibleNotifications); 3614 mVisibleNotifications.clear(); 3615 mVisibleNotifications.addAll(mNewVisibleNotifications); 3616 3617 // We have new notifications 3618 if (updates && mDozeServiceHost != null) { 3619 mDozeServiceHost.fireNewNotifications(); 3620 } 3621 } 3622 } 3623 3624 private final class DozeServiceHost implements DozeService.Host { 3625 // Amount of time to allow to update the time shown on the screen before releasing 3626 // the wakelock. This timeout is design to compensate for the fact that we don't 3627 // currently have a way to know when time display contents have actually been 3628 // refreshed once we've finished rendering a new frame. 3629 private static final long PROCESSING_TIME = 500; 3630 3631 private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>(); 3632 private final H mHandler = new H(); 3633 3634 private DozeService mCurrentDozeService; 3635 3636 public void fireNewNotifications() { 3637 for (Callback callback : mCallbacks) { 3638 callback.onNewNotifications(); 3639 } 3640 } 3641 3642 @Override 3643 public void addCallback(Callback callback) { 3644 mCallbacks.add(callback); 3645 } 3646 3647 @Override 3648 public void removeCallback(Callback callback) { 3649 mCallbacks.remove(callback); 3650 } 3651 3652 @Override 3653 public void requestDoze(DozeService dozeService) { 3654 if (dozeService == null) return; 3655 dozeService.stayAwake(PROCESSING_TIME); 3656 mHandler.obtainMessage(H.REQUEST_DOZE, dozeService).sendToTarget(); 3657 } 3658 3659 @Override 3660 public void requestTease(DozeService dozeService) { 3661 if (dozeService == null) return; 3662 dozeService.stayAwake(PROCESSING_TIME); 3663 mHandler.obtainMessage(H.REQUEST_TEASE, dozeService).sendToTarget(); 3664 } 3665 3666 @Override 3667 public void dozingStopped(DozeService dozeService) { 3668 if (dozeService == null) return; 3669 dozeService.stayAwake(PROCESSING_TIME); 3670 mHandler.obtainMessage(H.DOZING_STOPPED, dozeService).sendToTarget(); 3671 } 3672 3673 private void handleRequestDoze(DozeService dozeService) { 3674 mCurrentDozeService = dozeService; 3675 if (!mDozing) { 3676 mDozing = true; 3677 updateDozingState(); 3678 } 3679 mCurrentDozeService.startDozing(); 3680 } 3681 3682 private void handleRequestTease(DozeService dozeService) { 3683 if (!dozeService.equals(mCurrentDozeService)) return; 3684 final long stayAwake = mScrimController.tease(); 3685 mCurrentDozeService.stayAwake(stayAwake); 3686 } 3687 3688 private void handleDozingStopped(DozeService dozeService) { 3689 if (dozeService.equals(mCurrentDozeService)) { 3690 mCurrentDozeService = null; 3691 } 3692 if (mDozing) { 3693 mDozing = false; 3694 updateDozingState(); 3695 } 3696 } 3697 3698 private final class H extends Handler { 3699 private static final int REQUEST_DOZE = 1; 3700 private static final int REQUEST_TEASE = 2; 3701 private static final int DOZING_STOPPED = 3; 3702 3703 @Override 3704 public void handleMessage(Message msg) { 3705 if (msg.what == REQUEST_DOZE) { 3706 handleRequestDoze((DozeService) msg.obj); 3707 } else if (msg.what == REQUEST_TEASE) { 3708 handleRequestTease((DozeService) msg.obj); 3709 } else if (msg.what == DOZING_STOPPED) { 3710 handleDozingStopped((DozeService) msg.obj); 3711 } 3712 } 3713 } 3714 } 3715} 3716