KeyguardTransportControlView.java revision 20daffd91e4a53054f8c4d7a66c2a68100abee03
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.drawable.Drawable; 26import android.media.AudioManager; 27import android.media.MediaMetadataEditor; 28import android.media.MediaMetadataRetriever; 29import android.media.RemoteControlClient; 30import android.media.RemoteController; 31import android.os.Parcel; 32import android.os.Parcelable; 33import android.os.SystemClock; 34import android.text.TextUtils; 35import android.text.format.DateFormat; 36import android.transition.ChangeBounds; 37import android.transition.ChangeText; 38import android.transition.Fade; 39import android.transition.TransitionManager; 40import android.transition.TransitionSet; 41import android.util.AttributeSet; 42import android.util.DisplayMetrics; 43import android.util.Log; 44import android.view.KeyEvent; 45import android.view.View; 46import android.view.ViewGroup; 47import android.widget.FrameLayout; 48import android.widget.ImageView; 49import android.widget.SeekBar; 50import android.widget.TextView; 51 52import java.text.SimpleDateFormat; 53import java.util.Date; 54import java.util.TimeZone; 55 56/** 57 * This is the widget responsible for showing music controls in keyguard. 58 */ 59public class KeyguardTransportControlView extends FrameLayout { 60 61 private static final int DISPLAY_TIMEOUT_MS = 5000; // 5s 62 private static final int RESET_TO_METADATA_DELAY = 5000; 63 protected static final boolean DEBUG = false; 64 protected static final String TAG = "TransportControlView"; 65 66 private static final boolean ANIMATE_TRANSITIONS = true; 67 68 private ViewGroup mMetadataContainer; 69 private ViewGroup mInfoContainer; 70 private TextView mTrackTitle; 71 private TextView mTrackArtistAlbum; 72 73 private View mTransientSeek; 74 private SeekBar mTransientSeekBar; 75 private TextView mTransientSeekTimeElapsed; 76 private TextView mTransientSeekTimeRemaining; 77 78 private ImageView mBtnPrev; 79 private ImageView mBtnPlay; 80 private ImageView mBtnNext; 81 private Metadata mMetadata = new Metadata(); 82 private int mTransportControlFlags; 83 private int mCurrentPlayState; 84 private AudioManager mAudioManager; 85 private RemoteController mRemoteController; 86 87 private int MARQUEE_VIEWS[] = { R.id.title, R.id.artist_album }; 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 if (clearing) { 105 clearMetadata(); 106 } 107 } 108 109 @Override 110 public void onClientPlaybackStateUpdate(int state) { 111 setSeekBarsEnabled(false); 112 updatePlayPauseState(state); 113 } 114 115 @Override 116 public void onClientPlaybackStateUpdate(int state, long stateChangeTimeMs, 117 long currentPosMs, float speed) { 118 setSeekBarsEnabled(mMetadata != null && mMetadata.duration > 0); 119 updatePlayPauseState(state); 120 if (DEBUG) Log.d(TAG, "onClientPlaybackStateUpdate(state=" + state + 121 ", stateChangeTimeMs=" + stateChangeTimeMs + ", currentPosMs=" + currentPosMs + 122 ", speed=" + speed + ")"); 123 } 124 125 @Override 126 public void onClientTransportControlUpdate(int transportControlFlags) { 127 updateTransportControls(transportControlFlags); 128 } 129 130 @Override 131 public void onClientMetadataUpdate(RemoteController.MetadataEditor metadataEditor) { 132 updateMetadata(metadataEditor); 133 } 134 }; 135 136 private final Runnable mUpdateSeekBars = new Runnable() { 137 public void run() { 138 if (updateSeekBars()) { 139 postDelayed(this, 1000); 140 } 141 } 142 }; 143 144 private final Runnable mResetToMetadata = new Runnable() { 145 public void run() { 146 resetToMetadata(); 147 } 148 }; 149 150 private final OnClickListener mTransportCommandListener = new OnClickListener() { 151 public void onClick(View v) { 152 int keyCode = -1; 153 if (v == mBtnPrev) { 154 keyCode = KeyEvent.KEYCODE_MEDIA_PREVIOUS; 155 } else if (v == mBtnNext) { 156 keyCode = KeyEvent.KEYCODE_MEDIA_NEXT; 157 } else if (v == mBtnPlay) { 158 keyCode = KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE; 159 } 160 if (keyCode != -1) { 161 sendMediaButtonClick(keyCode); 162 } 163 } 164 }; 165 166 private final OnLongClickListener mTransportShowSeekBarListener = new OnLongClickListener() { 167 @Override 168 public boolean onLongClick(View v) { 169 if (mSeekEnabled) { 170 return tryToggleSeekBar(); 171 } 172 return false; 173 } 174 }; 175 176 private final SeekBar.OnSeekBarChangeListener mOnSeekBarChangeListener = 177 new SeekBar.OnSeekBarChangeListener() { 178 @Override 179 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 180 if (fromUser) { 181 scrubTo(progress); 182 delayResetToMetadata(); 183 } 184 updateSeekDisplay(); 185 } 186 187 @Override 188 public void onStartTrackingTouch(SeekBar seekBar) { 189 mUserSeeking = true; 190 } 191 192 @Override 193 public void onStopTrackingTouch(SeekBar seekBar) { 194 mUserSeeking = false; 195 } 196 }; 197 198 private static final int TRANSITION_DURATION = 200; 199 private final TransitionSet mMetadataChangeTransition; 200 201 KeyguardHostView.TransportControlCallback mTransportControlCallback; 202 203 private final KeyguardUpdateMonitorCallback mUpdateMonitor 204 = new KeyguardUpdateMonitorCallback() { 205 public void onScreenTurnedOff(int why) { 206 setEnableMarquee(false); 207 }; 208 public void onScreenTurnedOn() { 209 setEnableMarquee(true); 210 }; 211 }; 212 213 public KeyguardTransportControlView(Context context, AttributeSet attrs) { 214 super(context, attrs); 215 if (DEBUG) Log.v(TAG, "Create TCV " + this); 216 mAudioManager = new AudioManager(mContext); 217 mCurrentPlayState = RemoteControlClient.PLAYSTATE_NONE; // until we get a callback 218 mRemoteController = new RemoteController(context, mRCClientUpdateListener); 219 220 final DisplayMetrics dm = context.getResources().getDisplayMetrics(); 221 final int dim = Math.max(dm.widthPixels, dm.heightPixels); 222 mRemoteController.setArtworkConfiguration(true, dim, dim); 223 224 final ChangeText tc = new ChangeText(); 225 tc.setChangeBehavior(ChangeText.CHANGE_BEHAVIOR_OUT_IN); 226 final TransitionSet inner = new TransitionSet(); 227 inner.addTransition(tc).addTransition(new ChangeBounds()); 228 final TransitionSet tg = new TransitionSet(); 229 tg.addTransition(new Fade(Fade.OUT)).addTransition(inner). 230 addTransition(new Fade(Fade.IN)); 231 tg.setOrdering(TransitionSet.ORDERING_SEQUENTIAL); 232 tg.setDuration(TRANSITION_DURATION); 233 mMetadataChangeTransition = tg; 234 } 235 236 private void updateTransportControls(int transportControlFlags) { 237 mTransportControlFlags = transportControlFlags; 238 setSeekBarsEnabled( 239 (transportControlFlags & RemoteControlClient.FLAG_KEY_MEDIA_POSITION_UPDATE) != 0); 240 } 241 242 void setSeekBarsEnabled(boolean enabled) { 243 if (enabled == mSeekEnabled) return; 244 245 mSeekEnabled = enabled; 246 if (mTransientSeek.getVisibility() == VISIBLE) { 247 mTransientSeek.setVisibility(INVISIBLE); 248 mMetadataContainer.setVisibility(VISIBLE); 249 mUserSeeking = false; 250 cancelResetToMetadata(); 251 } 252 if (enabled) { 253 mUpdateSeekBars.run(); 254 postDelayed(mUpdateSeekBars, 1000); 255 } else { 256 removeCallbacks(mUpdateSeekBars); 257 } 258 } 259 260 public void setTransportControlCallback(KeyguardHostView.TransportControlCallback 261 transportControlCallback) { 262 mTransportControlCallback = transportControlCallback; 263 } 264 265 private void setEnableMarquee(boolean enabled) { 266 if (DEBUG) Log.v(TAG, (enabled ? "Enable" : "Disable") + " transport text marquee"); 267 for (int i = 0; i < MARQUEE_VIEWS.length; i++) { 268 View v = findViewById(MARQUEE_VIEWS[i]); 269 if (v != null) { 270 v.setSelected(enabled); 271 } 272 } 273 } 274 275 @Override 276 public void onFinishInflate() { 277 super.onFinishInflate(); 278 mInfoContainer = (ViewGroup) findViewById(R.id.info_container); 279 mMetadataContainer = (ViewGroup) findViewById(R.id.metadata_container); 280 mBadge = (ImageView) findViewById(R.id.badge); 281 mTrackTitle = (TextView) findViewById(R.id.title); 282 mTrackArtistAlbum = (TextView) findViewById(R.id.artist_album); 283 mTransientSeek = findViewById(R.id.transient_seek); 284 mTransientSeekBar = (SeekBar) findViewById(R.id.transient_seek_bar); 285 mTransientSeekBar.setOnSeekBarChangeListener(mOnSeekBarChangeListener); 286 mTransientSeekTimeElapsed = (TextView) findViewById(R.id.transient_seek_time_elapsed); 287 mTransientSeekTimeRemaining = (TextView) findViewById(R.id.transient_seek_time_remaining); 288 mBtnPrev = (ImageView) findViewById(R.id.btn_prev); 289 mBtnPlay = (ImageView) findViewById(R.id.btn_play); 290 mBtnNext = (ImageView) findViewById(R.id.btn_next); 291 final View buttons[] = { mBtnPrev, mBtnPlay, mBtnNext }; 292 for (View view : buttons) { 293 view.setOnClickListener(mTransportCommandListener); 294 view.setOnLongClickListener(mTransportShowSeekBarListener); 295 } 296 final boolean screenOn = KeyguardUpdateMonitor.getInstance(mContext).isScreenOn(); 297 setEnableMarquee(screenOn); 298 } 299 300 @Override 301 public void onAttachedToWindow() { 302 super.onAttachedToWindow(); 303 if (DEBUG) Log.v(TAG, "onAttachToWindow()"); 304 if (mPopulateMetadataWhenAttached != null) { 305 updateMetadata(mPopulateMetadataWhenAttached); 306 mPopulateMetadataWhenAttached = null; 307 } 308 if (DEBUG) Log.v(TAG, "Registering TCV " + this); 309 mAudioManager.registerRemoteController(mRemoteController); 310 KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitor); 311 } 312 313 @Override 314 protected void onConfigurationChanged(Configuration newConfig) { 315 super.onConfigurationChanged(newConfig); 316 final DisplayMetrics dm = getContext().getResources().getDisplayMetrics(); 317 final int dim = Math.max(dm.widthPixels, dm.heightPixels); 318 mRemoteController.setArtworkConfiguration(true, dim, dim); 319 } 320 321 @Override 322 public void onDetachedFromWindow() { 323 if (DEBUG) Log.v(TAG, "onDetachFromWindow()"); 324 super.onDetachedFromWindow(); 325 if (DEBUG) Log.v(TAG, "Unregistering TCV " + this); 326 mAudioManager.unregisterRemoteController(mRemoteController); 327 KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateMonitor); 328 mUserSeeking = false; 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.setImageAlpha(0xef); 338 } 339 340 class Metadata { 341 private String artist; 342 private String trackTitle; 343 private String albumTitle; 344 private Bitmap bitmap; 345 private long duration; 346 347 public void clear() { 348 artist = null; 349 trackTitle = null; 350 albumTitle = null; 351 bitmap = null; 352 duration = -1; 353 } 354 355 public String toString() { 356 return "Metadata[artist=" + artist + " trackTitle=" + trackTitle + 357 " albumTitle=" + albumTitle + " duration=" + duration + "]"; 358 } 359 } 360 361 void clearMetadata() { 362 mPopulateMetadataWhenAttached = null; 363 mMetadata.clear(); 364 populateMetadata(); 365 } 366 367 void updateMetadata(RemoteController.MetadataEditor data) { 368 if (isAttachedToWindow()) { 369 mMetadata.artist = data.getString(MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST, 370 mMetadata.artist); 371 mMetadata.trackTitle = data.getString(MediaMetadataRetriever.METADATA_KEY_TITLE, 372 mMetadata.trackTitle); 373 mMetadata.albumTitle = data.getString(MediaMetadataRetriever.METADATA_KEY_ALBUM, 374 mMetadata.albumTitle); 375 mMetadata.duration = data.getLong(MediaMetadataRetriever.METADATA_KEY_DURATION, -1); 376 mMetadata.bitmap = data.getBitmap(MediaMetadataEditor.BITMAP_KEY_ARTWORK, 377 mMetadata.bitmap); 378 populateMetadata(); 379 } else { 380 mPopulateMetadataWhenAttached = data; 381 } 382 } 383 384 /** 385 * Populates the given metadata into the view 386 */ 387 private void populateMetadata() { 388 if (ANIMATE_TRANSITIONS && isLaidOut() && mMetadataContainer.getVisibility() == VISIBLE) { 389 TransitionManager.beginDelayedTransition(mMetadataContainer, mMetadataChangeTransition); 390 } 391 392 final String remoteClientPackage = mRemoteController.getRemoteControlClientPackageName(); 393 Drawable badgeIcon = null; 394 try { 395 badgeIcon = getContext().getPackageManager().getApplicationIcon(remoteClientPackage); 396 } catch (PackageManager.NameNotFoundException e) { 397 Log.e(TAG, "Couldn't get remote control client package icon", e); 398 } 399 setBadgeIcon(badgeIcon); 400 if (!TextUtils.isEmpty(mMetadata.trackTitle)) { 401 mTrackTitle.setText(mMetadata.trackTitle); 402 } 403 StringBuilder sb = new StringBuilder(); 404 if (!TextUtils.isEmpty(mMetadata.artist)) { 405 if (sb.length() != 0) { 406 sb.append(" - "); 407 } 408 sb.append(mMetadata.artist); 409 } 410 if (!TextUtils.isEmpty(mMetadata.albumTitle)) { 411 if (sb.length() != 0) { 412 sb.append(" - "); 413 } 414 sb.append(mMetadata.albumTitle); 415 } 416 mTrackArtistAlbum.setText(sb.toString()); 417 418 if (mMetadata.duration >= 0) { 419 setSeekBarsEnabled(true); 420 setSeekBarDuration(mMetadata.duration); 421 422 final String skeleton; 423 424 if (mMetadata.duration >= 86400000) { 425 skeleton = "DDD kk mm ss"; 426 } else if (mMetadata.duration >= 3600000) { 427 skeleton = "kk mm ss"; 428 } else { 429 skeleton = "mm ss"; 430 } 431 mFormat = new SimpleDateFormat(DateFormat.getBestDateTimePattern( 432 getContext().getResources().getConfiguration().locale, 433 skeleton)); 434 mFormat.setTimeZone(TimeZone.getTimeZone("GMT+0")); 435 } else { 436 setSeekBarsEnabled(false); 437 } 438 439 KeyguardUpdateMonitor.getInstance(getContext()).dispatchSetBackground( 440 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.GONE); 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 postDelayed(mUpdateSeekBars, 1000); 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 (!mUserSeeking) { 570 mTransientSeekBar.setProgress(position); 571 } 572 return true; 573 } 574 Log.w(TAG, "Updating seek bars; received invalid estimated media position (" + 575 position + "). Disabling seek."); 576 setSeekBarsEnabled(false); 577 return false; 578 } 579 580 static class SavedState extends BaseSavedState { 581 boolean clientPresent; 582 583 SavedState(Parcelable superState) { 584 super(superState); 585 } 586 587 private SavedState(Parcel in) { 588 super(in); 589 this.clientPresent = in.readInt() != 0; 590 } 591 592 @Override 593 public void writeToParcel(Parcel out, int flags) { 594 super.writeToParcel(out, flags); 595 out.writeInt(this.clientPresent ? 1 : 0); 596 } 597 598 public static final Parcelable.Creator<SavedState> CREATOR 599 = new Parcelable.Creator<SavedState>() { 600 public SavedState createFromParcel(Parcel in) { 601 return new SavedState(in); 602 } 603 604 public SavedState[] newArray(int size) { 605 return new SavedState[size]; 606 } 607 }; 608 } 609 610 private void sendMediaButtonClick(int keyCode) { 611 // TODO We should think about sending these up/down events accurately with touch up/down 612 // on the buttons, but in the near term this will interfere with the long press behavior. 613 mRemoteController.sendMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keyCode)); 614 mRemoteController.sendMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, keyCode)); 615 616 mTransportControlCallback.userActivity(); 617 } 618 619 public boolean providesClock() { 620 return false; 621 } 622 623 private boolean wasPlayingRecently(int state, long stateChangeTimeMs) { 624 switch (state) { 625 case RemoteControlClient.PLAYSTATE_PLAYING: 626 case RemoteControlClient.PLAYSTATE_FAST_FORWARDING: 627 case RemoteControlClient.PLAYSTATE_REWINDING: 628 case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS: 629 case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS: 630 case RemoteControlClient.PLAYSTATE_BUFFERING: 631 // actively playing or about to play 632 return true; 633 case RemoteControlClient.PLAYSTATE_NONE: 634 return false; 635 case RemoteControlClient.PLAYSTATE_STOPPED: 636 case RemoteControlClient.PLAYSTATE_PAUSED: 637 case RemoteControlClient.PLAYSTATE_ERROR: 638 // we have stopped playing, check how long ago 639 if (DEBUG) { 640 if ((SystemClock.elapsedRealtime() - stateChangeTimeMs) < DISPLAY_TIMEOUT_MS) { 641 Log.v(TAG, "wasPlayingRecently: time < TIMEOUT was playing recently"); 642 } else { 643 Log.v(TAG, "wasPlayingRecently: time > TIMEOUT"); 644 } 645 } 646 return ((SystemClock.elapsedRealtime() - stateChangeTimeMs) < DISPLAY_TIMEOUT_MS); 647 default: 648 Log.e(TAG, "Unknown playback state " + state + " in wasPlayingRecently()"); 649 return false; 650 } 651 } 652} 653