Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit eaacc7c1 authored by Jim Miller's avatar Jim Miller Committed by Android Git Automerger
Browse files

am bb78ae9e: am 8cba0d0b: Merge "Fix scrubbing behavior on keyguard music transport" into klp-dev

* commit 'bb78ae9e':
  Fix scrubbing behavior on keyguard music transport
parents 25f6d3e6 bb78ae9e
Loading
Loading
Loading
Loading
+85 −72
Original line number Diff line number Diff line
@@ -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();
            }
        }

        @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);
            }
            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;
        }
    }
}