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