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