package fr.tvbarthel.cheerleader.sampleapp.ui;
import android.content.Context;
import android.graphics.PorterDuff;
import android.text.Html;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;
import com.squareup.picasso.Picasso;
import fr.tvbarthel.cheerleader.library.client.SoundCloudTrack;
import fr.tvbarthel.cheerleader.library.helpers.SoundCloudArtworkHelper;
import fr.tvbarthel.cheerleader.library.player.CheerleaderPlayer;
import fr.tvbarthel.cheerleader.library.player.CheerleaderPlayerListener;
import fr.tvbarthel.cheerleader.sampleapp.R;
/**
* Simple view used to display basic player button : play/pause, next and previous.
*/
public class PlaybackView extends FrameLayout implements View.OnClickListener,
CheerleaderPlayerListener, SeekBar.OnSeekBarChangeListener {
private ImageView mArtwork;
private TextView mTitle;
private TextView mCurrentTime;
private TextView mDuration;
private ImageView mPlayPause;
private SeekBar mSeekBar;
private ProgressBarCompat mLoader;
private boolean mSeeking;
/**
* Dummy listener.
*/
private static Listener sDummyListener = new Listener() {
@Override
public void onTogglePlayPressed() {
}
@Override
public void onPreviousPressed() {
}
@Override
public void onNextPressed() {
}
@Override
public void onSeekToRequested(int milli) {
}
};
/**
* Current listener object.
*/
private Listener mListener = sDummyListener;
/**
* Simple view used to display basic player button : play/pause, next and previous.
*
* @param context calling context.
*/
public PlaybackView(Context context) {
super(context);
if (!isInEditMode()) {
init(context);
}
}
/**
* Simple view used to display basic player button : play/pause, next and previous.
*
* @param context calling context.
* @param attrs attr from xml.
*/
public PlaybackView(Context context, AttributeSet attrs) {
super(context, attrs);
if (!isInEditMode()) {
init(context);
}
}
/**
* Simple view used to display basic player button : play/pause, next and previous.
*
* @param context calling context.
* @param attrs attr from xml.
* @param defStyleAttr style from xml.
*/
public PlaybackView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
if (!isInEditMode()) {
init(context);
}
}
/**
* Allow to catch event performed on the view.
*
* @param listener listener.
*/
public void setListener(Listener listener) {
if (listener == null) {
mListener = sDummyListener;
} else {
mListener = listener;
}
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.playback_view_next:
mListener.onNextPressed();
break;
case R.id.playback_view_previous:
mListener.onPreviousPressed();
break;
case R.id.playback_view_toggle_play:
mListener.onTogglePlayPressed();
break;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////// Player callback ///////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
@Override
public void onPlayerPlay(SoundCloudTrack track, int position) {
setTrack(track);
}
@Override
public void onPlayerPause() {
mPlayPause.setImageResource(R.drawable.ic_play_white);
if (mPlayPause.getVisibility() == INVISIBLE) {
mLoader.setVisibility(INVISIBLE);
mPlayPause.setVisibility(VISIBLE);
}
}
@Override
public void onPlayerSeekTo(int milli) {
mSeekBar.setProgress(milli);
}
@Override
public void onPlayerDestroyed() {
mPlayPause.setImageResource(R.drawable.ic_play_white);
}
@Override
public void onBufferingStarted() {
mLoader.setVisibility(VISIBLE);
mPlayPause.setVisibility(INVISIBLE);
}
@Override
public void onBufferingEnded() {
mLoader.setVisibility(INVISIBLE);
mPlayPause.setVisibility(VISIBLE);
}
@Override
public void onProgressChanged(int milli) {
if (!mSeeking) {
mSeekBar.setProgress(milli);
int[] secondMinute = getSecondMinutes(milli);
String duration = String.format(getResources().getString(R.string.playback_view_time), secondMinute[0], secondMinute[1]);
mCurrentTime.setText(duration);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////// SeekBar listener /////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
int[] secondMinute = getSecondMinutes(progress);
mCurrentTime.setText(
String.format(getResources().getString(R.string.playback_view_time),
secondMinute[0], secondMinute[1])
);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
mSeeking = true;
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
mSeeking = false;
mListener.onSeekToRequested(seekBar.getProgress());
}
/**
* Synchronize the player view with the current player state.
* <p/>
* Basically, check if a track is loaded as well as the playing state.
*
* @param player player currently used.
*/
public void synchronize(CheerleaderPlayer player) {
setTrack(player.getCurrentTrack());
mLoader.setVisibility(INVISIBLE);
mPlayPause.setVisibility(VISIBLE);
setPlaying(player.isPlaying());
}
/**
* Set the current played track.
*
* @param track track which is played.
*/
private void setTrack(SoundCloudTrack track) {
if (track == null) {
mTitle.setText("");
mArtwork.setImageDrawable(null);
mPlayPause.setImageResource(R.drawable.ic_play_white);
mSeekBar.setProgress(0);
String none = String.format(getResources().getString(R.string.playback_view_time), 0, 0);
mCurrentTime.setText(none);
mDuration.setText(none);
} else {
Picasso.with(getContext())
.load(SoundCloudArtworkHelper.getArtworkUrl(track, SoundCloudArtworkHelper.XLARGE))
.fit()
.centerCrop()
.placeholder(R.color.grey)
.into(mArtwork);
mTitle.setText(Html.fromHtml(String.format(getResources().getString(R.string.playback_view_title),
track.getArtist(), track.getTitle())));
mPlayPause.setImageResource(R.drawable.ic_pause_white);
if (getTranslationY() != 0) {
this.animate().translationY(0);
}
mSeekBar.setMax(((int) track.getDurationInMilli()));
String none = String.format(getResources().getString(R.string.playback_view_time), 0, 0);
int[] secondMinute = getSecondMinutes(track.getDurationInMilli());
String duration = String.format(getResources().getString(R.string.playback_view_time),
secondMinute[0], secondMinute[1]);
mCurrentTime.setText(none);
mDuration.setText(duration);
}
}
/**
* Used to update the play/pause button.
* <p/>
* Should be synchronize with the player playing state.
* See also : {@link CheerleaderPlayer#isPlaying()}.
*
* @param isPlaying true if a track is currently played.
*/
private void setPlaying(boolean isPlaying) {
if (isPlaying) {
mPlayPause.setImageResource(R.drawable.ic_pause_white);
} else {
mPlayPause.setImageResource(R.drawable.ic_play_white);
}
}
private void init(Context context) {
LayoutInflater.from(context).inflate(R.layout.playback_view, this);
findViewById(R.id.playback_view_next).setOnClickListener(this);
findViewById(R.id.playback_view_previous).setOnClickListener(this);
mCurrentTime = ((TextView) findViewById(R.id.playback_view_current_time));
mDuration = ((TextView) findViewById(R.id.playback_view_duration));
mPlayPause = ((ImageView) findViewById(R.id.playback_view_toggle_play));
mLoader = ((ProgressBarCompat) findViewById(R.id.playback_view_loader));
mPlayPause.setOnClickListener(this);
mArtwork = ((ImageView) findViewById(R.id.playback_view_artwork));
mArtwork.setColorFilter(
getResources().getColor(R.color.playback_view_track_artwork_filter),
PorterDuff.Mode.SRC_ATOP
);
mTitle = ((TextView) findViewById(R.id.playback_view_track));
mSeekBar = ((SeekBar) findViewById(R.id.playback_view_seekbar));
mSeekBar.setOnSeekBarChangeListener(this);
mSeeking = false;
}
private int[] getSecondMinutes(long milli) {
int inSeconds = (int) milli / 1000;
return new int[]{inSeconds / 60, inSeconds % 60};
}
/**
* Interface used to catch player event.
*/
public interface Listener {
/**
* Called when user pressed the toggle play/pause button.
*/
void onTogglePlayPressed();
/**
* Called when user pressed the previous button.
*/
void onPreviousPressed();
/**
* Called when user pressed the next button.
*/
void onNextPressed();
/**
* Called when user touch the seek bar to request a seek to action.
*
* @param milli milli second to which the player should seek to.
*/
void onSeekToRequested(int milli);
}
}