CountDownTimer strange Finish behaviour - java

When Timer Finish it goes back to previous Activity for a Second then Apply onFinish
What is the reason and how to prevent that.
mCountDownTimer = new CountDownTimer(mTimeLeftInMillis, 1000) {
#Override
public void onTick(long millisUntilFinished) {
mTimeLeftInMillis = millisUntilFinished;
updateCountDownText();
}
#Override
public void onFinish() {
playAgain();
}
}.start();

The Reseaon is at the start of every tick, before onTick() is called, the remaining time until the end of the countdown is calculated and If this time is smaller than the countdown time interval, onTick() would not not called anymore.
When onTick() called CountDownTimer consume some time for calculation. the easiest way to handle this is to add some extra timeLeftInMillis to your timer!

Related

Prevent this from happening with the ountdown timer function

I have this code in android studio:
The question is that if I give the button back and the main activity takes me, that is going to continue executing.
How do I make it so that once it's behind, it does not run what's in the onfinished?
new CountDownTimer (40000,1000) {
#Override
public void onTick(long l)
{
crono.setText("00:"+l/1000);
if(contador==10)
{
}
}
#Override
public void onFinish()
{
if(value.equals("tiempo"))
{
crono.setText("00:00");
}
else
{
esperar();
}
}
}.start();
CountDownTimer class help us to implement timer with custom time interval and duration. It provide 2 callback onTick(int millisUntilFinished) which called after each interval and onFinish is called when time duration is completed.
If you want to stop countdown then store instance countdown and call countdown.cancel() in onDestroy or button click(any where by user action)
You can refer this

How to add countdown timer into Firebase?

