I want to play a radio channel from url by using SimpleExoPlayer, the code works only for the first time, i.e., when I press play it works, then I press again it stops. But if I press again play button it doesn't play.
I tried to delete cache in every stop event but it also didn't solve the problem.
public class MainActivity extends AppCompatActivity {
private String source;
private SimpleExoPlayer simpleExoPlayer;
private boolean isPlaying;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ImageButton btnPlay = findViewById(R.id.image_play);
source = "http://mp3stream7.apasf.apa.at/stream/1/";
isPlaying = false;
btnPlay.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (!isPlaying) {
setupPlayer();
simpleExoPlayer.setPlayWhenReady(true);
isPlaying = true;
} else {
simpleExoPlayer.stop();
simpleExoPlayer.setPlayWhenReady(false);
isPlaying = false;
}
}
});
}
private void setupPlayer() {
DefaultRenderersFactory renderersFactory = new DefaultRenderersFactory(MainActivity.this, null,
DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF);
TrackSelector trackSelector = new DefaultTrackSelector();
simpleExoPlayer = ExoPlayerFactory.newSimpleInstance(renderersFactory, trackSelector);
String userAgent = Util.getUserAgent(MainActivity.this, "Play Audio");
ExtractorMediaSource mediaSource = new ExtractorMediaSource(Uri.parse(source),
new CacheDataSourceFactory(MainActivity.this, 100 * 1024 * 1024, 5 * 1024 * 1024),
new DefaultExtractorsFactory(), null, null);
simpleExoPlayer.prepare(mediaSource);
}
private void release_player() {
simpleExoPlayer.release();
}
#Override
protected void onDestroy() {
simpleExoPlayer.release();
super.onDestroy();
}
#Override
protected void onPause() {
simpleExoPlayer.release();
super.onPause();
}
}
try this:
public class MainActivity extends AppCompatActivity {
private String source;
private SimpleExoPlayer simpleExoPlaye = null;
private boolean isPlaying;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ImageButton btnPlay = findViewById(R.id.image_play);
source = "http://mp3stream7.apasf.apa.at/stream/1/";
isPlaying = false;
//add this
setupPlayer();
btnPlay.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (!isPlaying) {
//remove setupPlayer(); from here
simpleExoPlayer.setPlayWhenReady(true);
isPlaying = true;
} else {
//remove simpleExoPlayer.stop(); from here
simpleExoPlayer.setPlayWhenReady(false);
isPlaying = false;
}
}
});
}
#Override
public void onResume() {
super.onResume();
if (simpleExoPlayer != null) {
simpleExoPlayer.setPlayWhenReady(isPlaying);
} else {
setupPlayer();
}
}
private void setupPlayer() {
if (simpleExoPlayer != null) {
simpleExoPlayer.stop();
}
DefaultRenderersFactory renderersFactory = new DefaultRenderersFactory(MainActivity.this, null,
DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF);
TrackSelector trackSelector = new DefaultTrackSelector();
simpleExoPlayer = ExoPlayerFactory.newSimpleInstance(renderersFactory, trackSelector);
String userAgent = Util.getUserAgent(MainActivity.this, "Play Audio");
ExtractorMediaSource mediaSource = new ExtractorMediaSource(Uri.parse(source),
new CacheDataSourceFactory(MainActivity.this, 100 * 1024 * 1024, 5 * 1024 * 1024),
new DefaultExtractorsFactory(), null, null);
simpleExoPlayer.prepare(mediaSource);
}
#Override
protected void onDestroy() {
//remove releaze from onDestroy and onPause
simpleExoPlayer.stop();
super.onDestroy();
}
How do I change the playback state of ExoPlayer when the Broadcast receiver is triggered?
I have a BroadcastReceiver that gets triggered when the headset is disconnected however I am not sure of the correct way to handle the pausing of audio when onReceive() is called. Do I need to change the playback state of the Mediasession here?
private static final int CORRECT_ANSWER_DELAY_MILLIS = 1000;
public static final String TAG = QuizActivity.class.getSimpleName();
private SimpleExoPlayer mExoPlayer;
private PlayerView mPlayerView;
private PlayerListener mPlayerListener;
private PlaybackStateCompat.Builder mPlaybackStateBuilder;
public static MediaSessionCompat mMediaSession;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_quiz);
initializeMediaSession();
mPlayerListener = new PlayerListener();
// Initialize the player view.
mPlayerView = (PlayerView) findViewById(R.id.playerView);
// Initialize the player.
initializePlayer(Uri.parse(answerSample.getUri()));
}
/**
* Initialize ExoPlayer.
*
* #param mediaUri The URI of the sample to play.
*/
// done (2): Set the ExoPlayer.EventListener to this activity
private void initializePlayer(Uri mediaUri) {
// 1. Create a default TrackSelector
if (mExoPlayer == null) {
Handler mainHandler = new Handler();
BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
TrackSelection.Factory videoTrackSelectionFactory =
new AdaptiveTrackSelection.Factory(bandwidthMeter);
DefaultTrackSelector trackSelector =
new DefaultTrackSelector(videoTrackSelectionFactory);
// 2. Create the player
mExoPlayer =
ExoPlayerFactory.newSimpleInstance(this, trackSelector);
mExoPlayer.addListener(mPlayerListener);
mPlayerView.setPlayer(mExoPlayer);
DefaultBandwidthMeter defaultBandwidthMeter = new DefaultBandwidthMeter();
// Produces DataSource instances through which media data is loaded.
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(this,
Util.getUserAgent(this, "yourApplicationName"), defaultBandwidthMeter);
// This is the MediaSource representing the media to be played.
MediaSource videoSource = new ExtractorMediaSource.Factory(dataSourceFactory)
.createMediaSource(mediaUri);
// Prepare the player with the source.
mExoPlayer.prepare(videoSource);
mExoPlayer.setPlayWhenReady(true);
}
}
/**
* Release ExoPlayer.
*/
private void releasePlayer() {
mExoPlayer.stop();
mExoPlayer.release();
mExoPlayer = null;
}
* toggles the UI to show the correct answer.
*
* #param v The button that was clicked.
*/
#Override
public void onClick(View v) {
// Wait some time so the user can see the correct answer, then go to the next question.
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
mExoPlayer.stop();
Intent nextQuestionIntent = new Intent(QuizActivity.this, QuizActivity.class);
nextQuestionIntent.putExtra(REMAINING_SONGS_KEY, mRemainingSampleIDs);
finish();
startActivity(nextQuestionIntent);
}
}, CORRECT_ANSWER_DELAY_MILLIS);
}
/**
* Release the player when the activity is destroyed.
*/
#Override
protected void onDestroy() {
super.onDestroy();
releasePlayer();
mMediaSession.setActive(false);
}
private void initializeMediaSession() {
mMediaSession = new MediaSessionCompat(this, TAG);
mMediaSession.setFlags(
MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS |
MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
// Do not let MediaButtons restart the player when the app is not visible.
mMediaSession.setMediaButtonReceiver(null);
mPlaybackStateBuilder = new PlaybackStateCompat.Builder()
// Set an initial PlaybackState with ACTION_PLAY, so media buttons can start the player.
.setActions(PlaybackStateCompat.ACTION_PLAY |
PlaybackStateCompat.ACTION_PAUSE |
PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS |
PlaybackStateCompat.ACTION_PLAY_PAUSE);
mMediaSession.setPlaybackState(mPlaybackStateBuilder.build());
mMediaSession.setCallback(new MediaSessionCompat.Callback() {
#Override
public void onPlay() {
mExoPlayer.setPlayWhenReady(true);
}
#Override
public void onPause() {
mExoPlayer.setPlayWhenReady(false);
}
});
// Start the Media Session since the activity is active.
mMediaSession.setActive(true);
}
public class PlayerListener extends Player.DefaultEventListener {
#Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
//super.onPlayerStateChanged(playWhenReady, playbackState);
if (playbackState == Player.STATE_READY && playWhenReady) {
mPlaybackStateBuilder.setState(STATE_PLAYING, mExoPlayer.getCurrentPosition(), 1f);
} else if (playbackState == Player.STATE_READY) {
mPlaybackStateBuilder.setState(STATE_PAUSED, mExoPlayer.getCurrentPosition(), 1f);
}
mMediaSession.setPlaybackState(mPlaybackStateBuilder.build());
showMediaStyleNotification(mPlaybackStateBuilder.build());
}
}
public static class MediaReceiver extends BroadcastReceiver {
public MediaReceiver() { }
#Override
public void onReceive(final Context context, Intent intent) {
if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction())) {
Toast.makeText(context, "Headset unplugged", Toast.LENGTH_SHORT).show();
}
}
}
You should register the Receiver first in the Activity like -
MediaReceiver mr = new MediaReceiver(mPlayerView);
registerReceiver(mr);
then,
private class MediaReceiver extends BroadcastReceiver {
private PlayerView pv;
public MediaReceiver(PlayerView playerView) {
this.pv = playerView;
}
#Override
public void onReceive(final Context context, Intent intent) {
if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction())) {
Toast.makeText(context, "Headset unplugged", Toast.LENGTH_SHORT).show();
pv.pause();
}
}
also, don't forget to unregister Receiver in onDestroy() or onPause()
Exoplayer 2.11.0 supports setHandleAudioBecomingNoisy:
SimpleExoPlayer.Builder(this).setHandleAudioBecomingNoisy(true).build()
In my app, I am using media player class. I want to update the progress of playing audio file on seek bar and for this I am using below code for playing media file
public class AudioPlayer implements MediaPlayer.OnCompletionListener {
private final static int SAMPLE_RATE = 16000;
private final int DELAY_FOR_HANDLER = 1_000;
static final String LOG_TAG = AudioPlayer.class.getSimpleName();
private Context mContext;
private MediaPlayer mPlayer;
private AudioTrack mProgressTone;
private MediaPlayerStatusCallbacks mMediaPlayerStatusCallBacks;
private Handler mHandler;
private PlayerUpdater mPlayerUpdater;
private SeekBar mSeekBar;
public AudioPlayer(SeekBar seekBar, Context context, MediaPlayerStatusCallbacks mediaPlayerStatusCallbacks) {
mPlayer = new MediaPlayer();
mPlayer.setOnCompletionListener(this);
mMediaPlayerStatusCallBacks = mediaPlayerStatusCallbacks;
mHandler = new Handler();
mContext = context;
mSeekBar = seekBar;
}
private void startSendingProgress() {
mPlayerUpdater = new PlayerUpdater();
((MessagingActivity) mContext).runOnUiThread(mPlayerUpdater);
}
private class PlayerUpdater implements Runnable {
#Override
public void run() {
if(mPlayer != null){
int currentProgress = mPlayer.getCurrentPosition() / 1000;
mSeekBar.setProgress(currentProgress);
}
mHandler.postDelayed(this, DELAY_FOR_HANDLER);
}
}
public static String getMediaDuration(String path) {
MediaMetadataRetriever mmr = new MediaMetadataRetriever();
mmr.setDataSource(path);
String duration = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
mmr.release();
return duration;
}
public void playAudio(String path) throws IOException {
mPlayer.reset();
mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mPlayer.setDataSource(path);
mPlayer.prepare();
mSeekBar.setMax(mPlayer.getDuration());
mPlayer.start();
startSendingProgress();
}
public void stopAudio() {
if (mPlayer != null) {
mPlayer.stop();
}
}
public MediaPlayer getMediaPlayer() {
return mPlayer;
}
#Override
public void onCompletion(MediaPlayer mp) {
mMediaPlayerStatusCallBacks.onMediaCompleted();
mHandler.removeCallbacks(mPlayerUpdater);
}
}
And below is the code I am using in View Holder of my recycler view
public class AudioViewHolder extends RecyclerView.ViewHolder {
public View mView;
public QuickContactBadge qcbProfileImage;
public ImageView ivPlay, ivDeliveryStatus;
public SeekBar sbAudioPlayer;
public TextView tvDateTime, tvDuration;
public VoipMessage mItem;
public MyAudioViewsClickListener myAudioViewsClickListener;
public AudioViewHolder(View itemView) {
super(itemView);
mView = itemView;
qcbProfileImage = (QuickContactBadge) itemView.findViewById(R.id.img_view_user);
ivPlay = (ImageView) itemView.findViewById(R.id.iv_play);
sbAudioPlayer = (SeekBar) itemView.findViewById(R.id.sb_audio_player);
ivDeliveryStatus = (ImageView) itemView.findViewById(R.id.img_view_message_status);
tvDateTime = (TextView) itemView.findViewById(R.id.txt_view_duration);
tvDuration = (TextView) itemView.findViewById(R.id.txt_view_time);
myAudioViewsClickListener = new MyAudioViewsClickListener();
ivPlay.setOnClickListener(myAudioViewsClickListener);
}
private class MyAudioViewsClickListener implements View.OnClickListener {
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.iv_play:
try {
playAndPauseAudio(getAdapterPosition());
} catch (IOException e) {
e.printStackTrace();
}
break;
}
}
}
private void playAndPauseAudio(int adapterPosition) throws IOException {
VoipMessage currentMessage = mMessages.get(adapterPosition);
RecyclerView.ViewHolder holder = mRecyclerView.findViewHolderForLayoutPosition(adapterPosition);
final AudioViewHolder audioViewHolder = (AudioViewHolder) holder;
String audioPath = currentMessage.getMediaReference();
AudioPlayer audioPlayer = new AudioPlayer(audioViewHolder.sbAudioPlayer, mContext, new AudioPlayer.MediaPlayerStatusCallbacks() {
#Override
public void onMediaCompleted() {
ivPlay.setImageResource(R.drawable.ic_audio_play);
}
#Override
public void onUpdatedMediaProgress(int progress) {
//audioViewHolder.sbAudioPlayer.setProgress(progress);
Logger.e("test","progress in adapter " + progress);
//sbAudioPlayer.setProgress(progress);
}
#Override
public void setDuration(int duration) {
//audioViewHolder.sbAudioPlayer.setMax(duration);
//sbAudioPlayer.setMax(duration);
}
});
if (audioPlayer.getMediaPlayer().isPlaying()) {
audioPlayer.stopAudio();
ivPlay.setImageResource(R.drawable.ic_audio_play);
} else {
audioPlayer.playAudio(audioPath);
ivPlay.setImageResource(R.drawable.ic_audio_pause);
}
}
}
I am passing seek bar reference from ViewHolder to the Audio Player class. I am using thread for updating seek bar. Thread is calculating progress and setting that progress to seek bar but the problem is seek bar is not updating on UI. For clear understanding I am attaching image of UI.
enter image description here
My my seek bar is not updating any help. I have spent many hours but could not solve this problem.
Try mSeekBar.setMax(mPlayer.getDuration() / 1000); instead of mSeekBar.setMax(mPlayer.getDuration());, because You set current process in seconds:
int currentProgress = mPlayer.getCurrentPosition() / 1000;
mSeekBar.setProgress(currentProgress);
but max value is in milliseconds and progress is less than 1 px.
How can I monitor progress changes on ExoPlayer?
I tried to implement a hidden MediaController and overriding setOnSeekBarChangeListener methods, but for now without success. I'm wondering if there is another way to listen to the ExoPlayer progress.
I know this question is very old. But, I landed on this while implementing ExoPlayer. This is to help the others who do the same later on:)
So, I have followed the following methods to track progress of the playback. This is the way it is done in the ExoPlayer Google Docs. It works as needed.
Checkout PlayerControlView.java in Google ExoPlayer repository
updateProgressBar() is the function to update the SeekBar progress:
private void updateProgressBar() {
long duration = player == null ? 0 : player.getDuration();
long position = player == null ? 0 : player.getCurrentPosition();
if (!dragging) {
mSeekBar.setProgress(progressBarValue(position));
}
long bufferedPosition = player == null ? 0 : player.getBufferedPosition();
mSeekBar.setSecondaryProgress(progressBarValue(bufferedPosition));
// Remove scheduled updates.
handler.removeCallbacks(updateProgressAction);
// Schedule an update if necessary.
int playbackState = player == null ? Player.STATE_IDLE : player.getPlaybackState();
if (playbackState != Player.STATE_IDLE && playbackState != Player.STATE_ENDED) {
long delayMs;
if (player.getPlayWhenReady() && playbackState == Player.STATE_READY) {
delayMs = 1000 - (position % 1000);
if (delayMs < 200) {
delayMs += 1000;
}
} else {
delayMs = 1000;
}
handler.postDelayed(updateProgressAction, delayMs);
}
}
private final Runnable updateProgressAction = new Runnable() {
#Override
public void run() {
updateProgressBar();
}
};
We call updateProgressBar() within updateProgressAction repeatedly until the playback stops.
The function is called the first time whenever there is a state change. We use removeCallbacks(Runnable runnable) so that there is always one updateProgressAction to care about.
#Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
updateProgressBar();
}
Hope this helps!
Just try this, its working for me :
handler = new Handler();
runnable = new Runnable() {
#Override
public void run() {
progressbar.setProgress((int) ((exoPlayer.getCurrentPosition()*100)/exoPlayer.getDuration()));
handler.postDelayed(runnable, 1000);
}
};
handler.postDelayed(runnable, 0);
Here,
getCurrentPosition() : return The current playback position in milliseconds.
getDuration(): The duration of the track in millisecond.
I've found a pretty elegant solution using RxJava. This involves a polling pattern as well, but we make sure to use an interval to poll every 1 second.
public Observable<Long> playbackProgressObservable =
Observable.interval(1, TimeUnit.SECONDS, AndroidSchedulers.mainThread())
The logic here is we create an Observable that will emit a sequential number every second. We then use the map operator to transform the number into the current playback position.
public Observable<Long> playbackProgressObservable =
Observable.interval(1, TimeUnit.SECONDS)
.map( { exoPlayer.getCurrentPosition() } );
To finally hooked this together, just call subscribe, ad the progress updates will be emitted every second:
playbackProgressObservable.subscribe( { progress -> // Update logic here } )
Note: Observable.interval runs on a default Scheduler of Schedulers.computation(). Therefore, you'll probably need to add an observeOn() operator to make sure the results are sent to the right thread.
playbackProgressObservable
.observeOn(AndroidSchedulers.mainThread())
.subscribe(progress -> {}) // Update logic here
The above statement will give you a Disposable which must be disposed when you are done observing.
You can do something like this ->
private var playbackDisposable: Disposable? = null
playbackDisposable = playbackProgressObservable
.observeOn(AndroidSchedulers.mainThead())
.subscribe(progress -> {}) // Update logic here
then to dispose the resource ->
playbackDisposable?.dispose()
Well, I did this through kotlin flow..
private fun audioProgress(exoPlayer: SimpleExoPlayer?) = flow<Int> {
while (true) {
emit(((exoPlayer?.currentPosition?.toFloat()?.div(exoPlayer.duration.toFloat())?.times(100))?.toInt()!!))
delay(1000)
}
}.flowOn(Dispatchers.IO)
then collect the progress like this...
val audioProgressJob = launch {
audioProgress(exoPlayer).collect {
MP_progress_bar.progress = it
}
}
Not sure it is the best way, but I achieved this by overloading the TrackRenderer.
I'm using videoPlayer.getBufferedPercentage(), but you might be able to compute the percentage yourself as well, by just using TrackRenderer's getBufferedPositionUs() and getDurationUs()
public interface ProgressListener {
public void onProgressChange(long progress);
}
public class CustomVideoRenderer extends MediaCodecVideoTrackRenderer {
long progress = 0;
private final CopyOnWriteArraySet<ProgressListener> progressListeners = new CopyOnWriteArraySet();
// [...]
// Skipped constructors
// [...]
public void doSomeWork(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException {
super.doSomeWork(positionUs, elapsedRealtimeUs);
long tmpProgress = videoPlayer.getBufferedPercentage();
if (tmpProgress != this.progress) {
this.progress = tmpProgress;
for (ProgressListener progressListener : this.progressListeners) {
progressListener.onProgressChange(progress);
}
}
}
public void addProgressListener(ProgressListener listener) {
this.progressListeners.add(listener);
}
}
To make it clear,there isn't a build in EventListener for the progress event, but you can call Handler.postDelayed inside you updateProgress() function to get the current progress
private void updateProgress(){
//get current progress
long position = player == null ? 0 : player.getCurrentPosition();
//updateProgress() will be called repeatedly, you can check
//player state to end it
handler.postDelayed(updateProgressAction,1000)
}
private final Runnable updateProgressAction = new Runnable() {
#Override
public void run() {
updateProgress();
}
};
for more details, see the source of PlaybackControlView.java inside Exoplayer
I'm not sure if it's the right approach, but I used EventBus and TimerTask to update the progress of the audio being played.
In my MusicController class I put:
private void sendElapsedDuration() {
//To send the current elapsed time
final Timer t = new Timer();
Runnable r = new Runnable() {
#Override
public void run() {
t.schedule(new TimerTask() {
#Override
public void run() {
EventBus.getDefault().post(
new ProgressNotification(
player.getCurrentPosition(), player.getDuration())
);
if (player.getCurrentPosition() >= player.getDuration() ){
// The audio is ended, we pause the playback,
// and reset the position
player.seekTo(0);
player.setPlayWhenReady(false);
this.cancel();
// stopping the Runnable to avoid memory leak
mainHandler.removeCallbacks(this);
}
}
},0,1000);
}
};
if(player != null) {
if (player.getPlaybackState() != Player.STATE_ENDED)
mainHandler.postDelayed(r, 500);
else {
//We put the TimerTask to sleep when audio is not playing
t.cancel();
}
}
}
Then I called the method inside the onPlayerStateChanged when adding the listener to my SimpleExoPlayer instance. The code above, sends the elapsed and total duration of the audio being played every 1 second (1000 ms) via the EventBus. Then inside the activity hosting the SeekBar:
#Subscribe(threadMode = ThreadMode.MAIN)
public void updateProgress(ProgressNotification pn) {
seekBar.setMax((int) pn.duration);
seekBar.setProgress((int) pn.currentPosition);
}
I had this problem too, and i found the solution on this link
But solution:
1. create a class like this:
public class ProgressTracker implements Runnable {
public interface PositionListener{
public void progress(long position);
}
private final Player player;
private final Handler handler;
private PositionListener positionListener;
public ProgressTracker(Player player, PositionListener positionListener) {
this.player = player;
this.positionListener = positionListener;
handler = new Handler();
handler.post(this);
}
public void run() {
long position = player.getCurrentPosition();
positionListener.progress(position);
handler.postDelayed(this, 1000);
}
public void purgeHandler() {
handler.removeCallbacks(this);
}
}
2. and finally use it in your code:
tracker = new ProgressTracker(player, new ProgressTracker.PositionListener() {
#Override
public void progress(long position) {
Log.i(TAG, "VideoViewActivity/progress: position=" + position);
}
});
3. in the last step dont forget call purgeHandler when you want release player (important)
tracker.purgeHandler();
player.release();
player = null;
Extend your current player class (SimpleExoPlayer for ex.) and add
public interface PlayerEventsListener {
void onSeek(int from, int to);
void onProgressUpdate(long progress);
}
private PlayerEventsListener mListener;
private Handler mHandler;
private Runnable mProgressUpdater;
private boolean isUpdatingProgress = false;
public SomePlayersConstructor(Activity activity, /*...*/) {
//...
mListener = (PlayerEventsListener) activity;
mHandler = new Handler();
mProgressUpdater = new ProgressUpdater();
}
// Here u gain access to seek events
#Override
public void seekTo(long positionMs) {
mListener.onSeek(-1, (int)positionMs/1000);
super.seekTo(positionMs);
}
#Override
public void seekTo(int windowIndex, long positionMs) {
mListener.onSeek((int)getCurrentPosition()/1000, (int)positionMs/1000);
super.seekTo(windowIndex, positionMs);
}
// Here u gain access to progress
public void startProgressUpdater() {
if (!isUpdatingProgress) {
mProgressUpdater.run();
isUpdatingProgress = true;
}
}
private class ProgressUpdater implements Runnable {
private static final int TIME_UPDATE_MS = 500;
#Override
public void run() {
mListener.onProgressUpdate(getCurrentPosition());
mHandler.postDelayed(mProgressUpdater, TIME_UPDATE_MS);
}
}
Then inside player activity just implement interface and start updates with player.startProgressUpdater();
If you want to accomplish this, just listen to onPositionDiscontinuity(). It will give you information if the seekbar is being scrub
rx java implementation :
private val disposablesVideoControlsDisposable = CompositeDisposable()
fun showVideoControlsAndSimilarTray() {
videoSeekBar?.setOnSeekBarChangeListener(object :
SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
if (fromUser) seekVideoProgress(progress)
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {}
override fun onStopTrackingTouch(seekBar: SeekBar?) {}
})
val disposable = Observable.interval(0, 1, TimeUnit.SECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
calculateVideoProgress()
}
disposablesVideoControlsDisposable.add(disposable)
}
private fun calculateVideoProgress() {
val currentMill = exoPlayer.currentPosition
val totalMillis = exoPlayer.duration
if (totalMillis > 0L) {
val remainMillis = (totalMillis - currentMill).toFloat() / 1000
val remainMins = (remainMillis / 60).toInt()
val remainSecs = (remainMillis % 60).toInt()
videoProgressText.setText("$remainMins:${String.format("%02d", remainSecs)}")
seekBarProgress.set((currentMill.toFloat() / totalMillis * 100).toInt())
}
}
private fun seekVideoProgress(progress: Int) {
val seekMillis = exoPlayer.duration.toFloat() * progress / 100
exoPlayer.seekTo(seekMillis.toLong())
}
And finally when you are done :
fun disposeVideoControlsObservable() {
disposablesVideoControlsDisposable.clear()
}
if you are using the player view or the player control view fully or partially(for just the buttons or something) you could set progress listener directly from it:
PlayerControlView playerControlView = miniPlayerCardView.findViewById(R.id.playerView);
ProgressBar audioProgressBar = miniPlayerCardView.findViewById(R.id.audioProgressBar);
playerControlView.setProgressUpdateListener((position, bufferedPosition) -> {
int progressBarPosition = (int) ((position*100)/player.getDuration());
int bufferedProgressBarPosition = (int) ((bufferedPosition*100)/player.getDuration());
audioProgressBar.setProgress(progressBarPosition);
audioProgressBar.setSecondaryProgress(bufferedProgressBarPosition);
});
Only use onTouchListener with the MotionEvent.ACTION_UP
SeekBar exo_progress = (SeekBar) findViewById(R.id.exo_progress);
exo_progress.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
//put your code here!!
}
return false;
}
});
This works at least with Exoplayer 2.
There is four playback states: STATE_IDLE, STATE_BUFFERING, STATE_READY and STATE_ENDED.
Checking playback state is easy to do. There is at least two solution: if-statement or switch-statement.
Whatever playback state is going on you can execute your method or set something else for example progressbar.
#Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
if (playbackState == ExoPlayer.STATE_ENDED) {
showControls();
Toast.makeText(getApplicationContext(), "Playback ended", Toast.LENGTH_LONG).show();
}
else if (playbackState == ExoPlayer.STATE_BUFFERING)
{
progressBar.setVisibility(View.VISIBLE);
Toast.makeText(getApplicationContext(), "Buffering..", Toast.LENGTH_SHORT).show();
}
else if (playbackState == ExoPlayer.STATE_READY)
{
progressBar.setVisibility(View.INVISIBLE);
}
}
it's simple
var player = SimpleExoPlayer.Builder(context).build();
player.addListener(object:Player.Listener{
override fun onEvents(player: Player, events: Player.Events) {
super.onEvents(player, events)
if (events.containsAny(
Player.EVENT_IS_LOADING_CHANGED,
Player.EVENT_PLAYBACK_STATE_CHANGED,
Player.EVENT_PLAY_WHEN_READY_CHANGED,
Player.EVENT_IS_PLAYING_CHANGED
)) {
log(msg="progres ${player.currentPosition}")
}
}
})
and you can view com.google.android.exoplayer2.ui.PlayerControlView.java at 1356 line
I dont know how I add extra time to a timer that is running.
Evertime I press a button I want to add 1 second.
So my question is, how do I do this?
Here is my timer(code):
private void TimerGame(){
new CountDownTimer(15000, 1000) {
TextView mTextField = (TextView) findViewById(R.id.timeTv);
Button start = (Button)findViewById(R.id.startGameBtn);
TextView go = (TextView)findViewById(R.id.tvGo);
public void onTick(long millisUntilFinished) {
mTextField.setText("" +millisUntilFinished / 1000);
}
public void onFinish() {
mTextField.setText("15");
start.setVisibility(View.VISIBLE);
go.setVisibility(View.INVISIBLE);
reset();
}
}.start();
}
As far as I looked into CountDownTimer there is no method to what you want.
However, I made a small example of what you can do. I haven't tested it but it shouldn't be too hard to put it on track.
private Handler countdownHandler = new Handler();
private Runnable updateCountdownTask = new UpdateCountdownTaskRunnable();
private int timeSeconds;
private class UpdateCountdownTaskRunnable implements Runnable {
#Override
public void run() {
timeSeconds -= 1;
if (timeSeconds == 0) {
stopWhatever();
} else {
updateWhatever();
countdownHandler.postDelayed(this, 1000);
}
}
}
private void start() {
timeSeconds = 20;//seconds
countdownHandler.postDelayed(updateCountdownTask, 0);
}
private void stop(){};
private void update(){};
private void addTime(int time){ timeSeconds += time; };