KeyguardTransportControlView.java revision 1693f6a5639a1f993bdc1dca9dade5e3eb3c14f2
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 = false; 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 removeCallbacks(this); 138 postDelayed(this, 1000); 139 } 140 } 141 }; 142 143 private final Runnable mResetToMetadata = new Runnable() { 144 public void run() { 145 resetToMetadata(); 146 } 147 }; 148 149 private final OnClickListener mTransportCommandListener = new OnClickListener() { 150 public void onClick(View v) { 151 int keyCode = -1; 152 if (v == mBtnPrev) { 153 keyCode = KeyEvent.KEYCODE_MEDIA_PREVIOUS; 154 } else if (v == mBtnNext) { 155 keyCode = KeyEvent.KEYCODE_MEDIA_NEXT; 156 } else if (v == mBtnPlay) { 157 keyCode = KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE; 158 } 159 if (keyCode != -1) { 160 sendMediaButtonClick(keyCode); 161 } 162 } 163 }; 164 165 private final OnLongClickListener mTransportShowSeekBarListener = new OnLongClickListener() { 166 @Override 167 public boolean onLongClick(View v) { 168 if (mSeekEnabled) { 169 return tryToggleSeekBar(); 170 } 171 return false; 172 } 173 }; 174 175 private final SeekBar.OnSeekBarChangeListener mOnSeekBarChangeListener = 176 new SeekBar.OnSeekBarChangeListener() { 177 @Override 178 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 179 if (fromUser) { 180 scrubTo(progress); 181 delayResetToMetadata(); 182 } 183 updateSeekDisplay(); 184 } 185 186 @Override 187 public void onStartTrackingTouch(SeekBar seekBar) { 188 mUserSeeking = true; 189 } 190 191 @Override 192 public void onStopTrackingTouch(SeekBar seekBar) { 193 mUserSeeking = false; 194 } 195 }; 196 197 private static final int TRANSITION_DURATION = 200; 198 private final TransitionSet mMetadataChangeTransition; 199 200 KeyguardHostView.TransportControlCallback mTransportControlCallback; 201 202 private final KeyguardUpdateMonitorCallback mUpdateMonitor 203 = new KeyguardUpdateMonitorCallback() { 204 public void onScreenTurnedOff(int why) { 205 setEnableMarquee(false); 206 }; 207 public void onScreenTurnedOn() { 208 setEnableMarquee(true); 209 }; 210 }; 211 212 public KeyguardTransportControlView(Context context, AttributeSet attrs) { 213 super(context, attrs); 214 if (DEBUG) Log.v(TAG, "Create TCV " + this); 215 mAudioManager = new AudioManager(mContext); 216 mCurrentPlayState = RemoteControlClient.PLAYSTATE_NONE; // until we get a callback 217 mRemoteController = new RemoteController(context, mRCClientUpdateListener); 218 219 final DisplayMetrics dm = context.getResources().getDisplayMetrics(); 220 final int dim = Math.max(dm.widthPixels, dm.heightPixels); 221 mRemoteController.setArtworkConfiguration(true, dim, dim); 222 223 final ChangeText tc = new ChangeText(); 224 tc.setChangeBehavior(ChangeText.CHANGE_BEHAVIOR_OUT_IN); 225 final TransitionSet inner = new TransitionSet(); 226 inner.addTransition(tc).addTransition(new ChangeBounds()); 227 final TransitionSet tg = new TransitionSet(); 228 tg.addTransition(new Fade(Fade.OUT)).addTransition(inner). 229 addTransition(new Fade(Fade.IN)); 230 tg.setOrdering(TransitionSet.ORDERING_SEQUENTIAL); 231 tg.setDuration(TRANSITION_DURATION); 232 mMetadataChangeTransition = tg; 233 } 234 235 private void updateTransportControls(int transportControlFlags) { 236 mTransportControlFlags = transportControlFlags; 237 setSeekBarsEnabled( 238 (transportControlFlags & RemoteControlClient.FLAG_KEY_MEDIA_POSITION_UPDATE) != 0); 239 } 240 241 void setSeekBarsEnabled(boolean enabled) { 242 if (enabled == mSeekEnabled) return; 243 244 mSeekEnabled = enabled; 245 if (mTransientSeek.getVisibility() == VISIBLE) { 246 mTransientSeek.setVisibility(INVISIBLE); 247 mMetadataContainer.setVisibility(VISIBLE); 248 mUserSeeking = false; 249 cancelResetToMetadata(); 250 } 251 if (enabled) { 252 mUpdateSeekBars.run(); 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(mMetadata.bitmap); 441 final int flags = mTransportControlFlags; 442 setVisibilityBasedOnFlag(mBtnPrev, flags, RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS); 443 setVisibilityBasedOnFlag(mBtnNext, flags, RemoteControlClient.FLAG_KEY_MEDIA_NEXT); 444 setVisibilityBasedOnFlag(mBtnPlay, flags, 445 RemoteControlClient.FLAG_KEY_MEDIA_PLAY 446 | RemoteControlClient.FLAG_KEY_MEDIA_PAUSE 447 | RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE 448 | RemoteControlClient.FLAG_KEY_MEDIA_STOP); 449 450 updatePlayPauseState(mCurrentPlayState); 451 } 452 453 void updateSeekDisplay() { 454 if (mMetadata != null && mRemoteController != null && mFormat != null) { 455 final long timeElapsed = mRemoteController.getEstimatedMediaPosition(); 456 final long duration = mMetadata.duration; 457 final long remaining = duration - timeElapsed; 458 459 mTransientSeekTimeElapsed.setText(mFormat.format(new Date(timeElapsed))); 460 mTransientSeekTimeRemaining.setText(mFormat.format(new Date(remaining))); 461 462 if (DEBUG) Log.d(TAG, "updateSeekDisplay timeElapsed=" + timeElapsed + 463 " duration=" + duration + " remaining=" + remaining); 464 } 465 } 466 467 boolean tryToggleSeekBar() { 468 if (ANIMATE_TRANSITIONS) { 469 TransitionManager.beginDelayedTransition(mInfoContainer); 470 } 471 if (mTransientSeek.getVisibility() == VISIBLE) { 472 mTransientSeek.setVisibility(INVISIBLE); 473 mMetadataContainer.setVisibility(VISIBLE); 474 cancelResetToMetadata(); 475 } else { 476 mTransientSeek.setVisibility(VISIBLE); 477 mMetadataContainer.setVisibility(INVISIBLE); 478 delayResetToMetadata(); 479 } 480 mTransportControlCallback.userActivity(); 481 return true; 482 } 483 484 void resetToMetadata() { 485 if (ANIMATE_TRANSITIONS) { 486 TransitionManager.beginDelayedTransition(mInfoContainer); 487 } 488 if (mTransientSeek.getVisibility() == VISIBLE) { 489 mTransientSeek.setVisibility(INVISIBLE); 490 mMetadataContainer.setVisibility(VISIBLE); 491 } 492 // TODO Also hide ratings, if applicable 493 } 494 495 void delayResetToMetadata() { 496 removeCallbacks(mResetToMetadata); 497 postDelayed(mResetToMetadata, RESET_TO_METADATA_DELAY); 498 } 499 500 void cancelResetToMetadata() { 501 removeCallbacks(mResetToMetadata); 502 } 503 504 void setSeekBarDuration(long duration) { 505 mTransientSeekBar.setMax((int) duration); 506 } 507 508 void scrubTo(int progress) { 509 mRemoteController.seekTo(progress); 510 mTransportControlCallback.userActivity(); 511 } 512 513 private static void setVisibilityBasedOnFlag(View view, int flags, int flag) { 514 if ((flags & flag) != 0) { 515 view.setVisibility(View.VISIBLE); 516 } else { 517 view.setVisibility(View.INVISIBLE); 518 } 519 } 520 521 private void updatePlayPauseState(int state) { 522 if (DEBUG) Log.v(TAG, 523 "updatePlayPauseState(), old=" + mCurrentPlayState + ", state=" + state); 524 if (state == mCurrentPlayState) { 525 return; 526 } 527 final int imageResId; 528 final int imageDescId; 529 switch (state) { 530 case RemoteControlClient.PLAYSTATE_ERROR: 531 imageResId = R.drawable.stat_sys_warning; 532 // TODO use more specific image description string for warning, but here the "play" 533 // message is still valid because this button triggers a play command. 534 imageDescId = R.string.keyguard_transport_play_description; 535 break; 536 537 case RemoteControlClient.PLAYSTATE_PLAYING: 538 imageResId = R.drawable.ic_media_pause; 539 imageDescId = R.string.keyguard_transport_pause_description; 540 if (mSeekEnabled) { 541 mUpdateSeekBars.run(); 542 } 543 break; 544 545 case RemoteControlClient.PLAYSTATE_BUFFERING: 546 imageResId = R.drawable.ic_media_stop; 547 imageDescId = R.string.keyguard_transport_stop_description; 548 break; 549 550 case RemoteControlClient.PLAYSTATE_PAUSED: 551 default: 552 imageResId = R.drawable.ic_media_play; 553 imageDescId = R.string.keyguard_transport_play_description; 554 break; 555 } 556 557 if (state != RemoteControlClient.PLAYSTATE_PLAYING) { 558 removeCallbacks(mUpdateSeekBars); 559 updateSeekBars(); 560 } 561 mBtnPlay.setImageResource(imageResId); 562 mBtnPlay.setContentDescription(getResources().getString(imageDescId)); 563 mCurrentPlayState = state; 564 } 565 566 boolean updateSeekBars() { 567 final int position = (int) mRemoteController.getEstimatedMediaPosition(); 568 if (position >= 0) { 569 if (DEBUG) Log.v(TAG, "Seek to " + position); 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