KeyguardTransportControlView.java revision cab6965b191da02e783bc578ae3921fc0bca019f
1/* 2 * Copyright (C) 2011 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.keyguard; 18 19import android.content.Context; 20import android.content.pm.PackageManager; 21import android.content.res.Configuration; 22import android.graphics.Bitmap; 23import android.graphics.ColorMatrix; 24import android.graphics.ColorMatrixColorFilter; 25import android.graphics.PorterDuff; 26import android.graphics.PorterDuffXfermode; 27import android.graphics.drawable.Drawable; 28import android.media.AudioManager; 29import android.media.MediaMetadataEditor; 30import android.media.MediaMetadataRetriever; 31import android.media.RemoteControlClient; 32import android.media.RemoteController; 33import android.os.Parcel; 34import android.os.Parcelable; 35import android.os.SystemClock; 36import android.text.TextUtils; 37import android.text.format.DateFormat; 38import android.transition.ChangeBounds; 39import android.transition.ChangeText; 40import android.transition.Fade; 41import android.transition.TransitionManager; 42import android.transition.TransitionSet; 43import android.util.AttributeSet; 44import android.util.DisplayMetrics; 45import android.util.Log; 46import android.view.KeyEvent; 47import android.view.View; 48import android.view.ViewGroup; 49import android.widget.FrameLayout; 50import android.widget.ImageView; 51import android.widget.SeekBar; 52import android.widget.TextView; 53 54import java.text.SimpleDateFormat; 55import java.util.Date; 56import java.util.TimeZone; 57 58/** 59 * This is the widget responsible for showing music controls in keyguard. 60 */ 61public class KeyguardTransportControlView extends FrameLayout { 62 63 private static final int DISPLAY_TIMEOUT_MS = 5000; // 5s 64 private static final int RESET_TO_METADATA_DELAY = 5000; 65 protected static final boolean DEBUG = false; 66 protected static final String TAG = "TransportControlView"; 67 68 private static final boolean ANIMATE_TRANSITIONS = true; 69 70 private ViewGroup mMetadataContainer; 71 private ViewGroup mInfoContainer; 72 private TextView mTrackTitle; 73 private TextView mTrackArtistAlbum; 74 75 private View mTransientSeek; 76 private SeekBar mTransientSeekBar; 77 private TextView mTransientSeekTimeElapsed; 78 private TextView mTransientSeekTimeRemaining; 79 80 private ImageView mBtnPrev; 81 private ImageView mBtnPlay; 82 private ImageView mBtnNext; 83 private Metadata mMetadata = new Metadata(); 84 private int mTransportControlFlags; 85 private int mCurrentPlayState; 86 private AudioManager mAudioManager; 87 private RemoteController mRemoteController; 88 89 private ImageView mBadge; 90 91 private boolean mSeekEnabled; 92 private boolean mUserSeeking; 93 private java.text.DateFormat mFormat; 94 95 private Date mTimeElapsed; 96 private Date mTimeRemaining; 97 98 /** 99 * The metadata which should be populated into the view once we've been attached 100 */ 101 private RemoteController.MetadataEditor mPopulateMetadataWhenAttached = null; 102 103 private RemoteController.OnClientUpdateListener mRCClientUpdateListener = 104 new RemoteController.OnClientUpdateListener() { 105 @Override 106 public void onClientChange(boolean clearing) { 107 clearMetadata(); 108 } 109 110 @Override 111 public void onClientPlaybackStateUpdate(int state) { 112 setSeekBarsEnabled(false); 113 updatePlayPauseState(state); 114 } 115 116 @Override 117 public void onClientPlaybackStateUpdate(int state, long stateChangeTimeMs, 118 long currentPosMs, float speed) { 119 setSeekBarsEnabled(mMetadata != null && mMetadata.duration > 0); 120 updatePlayPauseState(state); 121 if (DEBUG) Log.d(TAG, "onClientPlaybackStateUpdate(state=" + state + 122 ", stateChangeTimeMs=" + stateChangeTimeMs + ", currentPosMs=" + currentPosMs + 123 ", speed=" + speed + ")"); 124 } 125 126 @Override 127 public void onClientTransportControlUpdate(int transportControlFlags) { 128 updateTransportControls(transportControlFlags); 129 } 130 131 @Override 132 public void onClientMetadataUpdate(RemoteController.MetadataEditor metadataEditor) { 133 updateMetadata(metadataEditor); 134 } 135 }; 136 137 private final Runnable mUpdateSeekBars = new Runnable() { 138 public void run() { 139 if (updateSeekBars()) { 140 removeCallbacks(this); 141 postDelayed(this, 1000); 142 } 143 } 144 }; 145 146 private final Runnable mResetToMetadata = new Runnable() { 147 public void run() { 148 resetToMetadata(); 149 } 150 }; 151 152 private final OnClickListener mTransportCommandListener = new OnClickListener() { 153 public void onClick(View v) { 154 int keyCode = -1; 155 if (v == mBtnPrev) { 156 keyCode = KeyEvent.KEYCODE_MEDIA_PREVIOUS; 157 } else if (v == mBtnNext) { 158 keyCode = KeyEvent.KEYCODE_MEDIA_NEXT; 159 } else if (v == mBtnPlay) { 160 keyCode = KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE; 161 } 162 if (keyCode != -1) { 163 sendMediaButtonClick(keyCode); 164 } 165 } 166 }; 167 168 private final OnLongClickListener mTransportShowSeekBarListener = new OnLongClickListener() { 169 @Override 170 public boolean onLongClick(View v) { 171 if (mSeekEnabled) { 172 return tryToggleSeekBar(); 173 } 174 return false; 175 } 176 }; 177 178 private final SeekBar.OnSeekBarChangeListener mOnSeekBarChangeListener = 179 new SeekBar.OnSeekBarChangeListener() { 180 @Override 181 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 182 if (fromUser) { 183 scrubTo(progress); 184 delayResetToMetadata(); 185 } 186 updateSeekDisplay(); 187 } 188 189 @Override 190 public void onStartTrackingTouch(SeekBar seekBar) { 191 mUserSeeking = true; 192 } 193 194 @Override 195 public void onStopTrackingTouch(SeekBar seekBar) { 196 mUserSeeking = false; 197 } 198 }; 199 200 private static final int TRANSITION_DURATION = 200; 201 private final TransitionSet mMetadataChangeTransition; 202 203 KeyguardHostView.TransportControlCallback mTransportControlCallback; 204 205 private final KeyguardUpdateMonitorCallback mUpdateMonitor 206 = new KeyguardUpdateMonitorCallback() { 207 public void onScreenTurnedOff(int why) { 208 setEnableMarquee(false); 209 }; 210 public void onScreenTurnedOn() { 211 setEnableMarquee(true); 212 }; 213 }; 214 215 public KeyguardTransportControlView(Context context, AttributeSet attrs) { 216 super(context, attrs); 217 if (DEBUG) Log.v(TAG, "Create TCV " + this); 218 mAudioManager = new AudioManager(mContext); 219 mCurrentPlayState = RemoteControlClient.PLAYSTATE_NONE; // until we get a callback 220 mRemoteController = new RemoteController(context, mRCClientUpdateListener); 221 222 final DisplayMetrics dm = context.getResources().getDisplayMetrics(); 223 final int dim = Math.max(dm.widthPixels, dm.heightPixels); 224 mRemoteController.setArtworkConfiguration(true, dim, dim); 225 226 final ChangeText tc = new ChangeText(); 227 tc.setChangeBehavior(ChangeText.CHANGE_BEHAVIOR_OUT_IN); 228 final TransitionSet inner = new TransitionSet(); 229 inner.addTransition(tc).addTransition(new ChangeBounds()); 230 final TransitionSet tg = new TransitionSet(); 231 tg.addTransition(new Fade(Fade.OUT)).addTransition(inner). 232 addTransition(new Fade(Fade.IN)); 233 tg.setOrdering(TransitionSet.ORDERING_SEQUENTIAL); 234 tg.setDuration(TRANSITION_DURATION); 235 mMetadataChangeTransition = tg; 236 } 237 238 private void updateTransportControls(int transportControlFlags) { 239 mTransportControlFlags = transportControlFlags; 240 setSeekBarsEnabled( 241 (transportControlFlags & RemoteControlClient.FLAG_KEY_MEDIA_POSITION_UPDATE) != 0); 242 } 243 244 void setSeekBarsEnabled(boolean enabled) { 245 if (enabled == mSeekEnabled) return; 246 247 mSeekEnabled = enabled; 248 if (mTransientSeek.getVisibility() == VISIBLE) { 249 mTransientSeek.setVisibility(INVISIBLE); 250 mMetadataContainer.setVisibility(VISIBLE); 251 mUserSeeking = false; 252 cancelResetToMetadata(); 253 } 254 if (enabled) { 255 mUpdateSeekBars.run(); 256 } else { 257 removeCallbacks(mUpdateSeekBars); 258 } 259 } 260 261 public void setTransportControlCallback(KeyguardHostView.TransportControlCallback 262 transportControlCallback) { 263 mTransportControlCallback = transportControlCallback; 264 } 265 266 private void setEnableMarquee(boolean enabled) { 267 if (DEBUG) Log.v(TAG, (enabled ? "Enable" : "Disable") + " transport text marquee"); 268 if (mTrackTitle != null) mTrackTitle.setSelected(enabled); 269 if (mTrackArtistAlbum != null) mTrackTitle.setSelected(enabled); 270 } 271 272 @Override 273 public void onFinishInflate() { 274 super.onFinishInflate(); 275 mInfoContainer = (ViewGroup) findViewById(R.id.info_container); 276 mMetadataContainer = (ViewGroup) findViewById(R.id.metadata_container); 277 mBadge = (ImageView) findViewById(R.id.badge); 278 mTrackTitle = (TextView) findViewById(R.id.title); 279 mTrackArtistAlbum = (TextView) findViewById(R.id.artist_album); 280 mTransientSeek = findViewById(R.id.transient_seek); 281 mTransientSeekBar = (SeekBar) findViewById(R.id.transient_seek_bar); 282 mTransientSeekBar.setOnSeekBarChangeListener(mOnSeekBarChangeListener); 283 mTransientSeekTimeElapsed = (TextView) findViewById(R.id.transient_seek_time_elapsed); 284 mTransientSeekTimeRemaining = (TextView) findViewById(R.id.transient_seek_time_remaining); 285 mBtnPrev = (ImageView) findViewById(R.id.btn_prev); 286 mBtnPlay = (ImageView) findViewById(R.id.btn_play); 287 mBtnNext = (ImageView) findViewById(R.id.btn_next); 288 final View buttons[] = { mBtnPrev, mBtnPlay, mBtnNext }; 289 for (View view : buttons) { 290 view.setOnClickListener(mTransportCommandListener); 291 view.setOnLongClickListener(mTransportShowSeekBarListener); 292 } 293 final boolean screenOn = KeyguardUpdateMonitor.getInstance(mContext).isScreenOn(); 294 setEnableMarquee(screenOn); 295 } 296 297 @Override 298 public void onAttachedToWindow() { 299 super.onAttachedToWindow(); 300 if (DEBUG) Log.v(TAG, "onAttachToWindow()"); 301 if (mPopulateMetadataWhenAttached != null) { 302 updateMetadata(mPopulateMetadataWhenAttached); 303 mPopulateMetadataWhenAttached = null; 304 } 305 if (DEBUG) Log.v(TAG, "Registering TCV " + this); 306 mMetadata.clear(); 307 mAudioManager.registerRemoteController(mRemoteController); 308 KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitor); 309 } 310 311 @Override 312 protected void onConfigurationChanged(Configuration newConfig) { 313 super.onConfigurationChanged(newConfig); 314 final DisplayMetrics dm = getContext().getResources().getDisplayMetrics(); 315 final int dim = Math.max(dm.widthPixels, dm.heightPixels); 316 mRemoteController.setArtworkConfiguration(true, dim, dim); 317 } 318 319 @Override 320 public void onDetachedFromWindow() { 321 if (DEBUG) Log.v(TAG, "onDetachFromWindow()"); 322 super.onDetachedFromWindow(); 323 if (DEBUG) Log.v(TAG, "Unregistering TCV " + this); 324 mAudioManager.unregisterRemoteController(mRemoteController); 325 KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateMonitor); 326 mMetadata.clear(); 327 mUserSeeking = false; 328 removeCallbacks(mUpdateSeekBars); 329 } 330 331 void setBadgeIcon(Drawable bmp) { 332 mBadge.setImageDrawable(bmp); 333 334 final ColorMatrix cm = new ColorMatrix(); 335 cm.setSaturation(0); 336 mBadge.setColorFilter(new ColorMatrixColorFilter(cm)); 337 mBadge.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SCREEN)); 338 mBadge.setImageAlpha(0xef); 339 } 340 341 class Metadata { 342 private String artist; 343 private String trackTitle; 344 private String albumTitle; 345 private Bitmap bitmap; 346 private long duration; 347 348 public void clear() { 349 artist = null; 350 trackTitle = null; 351 albumTitle = null; 352 bitmap = null; 353 duration = -1; 354 } 355 356 public String toString() { 357 return "Metadata[artist=" + artist + " trackTitle=" + trackTitle + 358 " albumTitle=" + albumTitle + " duration=" + duration + "]"; 359 } 360 } 361 362 void clearMetadata() { 363 mPopulateMetadataWhenAttached = null; 364 mMetadata.clear(); 365 populateMetadata(); 366 } 367 368 void updateMetadata(RemoteController.MetadataEditor data) { 369 if (isAttachedToWindow()) { 370 mMetadata.artist = data.getString(MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST, 371 mMetadata.artist); 372 mMetadata.trackTitle = data.getString(MediaMetadataRetriever.METADATA_KEY_TITLE, 373 mMetadata.trackTitle); 374 mMetadata.albumTitle = data.getString(MediaMetadataRetriever.METADATA_KEY_ALBUM, 375 mMetadata.albumTitle); 376 mMetadata.duration = data.getLong(MediaMetadataRetriever.METADATA_KEY_DURATION, -1); 377 mMetadata.bitmap = data.getBitmap(MediaMetadataEditor.BITMAP_KEY_ARTWORK, 378 mMetadata.bitmap); 379 populateMetadata(); 380 } else { 381 mPopulateMetadataWhenAttached = data; 382 } 383 } 384 385 /** 386 * Populates the given metadata into the view 387 */ 388 private void populateMetadata() { 389 if (ANIMATE_TRANSITIONS && isLaidOut() && mMetadataContainer.getVisibility() == VISIBLE) { 390 TransitionManager.beginDelayedTransition(mMetadataContainer, mMetadataChangeTransition); 391 } 392 393 final String remoteClientPackage = mRemoteController.getRemoteControlClientPackageName(); 394 Drawable badgeIcon = null; 395 try { 396 badgeIcon = getContext().getPackageManager().getApplicationIcon(remoteClientPackage); 397 } catch (PackageManager.NameNotFoundException e) { 398 Log.e(TAG, "Couldn't get remote control client package icon", e); 399 } 400 setBadgeIcon(badgeIcon); 401 mTrackTitle.setText(!TextUtils.isEmpty(mMetadata.trackTitle) 402 ? mMetadata.trackTitle : null); 403 404 final StringBuilder sb = new StringBuilder(); 405 if (!TextUtils.isEmpty(mMetadata.artist)) { 406 if (sb.length() != 0) { 407 sb.append(" - "); 408 } 409 sb.append(mMetadata.artist); 410 } 411 if (!TextUtils.isEmpty(mMetadata.albumTitle)) { 412 if (sb.length() != 0) { 413 sb.append(" - "); 414 } 415 sb.append(mMetadata.albumTitle); 416 } 417 418 final String trackArtistAlbum = sb.toString(); 419 mTrackArtistAlbum.setText(!TextUtils.isEmpty(trackArtistAlbum) ? 420 trackArtistAlbum : null); 421 422 if (mMetadata.duration >= 0) { 423 setSeekBarsEnabled(true); 424 setSeekBarDuration(mMetadata.duration); 425 426 final String skeleton; 427 428 if (mMetadata.duration >= 86400000) { 429 skeleton = "DDD kk mm ss"; 430 } else if (mMetadata.duration >= 3600000) { 431 skeleton = "kk mm ss"; 432 } else { 433 skeleton = "mm ss"; 434 } 435 mFormat = new SimpleDateFormat(DateFormat.getBestDateTimePattern( 436 getContext().getResources().getConfiguration().locale, 437 skeleton)); 438 mFormat.setTimeZone(TimeZone.getTimeZone("GMT+0")); 439 } else { 440 setSeekBarsEnabled(false); 441 } 442 443 KeyguardUpdateMonitor.getInstance(getContext()).dispatchSetBackground(mMetadata.bitmap); 444 final int flags = mTransportControlFlags; 445 setVisibilityBasedOnFlag(mBtnPrev, flags, RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS); 446 setVisibilityBasedOnFlag(mBtnNext, flags, RemoteControlClient.FLAG_KEY_MEDIA_NEXT); 447 setVisibilityBasedOnFlag(mBtnPlay, flags, 448 RemoteControlClient.FLAG_KEY_MEDIA_PLAY 449 | RemoteControlClient.FLAG_KEY_MEDIA_PAUSE 450 | RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE 451 | RemoteControlClient.FLAG_KEY_MEDIA_STOP); 452 453 updatePlayPauseState(mCurrentPlayState); 454 } 455 456 void updateSeekDisplay() { 457 if (mMetadata != null && mRemoteController != null && mFormat != null) { 458 if (mTimeElapsed == null) { 459 mTimeElapsed = new Date(); 460 } 461 if (mTimeRemaining == null) { 462 mTimeRemaining = new Date(); 463 } 464 mTimeElapsed.setTime(mRemoteController.getEstimatedMediaPosition()); 465 mTimeRemaining.setTime(mMetadata.duration - mTimeElapsed.getTime()); 466 mTransientSeekTimeElapsed.setText(mFormat.format(mTimeElapsed)); 467 mTransientSeekTimeRemaining.setText(mFormat.format(mTimeRemaining)); 468 469 if (DEBUG) Log.d(TAG, "updateSeekDisplay timeElapsed=" + mTimeElapsed + 470 " duration=" + mMetadata.duration + " remaining=" + mTimeRemaining); 471 } 472 } 473 474 boolean tryToggleSeekBar() { 475 if (ANIMATE_TRANSITIONS) { 476 TransitionManager.beginDelayedTransition(mInfoContainer); 477 } 478 if (mTransientSeek.getVisibility() == VISIBLE) { 479 mTransientSeek.setVisibility(INVISIBLE); 480 mMetadataContainer.setVisibility(VISIBLE); 481 cancelResetToMetadata(); 482 } else { 483 mTransientSeek.setVisibility(VISIBLE); 484 mMetadataContainer.setVisibility(INVISIBLE); 485 delayResetToMetadata(); 486 } 487 mTransportControlCallback.userActivity(); 488 return true; 489 } 490 491 void resetToMetadata() { 492 if (ANIMATE_TRANSITIONS) { 493 TransitionManager.beginDelayedTransition(mInfoContainer); 494 } 495 if (mTransientSeek.getVisibility() == VISIBLE) { 496 mTransientSeek.setVisibility(INVISIBLE); 497 mMetadataContainer.setVisibility(VISIBLE); 498 } 499 // TODO Also hide ratings, if applicable 500 } 501 502 void delayResetToMetadata() { 503 removeCallbacks(mResetToMetadata); 504 postDelayed(mResetToMetadata, RESET_TO_METADATA_DELAY); 505 } 506 507 void cancelResetToMetadata() { 508 removeCallbacks(mResetToMetadata); 509 } 510 511 void setSeekBarDuration(long duration) { 512 mTransientSeekBar.setMax((int) duration); 513 } 514 515 void scrubTo(int progress) { 516 mRemoteController.seekTo(progress); 517 mTransportControlCallback.userActivity(); 518 } 519 520 private static void setVisibilityBasedOnFlag(View view, int flags, int flag) { 521 if ((flags & flag) != 0) { 522 view.setVisibility(View.VISIBLE); 523 } else { 524 view.setVisibility(View.INVISIBLE); 525 } 526 } 527 528 private void updatePlayPauseState(int state) { 529 if (DEBUG) Log.v(TAG, 530 "updatePlayPauseState(), old=" + mCurrentPlayState + ", state=" + state); 531 if (state == mCurrentPlayState) { 532 return; 533 } 534 final int imageResId; 535 final int imageDescId; 536 switch (state) { 537 case RemoteControlClient.PLAYSTATE_ERROR: 538 imageResId = R.drawable.stat_sys_warning; 539 // TODO use more specific image description string for warning, but here the "play" 540 // message is still valid because this button triggers a play command. 541 imageDescId = R.string.keyguard_transport_play_description; 542 break; 543 544 case RemoteControlClient.PLAYSTATE_PLAYING: 545 imageResId = R.drawable.ic_media_pause; 546 imageDescId = R.string.keyguard_transport_pause_description; 547 if (mSeekEnabled) { 548 mUpdateSeekBars.run(); 549 } 550 break; 551 552 case RemoteControlClient.PLAYSTATE_BUFFERING: 553 imageResId = R.drawable.ic_media_stop; 554 imageDescId = R.string.keyguard_transport_stop_description; 555 break; 556 557 case RemoteControlClient.PLAYSTATE_PAUSED: 558 default: 559 imageResId = R.drawable.ic_media_play; 560 imageDescId = R.string.keyguard_transport_play_description; 561 break; 562 } 563 564 if (state != RemoteControlClient.PLAYSTATE_PLAYING) { 565 removeCallbacks(mUpdateSeekBars); 566 updateSeekBars(); 567 } 568 mBtnPlay.setImageResource(imageResId); 569 mBtnPlay.setContentDescription(getResources().getString(imageDescId)); 570 mCurrentPlayState = state; 571 } 572 573 boolean updateSeekBars() { 574 final int position = (int) mRemoteController.getEstimatedMediaPosition(); 575 if (position >= 0) { 576 if (DEBUG) Log.v(TAG, "Seek to " + position); 577 if (!mUserSeeking) { 578 mTransientSeekBar.setProgress(position); 579 } 580 return true; 581 } 582 Log.w(TAG, "Updating seek bars; received invalid estimated media position (" + 583 position + "). Disabling seek."); 584 setSeekBarsEnabled(false); 585 return false; 586 } 587 588 static class SavedState extends BaseSavedState { 589 boolean clientPresent; 590 591 SavedState(Parcelable superState) { 592 super(superState); 593 } 594 595 private SavedState(Parcel in) { 596 super(in); 597 this.clientPresent = in.readInt() != 0; 598 } 599 600 @Override 601 public void writeToParcel(Parcel out, int flags) { 602 super.writeToParcel(out, flags); 603 out.writeInt(this.clientPresent ? 1 : 0); 604 } 605 606 public static final Parcelable.Creator<SavedState> CREATOR 607 = new Parcelable.Creator<SavedState>() { 608 public SavedState createFromParcel(Parcel in) { 609 return new SavedState(in); 610 } 611 612 public SavedState[] newArray(int size) { 613 return new SavedState[size]; 614 } 615 }; 616 } 617 618 private void sendMediaButtonClick(int keyCode) { 619 // TODO We should think about sending these up/down events accurately with touch up/down 620 // on the buttons, but in the near term this will interfere with the long press behavior. 621 mRemoteController.sendMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keyCode)); 622 mRemoteController.sendMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, keyCode)); 623 624 mTransportControlCallback.userActivity(); 625 } 626 627 public boolean providesClock() { 628 return false; 629 } 630 631 private boolean wasPlayingRecently(int state, long stateChangeTimeMs) { 632 switch (state) { 633 case RemoteControlClient.PLAYSTATE_PLAYING: 634 case RemoteControlClient.PLAYSTATE_FAST_FORWARDING: 635 case RemoteControlClient.PLAYSTATE_REWINDING: 636 case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS: 637 case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS: 638 case RemoteControlClient.PLAYSTATE_BUFFERING: 639 // actively playing or about to play 640 return true; 641 case RemoteControlClient.PLAYSTATE_NONE: 642 return false; 643 case RemoteControlClient.PLAYSTATE_STOPPED: 644 case RemoteControlClient.PLAYSTATE_PAUSED: 645 case RemoteControlClient.PLAYSTATE_ERROR: 646 // we have stopped playing, check how long ago 647 if (DEBUG) { 648 if ((SystemClock.elapsedRealtime() - stateChangeTimeMs) < DISPLAY_TIMEOUT_MS) { 649 Log.v(TAG, "wasPlayingRecently: time < TIMEOUT was playing recently"); 650 } else { 651 Log.v(TAG, "wasPlayingRecently: time > TIMEOUT"); 652 } 653 } 654 return ((SystemClock.elapsedRealtime() - stateChangeTimeMs) < DISPLAY_TIMEOUT_MS); 655 default: 656 Log.e(TAG, "Unknown playback state " + state + " in wasPlayingRecently()"); 657 return false; 658 } 659 } 660} 661