I am using FIREBASE DATABASE and I want to add A countdown timer to be shown to all users at the same time.
How to perform that in a very simple way?
My database is like this:
time:"9000" //which represent the 9 seconds
`
I tried to do something like this but it only retrieves the beginning of the time with no update of time of the database.
the overall idea is that I want every user to see the same time when he enters the activity
value= FirebaseDatabase.getInstance().getReference().child("time");
value.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
long v= (long) dataSnapshot.getValue();
CountDownTimer countDownTimer=new CountDownTimer(v,1000) {
#Override
public void onTick(long millisUntilFinished) {
show.setText("time : "+millisUntilFinished/1000);
}
#Override
public void onFinish() {
Intent i=new Intent(Main4Activity.this,OneActivity.class);
startActivity(i);
}
}.start();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
This might/might not be what you want, as it depends on a few factors. (especially cost)
You can have firebase-functions be your timer but it has a maximum execution time of 540 seconds, and after a certain quota, are not free.
https://cloud.google.com/functions/pricing
If you have a structure like this:
root
- connectedUsers
- userUid:true
- time: <time> // put your time in ms here
- observedTime: 0
- startTimer
then you will need to do a few things:
1: in onStart() / onStop() of your Activity, set/delete your user from connectedUsers via ref.child('connectedUsers').child(FirebaseAuth.getInstance().getCurrentUser().getUid()).setValue(true/null);
2: create 2 function triggers (pseudo-code)
exports.connectedUsers = functions.database.ref('/connectedUsers/{userId}').onWrite((event) => {
// return db.child('connectedUsers').get() as promise
// then check the childCount
// if it's 5, return db.child('startTimer').set(true);
// otherwise return 0
});
exports.timerFunction = functions.database.ref('/startTimer/').onWrite((event) => {
// start a timer, make sure it's a promise
// on each interval (seconds) call(not return) db.child('observedTime').set(timeLeft);
// after timer is done, return promise
// then return db.child('startTimer').delete();
});
3: inside your Activity onStart also add a ValueEventListener pointing to ref.child("observedTime") and set your Text with (don't forget to parse as string)
That's about it, as soon as 5 people are connected the function connectedUsers will trigger the timerFunction, which will start a timer and updating observedTime each interval, after that it will delete the triggerRequest.
Note, if one client loses connection, their timer will stop. There are more edge cases you would need to handle (what if a 6th client connects? or one leaves?)
If you don't want to use functions to do the countdown, you can of course have the client updating it, but that adds complexity (who updates the timer?)
Another solution could be that all users have their local timer on their phone, listen on the 'startTimer' node, and when it comes in they start their countdown. Someone(functions/client) would still need to post that request for everyone to observe, and clients might not be 100% sync
#Override
protected void onStart() {
super.onStart();
//fetch test time in minutes
//and convert that into milli seconds
mTimerRef = FirebaseDatabase.getInstance().getReference("eTest").child(key).child("TestMinutes");
mTimerRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
mTimeLeftInMillis = (Integer.parseInt(dataSnapshot.getValue(String.class))) * 6000;
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
throw databaseError.toException();
}
});
mCountDownTimer = new CountDownTimer(mTimeLeftInMillis,1000) {
#Override
public void onTick(long millisUntilFinished) {
mTimeLeftInMillis = millisUntilFinished;
updateCountDownText();
}
#Override
public void onFinish() {
mTimerRunning = false;
//when timer will finish
//submit marks to firebase and close this activity
//once marks is been submitted to firebase then close the quiz
FirebaseDatabase.getInstance().getReference("eTest").child(key).child("Marks").child(sIdHeader).setValue(Integer.toString(mScore));
Intent intent = new Intent(QnA_Test_Activity.this, Attempt_eTest_Activity.class);
startActivity(intent);
finish();
QnA_Test_Activity.this.finish();
}
}.start();
mTimerRunning = true;
}
private void updateCountDownText(){
int minutes = (int) (mTimeLeftInMillis / 1000) / 60;
int seconds = (int) (mTimeLeftInMillis / 1000) % 60;
String timeLeftFormatted = String.format(Locale.getDefault(),"%02d:%02d",minutes,seconds);
mQnATimer.setText(timeLeftFormatted);
}
I hope this will help you,
just set minutes in firebase and this code will fetch minutes and convert them into millis and then implement timer.

Java Android, Countdown Timer and ProgressBar (read first)

I have count down timer which is display by ProgressBar and successfully. But unfortunately there is problems.
The progressBar Start in 1/5 instead 0/5. and Stuck 2 seconds in 4/5 progress
If I recall the Function of Count down Timer before
public void onFinish() {
progresBar.setProgress(100);
}
display will be mess up for a while.
I really miss Unity Engine with Time.deltaTime;
Here is my code:
countDownTimer2 = new CountDownTimer(5000,1000) {
int i = 0;
#Override
public void onTick(long milliSecondUntilFinished) {
i++;
progresBar.setProgress((int) i * 100 / (5000/1000));
}
#Override
public void onFinish() {
progresBar.setProgress(100);
}
}.start();
You are incrementing your i variable before setting your progress. When onTick() is called for the first time, the value of i is 1. Thus progress you are setting is 20 which is equivalent to 1/5. Use this -
#Override
public void onTick(long milliSecondUntilFinished) {
progresBar.setProgress((int) i * 100 / (5000/1000));
i++;
}
Also, you should better use milliSecondUntilFinished to find progress instead of unneccesarily introducing i variable.
For the problem with the display, I highly doubt that you have not canceled the timer already running before calling a new one. If yes, then both timer would be running until older one finishes (till this point, the progress bar will be behaving weirdly).
You can cancel the previous timer as -
countDownTimer2.cancel();
Do this before starting a new one.
Using:
countDownTimer2 = new CountDownTimer(1000,5000) {
int i = 0;
#Override
public void onTick(long milliSecondUntilFinished) {
i++;
progresBar.setProgress((int) i * 100 / (5000/1000));
}
#Override
public void onFinish() {
progresBar.setProgress(100);
}
}.start();

How to loop countdown timer multiple times

I am having a problem repeating countdown timer multiple times,in my case 12 times. I have made two countdown timers,one for roundtime and one for pause and works great.Problem is when the pause is finihed I want that roundtime countdown starts again automaticly until 12 rounds with pause are finished.
I use for loop.Is there any better way?
Here is code: when user clicks the button countdown starts
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
long roundtime= Long.parseLong(String.valueOf(msg1))*1000;//user has picked roundtime
Counting timer = new Counting(roundtime, 100);//class Counting extends CountdownTimer
for(int i=0;i<12;i++){
timer.start(); // It goes just ones!
}
}
And countdown timer:
class Counting extends CountDownTimer {
public Counting(long roundtime, long countDownInterval) {
super(roundtime, countDownInterval);
}
#Override
public void onTick(long millisUntilFinished) {
textView.setText("" + String.format("%02d:%02d",
TimeUnit.MILLISECONDS.toMinutes(millisUntilFinished) - TimeUnit.HOURS.toMinutes(
TimeUnit.MILLISECONDS.toHours(millisUntilFinished)),
TimeUnit.MILLISECONDS.toSeconds(millisUntilFinished) - TimeUnit.MINUTES.toSeconds(
TimeUnit.MILLISECONDS.toMinutes(millisUntilFinished))));
}
#Override
public void onFinish() { // When roundtime is finished,start pause time!
int seconds = msg1 % 60;
int minutes = (msg1 / 60) % 60;
textView.setText(String.format("%02d : %02d", minutes, seconds));
long pause= Long.parseLong(String.valueOf(msg2))*1000; //user has picked pause time
Counting2 timer2 = new Counting2(pause, 100);
timer2.start();
}
}
class Counting2 extends CountDownTimer {
public Counting2(long pause, long countDownInterval) {
super(pause, countDownInterval);
}
#Override
public void onTick(long millisUntilFinished) {
textView2.setText("" + String.format("%02d:%02d",
TimeUnit.MILLISECONDS.toMinutes(millisUntilFinished) - TimeUnit.HOURS.toMinutes(
TimeUnit.MILLISECONDS.toHours(millisUntilFinished)),
TimeUnit.MILLISECONDS.toSeconds(millisUntilFinished) - TimeUnit.MINUTES.toSeconds(
TimeUnit.MILLISECONDS.toMinutes(millisUntilFinished))));
}
#Override
public void onFinish() {
int seconds = msg2 % 60;
int minutes = (msg2 / 60) % 60;
textView2.setText(String.format("%02d : %02d", minutes, seconds));
}
}
});
}
First off, you want it to loop 12 times.
This isn't the issue at hand, however, you'll thank me when it only loops 11 times.
it should be
for(int i=0;i<=12;i++){
timer.start(); // It goes just ones!
}
As I do not know the implementation of Counting2, it appears to be getting hung up on the start function there. I do not know if it's overridden or not. If you have a similar onFinish method, this is due to a recursive call back on the original Counting timer. Because of this, it loops with a waiting period in between (It doesn't get a stack overflow 'as quickly' as a result).
Try busy waiting for the previous timer to finish and then start it again, and it'll do so 12 times.
for(int i=0;i<12;i++){
while(!previousTimerActive);
timer.start();
previousTimerActive = true;
}
And in your Counting2's onFinish() set the indicator previousTimerActive = false;. Now you just need to see what way you want this boolean previousTimerActive to be 'global' (so it can be accessed from both your activity and your class Counting2). You can get it and set it in the SharedPreferences or use a third-party tool like EventBus to send it's value from one to the other.

Resuming CountdownTimer after rotation

I have a CountdownTimer that counts down from 60 seconds. This CountdownTimer works by setting a textView to the remaining milliseconds, but whenever i rotate my device, the CountdownTimer gets reset.
I know this happens because the Activity gets restarted on rotation. So i tried saving the time remaining in a bundle and then restoring it, after the Activity was restarted.
long transferValue;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_playtimemode);
Log.d("Debug", "onCreate: " + transferValue);
long setTime = 60000;
long difference = setTime - transferValue;
new CountDownTimer(difference, 1000) {
public void onTick(long millisUntilFinished) {
millisUntilFinishedToSave = millisUntilFinished;
tvCountdown.setText("" + millisUntilFinished / 1000);
}
public void onFinish() {
tvCountdown.setText("done!");
}
}.start();
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putLong("millisKey", millisUntilFinishedToSave);
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
transferValue = savedInstanceState.getLong("millisKey");
Log.d("Debug", "onRestoreInstanceState(): " + transferValue);
}
This however doesn't work. I am intializing transferValue at the top of this code (hence it returning 0), but how can i else save the data from the savedInstanceState to the CountdownTimer?
07-06 20:21:30.038: D/Debug(28995): onCreate: 0
07-06 20:21:30.043: D/Debug(28995): onRestoreInstanceState(): 55994
I would give your timer it's own thread. Your timer is being stopped because it's on the UI thread (as you stated) and when the UI redraws the Activity is re-initialized. All long running processes should have their own thread. Rule of thumb: get out of the UI thread as soon as possible.
Here is an example of using a Service. This service will start when called and stay in memory regardless of screen orientation changes or even activity focus changes.
Here is the service:
public class Timer extends Service {
#Override
public IBinder onBind(Intent i) {
return null;
}
#Override
public int onStartCommand(Intent i, int flags, int startId) {
// Put your timer code here
}
}
You will need to add this line to your manifest (somewhere between the application open/close tags):
<service android:name=".Timer" />
Then you can start the service at any time by using this (it's important to note that you can start it over and over again, the recursive calls do not make a new service.):
startService(new Intent(this, Timer.class));
Use System.currentTimeMillis to get the current system time, then add 60000 milliseconds to the time and store that as an end time. Then any time you have to change anything, just compare System.currentTimeMillis to the EndTime.
Endtime = System.currentTimeMillis + 60000;
then on every instance
TimeRemaining = Endtime - System.currentTimeMillis
The accepted answer makes the thing very complex. You can do it in a simpler way. The problem in your code is that Activity gets created on rotation (see Activity Lifecycle), so does the CountdownTimer instance.
I could write the whole example code but there is a nice answer from #Drew here: Android Innerclass TextView reference when activity resumes

Categories

Resources