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