I need make some action after video have played 50% of its length.
So I did not see some listener with something like "onCertainMills()", I see onVideoEnded() but I need get event when current time of played video will more then length/2.
You could try to use the YoutubePlayer's property getCurrentTimeMillis() to constantly obtain the time and getDurationMillis() to get the video's total duration. Thus, you can set the desired task when the youtubePlayer.getCurrentTimeMillis() >= youtubePlayer.getDurationMillis() with an asynctask.
The code will look like this:
player.setPlaylistEventListener(playlistEventListener);
player.setPlayerStateChangeListener(playerStateChangeListener);
player.setPlaybackEventListener(playbackEventListener);
AsyncTask a = new AsyncTask(); //listener for time
a.execute(player, 2000,this); //player, time, and activity
if (!wasRestored) {
playVideoAtSelection();
}
setControlsEnabled(true);
At the end of the ends I have made class to make action on certain second of playing video.
Here is the code to make some action after half time of the video playing: (+ 1 sec) Hope it will help to someone.
import android.os.Handler;
import android.util.Log;
import com.clipclash.android.entities.Clip;
import com.google.android.youtube.player.YouTubePlayer;
public class YoutubeCounter {
Clip clip;
long counter;
long startCut;
long endCut;
long durationGoal;
private YouTubePlayer youTubePlayer;
boolean goal = false;
Handler h;
Runnable checkGoal = new Runnable() {
public void run() {
getProgress();
if (counter >= durationGoal) {
// GOAL !!!
//TODO MAKE SOME ACTION
goal = true;
stopSchedule();
} else {
startSchedule();
}
}
};
public YoutubeCounter(Clip clip) {
this.clip = clip;
h = new Handler();
}
public void setYouTubePlayer(YouTubePlayer youTubePlayer) {
this.youTubePlayer = youTubePlayer;
}
public void play() {
if (!goal) {
if (durationGoal == 0) {
durationGoal = this.youTubePlayer.getDurationMillis() / 2;
}
startCut = youTubePlayer.getCurrentTimeMillis();
startSchedule();
}
}
public void stop() {
if (!goal) {
getProgress();
stopSchedule();
}
}
private void startSchedule() {
long newSchedule = durationGoal - counter;
newSchedule = newSchedule + 1000; // just a little bit more - not requires
h.postDelayed(checkGoal, newSchedule);
}
public void stopSchedule() {
h.removeCallbacks(checkGoal);
}
private void getProgress() {
try {
endCut = youTubePlayer.getCurrentTimeMillis();
long cut = endCut - startCut;
if (cut < 0) {
cut = 0;
}
counter += cut;
} catch (Exception e) {
}
}
public long getCounter() {
return counter;
}
}
Related
My app is built with around 50-60 screens. Each screen has a button to open a new screen. Now the issue I am facing is when user double tap button, a new screen is open twice.
For this issue, I found a solution like below.
if (SystemClock.elapsedRealtime() - mLastClickTime < 1000){
return;
}
mLastClickTime = SystemClock.elapsedRealtime();
But to prevent double click, I need to write the above code in each button click. I have not created common custom button which used everywhere.
Is there any way to double tap on app level?
i got same issue i solved it as below it might be helpfull for you.
you can achive by two ways
One:
try to using a boolean variable:
public class Blocker {
private static final int BLOCK_TIME = 1000;
private boolean isBlockClick;
/**
* Block any event occurs in 1000 millisecond to prevent spam action
* #return false if not in block state, otherwise return true.
*/
public boolean block(int blockInMillis) {
if (!isBlockClick) {
isBlockClick= true;
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
isBlockClick= false;
}
}, blockInMillis);
return false;
}
return true;
}
public boolean block() {
return block(BLOCK_TIME );
}
}
use this as below in every click.
button.setOnClickListener(new View.OnClickListener() {
private Blocker blocker = new Blocker();
#Override
public void onClick(View v) {
if (!blocker.block(block-Time-In-Millis)) {
// do your action
}
}
});
Two
or you can set button.setEnable(false) on every clickevent of button as below
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
btn.setEnabled(false);
}
});
actually you can set the activities not to have multiple instances by adding the flag Intent.FLAG_ACTIVITY_REORDER_TO_FRONT to the intent.
see answer from other question
If the activity is in the stack, it will not be created twice
use this custom class it can handle any doubletab or single tab on button click event
public class DoubleTabCustomButton implements View.OnClickListener {
private boolean isRunning = true;
private int resetInTime = 500;
private int counter = 0;
private DoubleClickCallback listener;
public DoubleTabCustomButton(Context context) {
listener = (DoubleClickCallback) context;
}
#Override
public void onClick(View v) {
if (isRunning) {
if (counter == 1) {
listener.onDoubleClick(v);
}
else if (counter==0){
listener.onSingleClick(v);
}
counter++;
if (!isRunning) {
isRunning = true;
new Thread(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(resetInTime);
isRunning = false;
counter = 0;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
}
}
it's interface
public interface DoubleClickCallback {
public void onDoubleClick(View v);
public void onSingleClick(View V);
}
and finally you can use in activity like this
public class ButtonDoubleTab extends AppCompatActivity implements DoubleClickCallback {
Button btndoubletab;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_button_double_tab);
btndoubletab=findViewById(R.id.btndoubletab);
// btndoubletab.setOnClickListener(this);
btndoubletab.setOnClickListener(new DoubleTabCustomButton(this));
}
#Override
public void onDoubleClick(View v) {
//do double tab action
}
#Override
public void onSingleClick(View V) {
//single tab action
}
}
If you have a base activity class, you can override the startActivity(Intent) method to add the Intent.FLAG_ACTIVITY_REORDER_TO_FRONT
abstract class BaseActivity: AppCompatActivity() {
final override fun startActivity(intent: Intent) {
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
super.startActivity(intent)
}
}
Use this inline function:
inline fun View.onSingleClick(minimumClickInterval: Long = 800, crossinline onClick: (View?) -> Unit) {
var isViewClicked = false
var mLastClickTime = 0L
setOnClickListener { view ->
val currentClickTime = SystemClock.uptimeMillis()
val elapsedTime = currentClickTime - mLastClickTime
mLastClickTime = currentClickTime
if (elapsedTime <= minimumClickInterval)
return#setOnClickListener
if (!isViewClicked) {
isViewClicked = true
Handler(Looper.getMainLooper()).postDelayed({ isViewClicked = false }, 600)
} else {
return#setOnClickListener
}
onClick(view)
Log.d(this.javaClass.simpleName, "onSingleClick successfully called")
}
}
Use with any view like this:
button.onSingleClick {
// do something here on the button click
}
You can also set the minimum click interval like this:
button.onSingleClick(1000) {
// do something here on the button click
}
make the button disable on click .
b.setEnabled(false);
You can make it back enable it onResume or any other certain callback
b.setEnabled(true);
I have a media player on one activity (called player) and I want to be able to support continuous video playback from when the player is closed into a miniature view on the parent activity.
I am able to do this just fine when it is audio only, the problem lies when I attach a SurfaceView via mediaPlayer.setDisplay();
I can attach the SurfaceView just fine initially but the problems start when I close the Player activity. If I make no changes, the mediaPlayer gets thrown into an error state somehow with the usual unhelpful errors (1, -19) etc.
I have tried using setDisplay(null) when the Player SurfaceView is destroyed which appears to work. But for some reason it resets the video stream. I've tried overriding seekTo() in order to figure out what is happening but seekTo() is not being called. I've also put logging statements everywhere I can think of but nothing is being triggered.
Why would setDisplay(null) cause my video stream to restart?
Here is my current MediaPlayer code (some of the weird stuff is from me trying to solve the issue (like isReallyPlaying()):
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
private ScheduledFuture beeperhandle;
private boolean isPaused = false;
private BaseMedia currentMedia;
private PlaybackService.RepeatStatus repeatStatus = PlaybackService.RepeatStatus.REPEAT_NONE;
public void startMedia(BaseMedia model, Integer position) {
Timber.d("Starting media");
startBeeper();
isPaused = false;
lastBeep = -1;
currentMedia = model;
if (position != null) {
seekTo(position);
}
super.start();
}
public BaseMedia getCurrentMedia() {
return currentMedia;
}
#Override
public void start() throws IllegalStateException {
Timber.e("Invalid start called, should request startSong or startVideo");
}
private int lastBeep = -1;
// Because isPlaying is returning false and canceling the beeper. Probably has something to do with the surfaceview being destroyed
private boolean isStillPlaying() {
if (lastBeep != getCurrentPosition()) {
lastBeep = getCurrentPosition();
return true;
}
return false;
}
private final Runnable seekBarCheck = new Runnable() {
public void run() {
if (isStillPlaying() && !beeperhandle.isCancelled()) {
EventBus.getDefault().post(new MusicStatusTimeEvent(
currentMedia, true, GevaldMediaPlayer.this));
} else {
Timber.d("Canceling Beeper, !isPlaying");
beeperhandle.cancel(true);
}
}
};
private void startBeeper() {
Timber.d("Starting Beeper");
beeperhandle = scheduler.scheduleAtFixedRate(seekBarCheck, 100, 100, TimeUnit.MILLISECONDS);
}
#Override
public void seekTo(final int msec) throws IllegalStateException {
Timber.d("Seeking to " + msec);
if (beeperhandle != null) {
Timber.d("Canceling beeper in prep for seek");
beeperhandle.cancel(true);
}
setOnSeekCompleteListener(new OnSeekCompleteListener() {
#Override
public void onSeekComplete(MediaPlayer mp) {
Timber.d("Seek complete to: " + msec);
startBeeper();
}
});
super.seekTo(msec);
}
#Override
public void stop() throws IllegalStateException {
super.stop();
Timber.d("Stopping media");
doStop();
}
private void doStop() {
if (beeperhandle != null) {
Timber.d("Canceling beeper, doStop");
beeperhandle.cancel(true);
}
isPaused = false;
}
#Override
public void pause() throws IllegalStateException {
Timber.d("Pause requested");
if (beeperhandle != null) {
Timber.d("Canceling beeper, pause");
beeperhandle.cancel(true);
}
doStop();
EventBus.getDefault().post(new MusicStatusStoppedEvent(this));
super.pause();
}
public boolean isPaused() {
return isPaused;
}
Figured it out. Apparently closing an activity causes an audio loss with a value of AudioManager.AUDIOFOCUS_LOSS.
Since I was being a good Android citizen, that was set to release the media player. But audio would then be regained which would cause my media player to get reset and hence start from the beginning.
It just happened to line up that this occurred right around the setDisplay() method.
I am a programmer writing my first Android Application.
In C# I usually use a Thread Worker class for what I am trying to achieve.
The application consists of a set of text boxes that I need to update every second based on variables in the class. I want to be able to start a background thread that contains a loop that does the updating.
The code I am using is shown below.
I can get the update done once if I exit the loop but the GUI becomes unresponsive (locks up) if I do not do this. I assume the code is not being run in the background.
Currently I am trying this using Runnable but may look at AsyncTask or even Executor.
package com.example.redkatipo.myfirstapp;
import android.os.*;
import java.util.List;
/**
* Created by Brian on 6/04/2015.
*/
class GroupTimerWorker implements Runnable {
Boolean _stopping = false;
Boolean _stopped = false;
Boolean _paused = false;
List<TimerLine> _lines;
public GroupTimerWorker(List<TimerLine> lines) {
_lines = lines;
}
public void Stop() {
_stopping = true;
}
public void SetPaused(Boolean pause) {
_paused = pause;
}
#Override
public void run() {
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUN D);
while (!_stopping) {
try {
Thread.sleep(1000);
if (!_paused) {
updateAllTimers();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
_stopping = true;
}
}
public synchronized void updateAllTimers() {
for (int i = 0; i < _lines.size(); i++) {
TimerLine tl = _lines.get(i);
if (tl._isRunning) {
tl.SetCurrentDuration();
}
}
}
}
public void updateAllTimers(View view) {
_groupTimerWorker = new GroupTimerWorker(_displayLines);
new Thread (_groupTimerWorker).run();
}
package com.example.redkatipo.myfirstapp;
import android.content.Context;
import android.os.SystemClock;
import android.content.res.Resources;
import android.text.method.BaseKeyListener;
import android.widget.TextView;
import android.widget.Button;
/**
* Created by Brian on 27/03/2015.
*/
public class TimerLine {
public Context _context;
public Button _stopStart;
TextView _person;
public TextView _elapsed;
boolean _isRunning;
int _startTime = 0;
int _currentTime = 0;
int _previousTime = 0;
public TimerLine(Context context, Button _control, TextView _id, TextView _output) {
_context = context;
_stopStart = _control;
_person = _id;
_elapsed = _output;
_isRunning = false;
_stopStart.setTag(this);
_elapsed.setText("idle");
}
private int CurrentSeconds()
{
return (int)(SystemClock.uptimeMillis()/1000);
}
public void stopStartButtonClick()
{
if (_isRunning == false) {
_isRunning = true;
// _stopStart.setBackground(_context.getResources().getDrawable(R.drawable.red_button));
_stopStart.setText("Stop");
_elapsed.setBackground(_context.getResources().getDrawable(R.drawable.green_button));
_previousTime = _previousTime + _currentTime - _startTime;
_elapsed.setText(formatTime(_previousTime));
_startTime = CurrentSeconds();
} else {
_isRunning = false;
/// _stopStart.setBackground(_context.getResources().getDrawable(R.drawable.green_button));
_stopStart.setText("Start");
_currentTime = CurrentSeconds();
int difference = (_currentTime - _startTime);
int totalTime = difference + _previousTime;
_elapsed.setText(formatTime(totalTime));
_elapsed.setBackground(_context.getResources().getDrawable(R.drawable.red_button));
}
}
public void SetCurrentDuration()
{
int now = CurrentSeconds();
int difference = now - _startTime;
int totalTime = difference + _previousTime;
_elapsed.setText(formatTime(totalTime));
}
private String formatTime(int totalTime)
{
int minutes = totalTime/60;
int seconds = totalTime % 60;
return "" + minutes + ":" + String.format("%02d", seconds);
}
}
In android there is several way to do this. But I prefer below process.
class Yourclass{
Runnable mRunnable ;
int x=1;//number of sec
private void AnyFunction( ) {
mRunnable = new Runnable() {
#Override
public void run() {
<create a handler here say myhandler>
//Optional if you want to start the first call af 'x' sec
myhandler.postDelayed(mRunnable, x * 1000);
}
};
}
private final Handler myhandler= new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case ans1:
{
<your code to change update ui etc>
myhandler.postDelayed(mRunnable, x * 1000);
break;
}
}
/*How to call this from any function wher you need to call the interval if oncreate the on creat call method AnyFunction*/
AnyFunction();
}
Hope this may help.
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
In my application i have a button. After single and double clicking of the button will perform separate operation. How can i do that? Thanks
Well it is simple just override.
onClick method of OnClickListener
public abstract class DoubleClickListener implements View.OnClickListener {
private static final long DEFAULT_QUALIFICATION_SPAN = 200;
private boolean isSingleEvent;
private long doubleClickQualificationSpanInMillis;
private long timestampLastClick;
private Handler handler;
private Runnable runnable;
public DoubleClickListener() {
doubleClickQualificationSpanInMillis = DEFAULT_QUALIFICATION_SPAN;
timestampLastClick = 0;
handler = new Handler();
runnable = new Runnable() {
#Override
public void run() {
if (isSingleEvent) {
onSingleClick();
}
}
};
}
#Override
public void onClick(View v) {
if((SystemClock.elapsedRealtime() - timestampLastClick) < doubleClickQualificationSpanInMillis) {
isSingleEvent = false;
handler.removeCallbacks(runnable);
onDoubleClick();
return;
}
isSingleEvent = true;
handler.postDelayed(runnable, DEFAULT_QUALIFICATION_SPAN);
timestampLastClick = SystemClock.elapsedRealtime();
}
public abstract void onDoubleClick();
public abstract void onSingleClick();
}
Usage
button.setOnClickListener(new DoubleClickListener() {
#Override
public void onDoubleClick() {
Log.i("onClick", "double");
}
#Override
public void onSingleClick() {
Log.i("onClick", "single");
}
});
You may need to create a delay variable which will differenciate between single click and double click.
See this code,
private static final long DOUBLE_PRESS_INTERVAL = 250; // in millis
private long lastPressTime;
private boolean mHasDoubleClicked = false;
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
// Get current time in nano seconds.
long pressTime = System.currentTimeMillis();
// If double click...
if (pressTime - lastPressTime <= DOUBLE_PRESS_INTERVAL) {
Toast.makeText(getApplicationContext(), "Double Click Event", Toast.LENGTH_SHORT).show();
mHasDoubleClicked = true;
}
else { // If not double click....
mHasDoubleClicked = false;
Handler myHandler = new Handler() {
public void handleMessage(Message m) {
if (!mHasDoubleClicked) {
Toast.makeText(getApplicationContext(), "Single Click Event", Toast.LENGTH_SHORT).show();
}
}
};
Message m = new Message();
myHandler.sendMessageDelayed(m,DOUBLE_PRESS_INTERVAL);
}
// record the last time the menu button was pressed.
lastPressTime = pressTime;
return true;
}
You may want to consider not using a DoubleTap. It is not a normal Android behavior.
When I first started programming on the Android, I kept running into things that were really "hard" to do on the android. Over time, I've found that many of them were difficult because they were a pretty bad idea.
If you are porting an iOS app, or emulating an iOS app's behavior, you may want to consider converting the UI over to Android style behaviors and use a longPress or other 'androidy' gestures.
Here is a similar question and answer:
Android: How to detect double-tap?
You have to implement GestureDetector and put your code in single/double click.
TestActivity.java
iv.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
//putyour first activity call.
}
}
iv.setOnTouchListener(new OnTouchListener() {
GestureDetector gestureDetector = new GestureDetector(context, new MyGestureDetector(context));
#Override
public boolean onTouch(View v, MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
});
Now you have to create GestureDetector.java class.
public class MyGestureDetector extends SimpleOnGestureListener {
public Context context;
public String phno;
public MyGestureDetector(Context con) {
this.context=con;
}
#Override
public boolean onDown(MotionEvent e) {
return super.onDown(e);
}
#Override
public boolean onDoubleTap(MotionEvent e) {
System.out.println("in Double tap");
return true;
}
#Override
public boolean onSingleTapUp(MotionEvent e) {
System.out.println("in single tap up");
//put your second activity.
return super.onSingleTapUp(e);
}
}
Thanks to #NikolaDespotoski.
You can check DOUBLE-TAP example from following URL.
that is used in listView. i hope it is useful for you.
https://nodeload.github.com/NikolaDespotoski/DoubleTapListView/zip/master
Though it's too late, but anyone can figure out if they see this.
int number_of_clicks = 0;
boolean thread_started = false;
final int DELAY_BETWEEN_CLICKS_IN_MILLISECONDS = 250;
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
++number_of_clicks;
if(!thread_started){
new Thread(new Runnable() {
#Override
public void run() {
thread_started = true;
try {
Thread.sleep(DELAY_BETWEEN_CLICKS_IN_MILLISECONDS);
if(number_of_clicks == 1){
client.send(AppHelper.FORMAT_LEFT_CLICK);
} else if(number_of_clicks == 2){
client.send(AppHelper.FORMAT_DOUBLE_CLICK);
}
number_of_clicks = 0;
thread_started = false;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
});
Explanation:
Before the button click, number_of_click is initialized to 0.
thread_started is a flag detecting if the thread is started before or not.
Now, on button click, increase the number of button click by incremental operator.
check if the thread is previously started or not, if not, then start the thread.
on thread, apply your logic by using the number_of_clicks. and the thread will wait for next milliseconds and then will go through your logic.
So, now you can apply as many clicks as you want.
This is #saksham's answer in Kotlin.
abstract class DoubleClickListener : View.OnClickListener {
private val DEFAULT_QUALIFICATION_SPAN = 200L
private var isSingleEvent = false
private val doubleClickQualificationSpanInMillis =
DEFAULT_QUALIFICATION_SPAN
private var timestampLastClick = 0L
private val handler = Handler(Looper.getMainLooper())
private val runnable: () -> Unit = {
if (isSingleEvent) {
onSingleClick()
}
}
override fun onClick(v: View) {
if (SystemClock.elapsedRealtime() - timestampLastClick < doubleClickQualificationSpanInMillis) {
isSingleEvent = false
handler.removeCallbacks(runnable)
onDoubleClick()
return
}
isSingleEvent = true
handler.postDelayed(runnable, DEFAULT_QUALIFICATION_SPAN)
timestampLastClick = SystemClock.elapsedRealtime()
}
abstract fun onDoubleClick()
abstract fun onSingleClick()
}
Solving this by inherit from the View.OnClickListener and checking the click time to distinguish the single click or double click, this also solve the problem of fast clicking. This solution will bring minor code change, just replace View.OnClickLister. You also can override the getGap() to redefine the time between two clicks.
import android.os.SystemClock;
import android.view.View;
/*****
* Implement to fix the double click problem.
* Avoid the fast double click for button and images.
*/
public abstract class OnSingleClickListener implements View.OnClickListener {
private long prevClickTime =0;
#Override
public void onClick(View v) {
_onClick(v);
}
private synchronized void _onClick(View v){
long current = SystemClock.elapsedRealtime();
if(current-prevClickTime> getGap()){
onSingleClick(v);
prevClickTime = SystemClock.elapsedRealtime();
}else {
onDoubleClick(v);
prevClickTime = 0;
}
}
public abstract void onSingleClick(View v);
public abstract void onDoubleClick(View v);
/********
*
* #return The time in ms between two clicks.
*/
public long getGap(){
return 500L; //500ms
}
}
double click and single click in android
A solution that fits almost all versions
Detect the type of event within the standard duration you want to define It can detect the event in an accelerated and
sequential manner, such as passing a video for a certain period
//general definition
private CountDownTimer countDownTimer;
private int click_duble = 1;
button.setOnClickListener(view -> {
if (countDownTimer == null) {
float Second= (float) 0.25; //Detecting the type of event within a quarter of a second
countDownTimer= new CountDownTimer((long) (Second * 1000), 50) {
#Override public void onTick(long l){}
#Override
public void onFinish() {
if (click_duble >= 2) {
Toast.makeText(player_fb.this, "Double", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(player_fb.this, "Single", Toast.LENGTH_SHORT).show();
}
click_duble = 1;
countDownTimer = null;
}};countDownTimer.start();
}else
click_duble += 1;
});
The up solution cannot work for multi click, i test it but failed.
So i suggest to use RxBinding with ProgressDialog.
when click button, the progressDialog show setting it cannot be cancel, Fix it.
I also got the same problem once
setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if(isFmOn()){
//stopFM
}else{
//do other things
}
}
}
when I clicked the Button,FM stopped;but when I double clicked,FM did not stop.The problem was that single and double clicking of the button ,the value of isFmOn() was difference.
I sloved the problem using this:
setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Thread.sleep(500);//500ms was enough to finish stopFM before the second click
if(isFmOn()){
//stopFM
}else{
//do other things
}
}
}