blob: 71de9cae472f75deb1575945964b255fd5e0432f [file] [log] [blame]
/*
* Copyright 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.mediasession.ui;
import android.animation.ValueAnimator;
import android.content.Context;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.MediaControllerCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;
import android.support.v7.widget.AppCompatSeekBar;
import android.util.AttributeSet;
import android.view.animation.LinearInterpolator;
import android.widget.SeekBar;
/**
* SeekBar that can be used with a {@link MediaSessionCompat} to track and seek in playing
* media.
*/
public class MediaSeekBar extends AppCompatSeekBar {
private MediaControllerCompat mMediaController;
private ControllerCallback mControllerCallback;
private boolean mIsTracking = false;
private OnSeekBarChangeListener mOnSeekBarChangeListener = new OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
mIsTracking = true;
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
mMediaController.getTransportControls().seekTo(getProgress());
mIsTracking = false;
}
};
private ValueAnimator mProgressAnimator;
public MediaSeekBar(Context context) {
super(context);
super.setOnSeekBarChangeListener(mOnSeekBarChangeListener);
}
public MediaSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
super.setOnSeekBarChangeListener(mOnSeekBarChangeListener);
}
public MediaSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
super.setOnSeekBarChangeListener(mOnSeekBarChangeListener);
}
@Override
public void setOnSeekBarChangeListener(OnSeekBarChangeListener l) {
// Prohibit adding seek listeners to this subclass.
}
public void setMediaController(final MediaControllerCompat mediaController) {
if (mediaController != null) {
mControllerCallback = new ControllerCallback();
mediaController.registerCallback(mControllerCallback);
} else if (mMediaController != null) {
mMediaController.unregisterCallback(mControllerCallback);
mControllerCallback = null;
}
mMediaController = mediaController;
}
public void disconnectController() {
if (mMediaController != null) {
mMediaController.unregisterCallback(mControllerCallback);
mControllerCallback = null;
mMediaController = null;
}
}
private class ControllerCallback
extends MediaControllerCompat.Callback
implements ValueAnimator.AnimatorUpdateListener {
@Override
public void onSessionDestroyed() {
super.onSessionDestroyed();
}
@Override
public void onPlaybackStateChanged(PlaybackStateCompat state) {
super.onPlaybackStateChanged(state);
// If there's an ongoing animation, stop it now.
if (mProgressAnimator != null) {
mProgressAnimator.cancel();
mProgressAnimator = null;
}
final int progress = state != null
? (int) state.getPosition()
: 0;
setProgress(progress);
// If the media is playing then the seekbar should follow it, and the easiest
// way to do that is to create a ValueAnimator to update it so the bar reaches
// the end of the media the same time as playback gets there (or close enough).
if (state != null && state.getState() == PlaybackStateCompat.STATE_PLAYING) {
final int timeToEnd = (int) ((getMax() - progress) / state.getPlaybackSpeed());
mProgressAnimator = ValueAnimator.ofInt(progress, getMax())
.setDuration(timeToEnd);
mProgressAnimator.setInterpolator(new LinearInterpolator());
mProgressAnimator.addUpdateListener(this);
mProgressAnimator.start();
}
}
@Override
public void onMetadataChanged(MediaMetadataCompat metadata) {
super.onMetadataChanged(metadata);
final int max = metadata != null
? (int) metadata.getLong(MediaMetadataCompat.METADATA_KEY_DURATION)
: 0;
setProgress(0);
setMax(max);
}
@Override
public void onAnimationUpdate(final ValueAnimator valueAnimator) {
// If the user is changing the slider, cancel the animation.
if (mIsTracking) {
valueAnimator.cancel();
return;
}
final int animatedIntValue = (int) valueAnimator.getAnimatedValue();
setProgress(animatedIntValue);
}
}
}