Merge "Fix scrubbing behavior on keyguard music transport" into klp-dev
This commit is contained in:
@@ -60,12 +60,12 @@ import java.util.TimeZone;
|
||||
*/
|
||||
public class KeyguardTransportControlView extends FrameLayout {
|
||||
|
||||
private static final int DISPLAY_TIMEOUT_MS = 5000; // 5s
|
||||
private static final int RESET_TO_METADATA_DELAY = 5000;
|
||||
protected static final boolean DEBUG = false;
|
||||
protected static final String TAG = "TransportControlView";
|
||||
|
||||
private static final boolean ANIMATE_TRANSITIONS = true;
|
||||
protected static final long QUIESCENT_PLAYBACK_FACTOR = 1000;
|
||||
|
||||
private ViewGroup mMetadataContainer;
|
||||
private ViewGroup mInfoContainer;
|
||||
@@ -89,11 +89,9 @@ public class KeyguardTransportControlView extends FrameLayout {
|
||||
private ImageView mBadge;
|
||||
|
||||
private boolean mSeekEnabled;
|
||||
private boolean mUserSeeking;
|
||||
private java.text.DateFormat mFormat;
|
||||
|
||||
private Date mTimeElapsed;
|
||||
private Date mTrackDuration;
|
||||
private Date mTempDate = new Date();
|
||||
|
||||
/**
|
||||
* The metadata which should be populated into the view once we've been attached
|
||||
@@ -111,18 +109,25 @@ public class KeyguardTransportControlView extends FrameLayout {
|
||||
|
||||
@Override
|
||||
public void onClientPlaybackStateUpdate(int state) {
|
||||
setSeekBarsEnabled(false);
|
||||
updatePlayPauseState(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClientPlaybackStateUpdate(int state, long stateChangeTimeMs,
|
||||
long currentPosMs, float speed) {
|
||||
setSeekBarsEnabled(mMetadata != null && mMetadata.duration > 0);
|
||||
updatePlayPauseState(state);
|
||||
if (DEBUG) Log.d(TAG, "onClientPlaybackStateUpdate(state=" + state +
|
||||
", stateChangeTimeMs=" + stateChangeTimeMs + ", currentPosMs=" + currentPosMs +
|
||||
", speed=" + speed + ")");
|
||||
|
||||
removeCallbacks(mUpdateSeekBars);
|
||||
// Since the music client may be responding to historical events that cause the
|
||||
// playback state to change dramatically, wait until things become quiescent before
|
||||
// resuming automatic scrub position update.
|
||||
if (mTransientSeek.getVisibility() == View.VISIBLE
|
||||
&& playbackPositionShouldMove(mCurrentPlayState)) {
|
||||
postDelayed(mUpdateSeekBars, QUIESCENT_PLAYBACK_FACTOR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -136,15 +141,21 @@ public class KeyguardTransportControlView extends FrameLayout {
|
||||
}
|
||||
};
|
||||
|
||||
private final Runnable mUpdateSeekBars = new Runnable() {
|
||||
private class UpdateSeekBarRunnable implements Runnable {
|
||||
public void run() {
|
||||
if (updateSeekBars()) {
|
||||
boolean seekAble = updateOnce();
|
||||
if (seekAble) {
|
||||
removeCallbacks(this);
|
||||
postDelayed(this, 1000);
|
||||
}
|
||||
}
|
||||
public boolean updateOnce() {
|
||||
return updateSeekBars();
|
||||
}
|
||||
};
|
||||
|
||||
private final UpdateSeekBarRunnable mUpdateSeekBars = new UpdateSeekBarRunnable();
|
||||
|
||||
private final Runnable mResetToMetadata = new Runnable() {
|
||||
public void run() {
|
||||
resetToMetadata();
|
||||
@@ -163,6 +174,7 @@ public class KeyguardTransportControlView extends FrameLayout {
|
||||
}
|
||||
if (keyCode != -1) {
|
||||
sendMediaButtonClick(keyCode);
|
||||
delayResetToMetadata(); // if the scrub bar is showing, keep showing it.
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -177,25 +189,67 @@ public class KeyguardTransportControlView extends FrameLayout {
|
||||
}
|
||||
};
|
||||
|
||||
// This class is here to throttle scrub position updates to the music client
|
||||
class FutureSeekRunnable implements Runnable {
|
||||
private int mProgress;
|
||||
private boolean mPending;
|
||||
|
||||
public void run() {
|
||||
scrubTo(mProgress);
|
||||
mPending = false;
|
||||
}
|
||||
|
||||
void setProgress(int progress) {
|
||||
mProgress = progress;
|
||||
if (!mPending) {
|
||||
mPending = true;
|
||||
postDelayed(this, 30);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// This is here because RemoteControlClient's method isn't visible :/
|
||||
private final static boolean playbackPositionShouldMove(int playstate) {
|
||||
switch(playstate) {
|
||||
case RemoteControlClient.PLAYSTATE_STOPPED:
|
||||
case RemoteControlClient.PLAYSTATE_PAUSED:
|
||||
case RemoteControlClient.PLAYSTATE_BUFFERING:
|
||||
case RemoteControlClient.PLAYSTATE_ERROR:
|
||||
case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
|
||||
case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
|
||||
return false;
|
||||
case RemoteControlClient.PLAYSTATE_PLAYING:
|
||||
case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
|
||||
case RemoteControlClient.PLAYSTATE_REWINDING:
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private final FutureSeekRunnable mFutureSeekRunnable = new FutureSeekRunnable();
|
||||
|
||||
private final SeekBar.OnSeekBarChangeListener mOnSeekBarChangeListener =
|
||||
new SeekBar.OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
if (fromUser) {
|
||||
scrubTo(progress);
|
||||
mFutureSeekRunnable.setProgress(progress);
|
||||
delayResetToMetadata();
|
||||
mTempDate.setTime(progress);
|
||||
mTransientSeekTimeElapsed.setText(mFormat.format(mTempDate));
|
||||
} else {
|
||||
updateSeekDisplay();
|
||||
}
|
||||
updateSeekDisplay();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
mUserSeeking = true;
|
||||
delayResetToMetadata();
|
||||
removeCallbacks(mUpdateSeekBars); // don't update during user interaction
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
mUserSeeking = false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -247,17 +301,11 @@ public class KeyguardTransportControlView extends FrameLayout {
|
||||
if (enabled == mSeekEnabled) return;
|
||||
|
||||
mSeekEnabled = enabled;
|
||||
if (mTransientSeek.getVisibility() == VISIBLE) {
|
||||
if (mTransientSeek.getVisibility() == VISIBLE && !enabled) {
|
||||
mTransientSeek.setVisibility(INVISIBLE);
|
||||
mMetadataContainer.setVisibility(VISIBLE);
|
||||
mUserSeeking = false;
|
||||
cancelResetToMetadata();
|
||||
}
|
||||
if (enabled) {
|
||||
mUpdateSeekBars.run();
|
||||
} else {
|
||||
removeCallbacks(mUpdateSeekBars);
|
||||
}
|
||||
}
|
||||
|
||||
public void setTransportControlCallback(KeyguardHostView.TransportControlCallback
|
||||
@@ -294,6 +342,8 @@ public class KeyguardTransportControlView extends FrameLayout {
|
||||
}
|
||||
final boolean screenOn = KeyguardUpdateMonitor.getInstance(mContext).isScreenOn();
|
||||
setEnableMarquee(screenOn);
|
||||
// Allow long-press anywhere else in this view to show the seek bar
|
||||
setOnLongClickListener(mTransportShowSeekBarListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -326,7 +376,6 @@ public class KeyguardTransportControlView extends FrameLayout {
|
||||
mAudioManager.unregisterRemoteController(mRemoteController);
|
||||
KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateMonitor);
|
||||
mMetadata.clear();
|
||||
mUserSeeking = false;
|
||||
removeCallbacks(mUpdateSeekBars);
|
||||
}
|
||||
|
||||
@@ -484,18 +533,12 @@ public class KeyguardTransportControlView extends FrameLayout {
|
||||
|
||||
void updateSeekDisplay() {
|
||||
if (mMetadata != null && mRemoteController != null && mFormat != null) {
|
||||
if (mTimeElapsed == null) {
|
||||
mTimeElapsed = new Date();
|
||||
}
|
||||
if (mTrackDuration == null) {
|
||||
mTrackDuration = new Date();
|
||||
}
|
||||
mTimeElapsed.setTime(mRemoteController.getEstimatedMediaPosition());
|
||||
mTrackDuration.setTime(mMetadata.duration);
|
||||
mTransientSeekTimeElapsed.setText(mFormat.format(mTimeElapsed));
|
||||
mTransientSeekTimeTotal.setText(mFormat.format(mTrackDuration));
|
||||
mTempDate.setTime(mRemoteController.getEstimatedMediaPosition());
|
||||
mTransientSeekTimeElapsed.setText(mFormat.format(mTempDate));
|
||||
mTempDate.setTime(mMetadata.duration);
|
||||
mTransientSeekTimeTotal.setText(mFormat.format(mTempDate));
|
||||
|
||||
if (DEBUG) Log.d(TAG, "updateSeekDisplay timeElapsed=" + mTimeElapsed +
|
||||
if (DEBUG) Log.d(TAG, "updateSeekDisplay timeElapsed=" + mTempDate +
|
||||
" duration=" + mMetadata.duration);
|
||||
}
|
||||
}
|
||||
@@ -508,10 +551,16 @@ public class KeyguardTransportControlView extends FrameLayout {
|
||||
mTransientSeek.setVisibility(INVISIBLE);
|
||||
mMetadataContainer.setVisibility(VISIBLE);
|
||||
cancelResetToMetadata();
|
||||
removeCallbacks(mUpdateSeekBars); // don't update if scrubber isn't visible
|
||||
} else {
|
||||
mTransientSeek.setVisibility(VISIBLE);
|
||||
mMetadataContainer.setVisibility(INVISIBLE);
|
||||
delayResetToMetadata();
|
||||
if (playbackPositionShouldMove(mCurrentPlayState)) {
|
||||
mUpdateSeekBars.run();
|
||||
} else {
|
||||
mUpdateSeekBars.updateOnce();
|
||||
}
|
||||
}
|
||||
mTransportControlCallback.userActivity();
|
||||
return true;
|
||||
@@ -573,9 +622,6 @@ public class KeyguardTransportControlView extends FrameLayout {
|
||||
case RemoteControlClient.PLAYSTATE_PLAYING:
|
||||
imageResId = R.drawable.ic_media_pause;
|
||||
imageDescId = R.string.keyguard_transport_pause_description;
|
||||
if (mSeekEnabled) {
|
||||
mUpdateSeekBars.run();
|
||||
}
|
||||
break;
|
||||
|
||||
case RemoteControlClient.PLAYSTATE_BUFFERING:
|
||||
@@ -590,10 +636,9 @@ public class KeyguardTransportControlView extends FrameLayout {
|
||||
break;
|
||||
}
|
||||
|
||||
if (state != RemoteControlClient.PLAYSTATE_PLAYING) {
|
||||
removeCallbacks(mUpdateSeekBars);
|
||||
updateSeekBars();
|
||||
}
|
||||
boolean clientSupportsSeek = mMetadata != null && mMetadata.duration > 0;
|
||||
setSeekBarsEnabled(clientSupportsSeek);
|
||||
|
||||
mBtnPlay.setImageResource(imageResId);
|
||||
mBtnPlay.setContentDescription(getResources().getString(imageDescId));
|
||||
mCurrentPlayState = state;
|
||||
@@ -601,11 +646,9 @@ public class KeyguardTransportControlView extends FrameLayout {
|
||||
|
||||
boolean updateSeekBars() {
|
||||
final int position = (int) mRemoteController.getEstimatedMediaPosition();
|
||||
if (DEBUG) Log.v(TAG, "Estimated time:" + position);
|
||||
if (position >= 0) {
|
||||
if (DEBUG) Log.v(TAG, "Seek to " + position);
|
||||
if (!mUserSeeking) {
|
||||
mTransientSeekBar.setProgress(position);
|
||||
}
|
||||
mTransientSeekBar.setProgress(position);
|
||||
return true;
|
||||
}
|
||||
Log.w(TAG, "Updating seek bars; received invalid estimated media position (" +
|
||||
@@ -671,34 +714,4 @@ public class KeyguardTransportControlView extends FrameLayout {
|
||||
public boolean providesClock() {
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean wasPlayingRecently(int state, long stateChangeTimeMs) {
|
||||
switch (state) {
|
||||
case RemoteControlClient.PLAYSTATE_PLAYING:
|
||||
case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
|
||||
case RemoteControlClient.PLAYSTATE_REWINDING:
|
||||
case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
|
||||
case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
|
||||
case RemoteControlClient.PLAYSTATE_BUFFERING:
|
||||
// actively playing or about to play
|
||||
return true;
|
||||
case RemoteControlClient.PLAYSTATE_NONE:
|
||||
return false;
|
||||
case RemoteControlClient.PLAYSTATE_STOPPED:
|
||||
case RemoteControlClient.PLAYSTATE_PAUSED:
|
||||
case RemoteControlClient.PLAYSTATE_ERROR:
|
||||
// we have stopped playing, check how long ago
|
||||
if (DEBUG) {
|
||||
if ((SystemClock.elapsedRealtime() - stateChangeTimeMs) < DISPLAY_TIMEOUT_MS) {
|
||||
Log.v(TAG, "wasPlayingRecently: time < TIMEOUT was playing recently");
|
||||
} else {
|
||||
Log.v(TAG, "wasPlayingRecently: time > TIMEOUT");
|
||||
}
|
||||
}
|
||||
return ((SystemClock.elapsedRealtime() - stateChangeTimeMs) < DISPLAY_TIMEOUT_MS);
|
||||
default:
|
||||
Log.e(TAG, "Unknown playback state " + state + " in wasPlayingRecently()");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user