Android: How to pause and resume a Count Down Timer? - java

I have developed a Count Down Timer and I am not sure how to pause and resume the timer as the textview for the timer is being clicked. Click to start then click again to pause and to resume, click again the timer's text view.
This is my code:
Timer = (TextView) this.findViewById(R.id.time); //TIMER
Timer.setOnClickListener(TimerClickListener);
counter = new MyCount(600000, 1000);
}//end of create
private OnClickListener TimerClickListener = new OnClickListener() {
public void onClick(View v) {
updateTimeTask();
}
private void updateTimeTask() {
if (decision == 0) {
counter.start();
decision = 1;
} else if (decision == 2) {
counter.onResume1();
decision = 1;
} else {
counter.onPause1();
decision = 2;
}//end if
}
;
};
class MyCount extends CountDownTimer {
public MyCount(long millisInFuture, long countDownInterval) {
super(millisInFuture, countDownInterval);
}//MyCount
public void onResume1() {
onResume();
}
public void onPause1() {
onPause();
}
public void onFinish() {
Timer.setText("00:00");
p1++;
if (p1 <= 4) {
TextView PScore = (TextView) findViewById(R.id.pscore);
PScore.setText(p1 + "");
}//end if
}//finish
public void onTick(long millisUntilFinished) {
Integer milisec = new Integer(new Double(millisUntilFinished).intValue());
Integer cd_secs = milisec / 1000;
Integer minutes = (cd_secs % 3600) / 60;
Integer seconds = (cd_secs % 3600) % 60;
Timer.setText(String.format("%02d", minutes) + ":"
+ String.format("%02d", seconds));
///long timeLeft = millisUntilFinished / 1000;
/}//on tick
}//class MyCount
protected void onResume() {
super.onResume();
//handler.removeCallbacks(updateTimeTask);
//handler.postDelayed(updateTimeTask, 1000);
}//onResume
#Override
protected void onPause() {
super.onPause();
//do stuff
}//onPause

/*
* Copyright (C) 2010 Andrew Gainer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Adapted from Android's CountDownTimer class
package com.cycleindex.multitimer;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
/**
* Schedule a countdown until a time in the future, with
* regular notifications on intervals along the way.
*
* The calls to {#link #onTick(long)} are synchronized to this object so that
* one call to {#link #onTick(long)} won't ever occur before the previous
* callback is complete. This is only relevant when the implementation of
* {#link #onTick(long)} takes an amount of time to execute that is significant
* compared to the countdown interval.
*/
public abstract class CountDownTimerWithPause {
/**
* Millis since boot when alarm should stop.
*/
private long mStopTimeInFuture;
/**
* Real time remaining until timer completes
*/
private long mMillisInFuture;
/**
* Total time on timer at start
*/
private final long mTotalCountdown;
/**
* The interval in millis that the user receives callbacks
*/
private final long mCountdownInterval;
/**
* The time remaining on the timer when it was paused, if it is currently paused; 0 otherwise.
*/
private long mPauseTimeRemaining;
/**
* True if timer was started running, false if not.
*/
private boolean mRunAtStart;
/**
* #param millisInFuture The number of millis in the future from the call
* to {#link #start} until the countdown is done and {#link #onFinish()}
* is called
* #param countDownInterval The interval in millis at which to execute
* {#link #onTick(millisUntilFinished)} callbacks
* #param runAtStart True if timer should start running, false if not
*/
public CountDownTimerWithPause(long millisOnTimer, long countDownInterval, boolean runAtStart) {
mMillisInFuture = millisOnTimer;
mTotalCountdown = mMillisInFuture;
mCountdownInterval = countDownInterval;
mRunAtStart = runAtStart;
}
/**
* Cancel the countdown and clears all remaining messages
*/
public final void cancel() {
mHandler.removeMessages(MSG);
}
/**
* Create the timer object.
*/
public synchronized final CountDownTimerWithPause create() {
if (mMillisInFuture <= 0) {
onFinish();
} else {
mPauseTimeRemaining = mMillisInFuture;
}
if (mRunAtStart) {
resume();
}
return this;
}
/**
* Pauses the counter.
*/
public void pause () {
if (isRunning()) {
mPauseTimeRemaining = timeLeft();
cancel();
}
}
/**
* Resumes the counter.
*/
public void resume () {
if (isPaused()) {
mMillisInFuture = mPauseTimeRemaining;
mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture;
mHandler.sendMessage(mHandler.obtainMessage(MSG));
mPauseTimeRemaining = 0;
}
}
/**
* Tests whether the timer is paused.
* #return true if the timer is currently paused, false otherwise.
*/
public boolean isPaused () {
return (mPauseTimeRemaining > 0);
}
/**
* Tests whether the timer is running. (Performs logical negation on {#link #isPaused()})
* #return true if the timer is currently running, false otherwise.
*/
public boolean isRunning() {
return (! isPaused());
}
/**
* Returns the number of milliseconds remaining until the timer is finished
* #return number of milliseconds remaining until the timer is finished
*/
public long timeLeft() {
long millisUntilFinished;
if (isPaused()) {
millisUntilFinished = mPauseTimeRemaining;
} else {
millisUntilFinished = mStopTimeInFuture - SystemClock.elapsedRealtime();
if (millisUntilFinished < 0) millisUntilFinished = 0;
}
return millisUntilFinished;
}
/**
* Returns the number of milliseconds in total that the timer was set to run
* #return number of milliseconds timer was set to run
*/
public long totalCountdown() {
return mTotalCountdown;
}
/**
* Returns the number of milliseconds that have elapsed on the timer.
* #return the number of milliseconds that have elapsed on the timer.
*/
public long timePassed() {
return mTotalCountdown - timeLeft();
}
/**
* Returns true if the timer has been started, false otherwise.
* #return true if the timer has been started, false otherwise.
*/
public boolean hasBeenStarted() {
return (mPauseTimeRemaining <= mMillisInFuture);
}
/**
* Callback fired on regular interval
* #param millisUntilFinished The amount of time until finished
*/
public abstract void onTick(long millisUntilFinished);
/**
* Callback fired when the time is up.
*/
public abstract void onFinish();
private static final int MSG = 1;
// handles counting down
private Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
synchronized (CountDownTimerWithPause.this) {
long millisLeft = timeLeft();
if (millisLeft <= 0) {
cancel();
onFinish();
} else if (millisLeft < mCountdownInterval) {
// no tick, just delay until done
sendMessageDelayed(obtainMessage(MSG), millisLeft);
} else {
long lastTickStart = SystemClock.elapsedRealtime();
onTick(millisLeft);
// take into account user's onTick taking time to execute
long delay = mCountdownInterval - (SystemClock.elapsedRealtime() - lastTickStart);
// special case: user's onTick took more than mCountdownInterval to
// complete, skip to next interval
while (delay < 0) delay += mCountdownInterval;
sendMessageDelayed(obtainMessage(MSG), delay);
}
}
}
};
}
Source : This Gist.

A nice and simple way to create a Pause/Resume for your CountDownTimer is to create a separate method for your timer start, pause and resume as follows:
public void timerStart(long timeLengthMilli) {
timer = new CountDownTimer(timeLengthMilli, 1000) {
#Override
public void onTick(long milliTillFinish) {
milliLeft=milliTillFinish;
min = (milliTillFinish/(1000*60));
sec = ((milliTillFinish/1000)-min*60);
clock.setText(Long.toString(min)+":"+Long.toString(sec));
Log.i("Tick", "Tock");
}
}
timer.start();
The timerStart has a long parameter as it will be reused by the resume() method below. Remember to store your milliTillFinished (above as milliLeft) so that you may send it through in your resume() method. Pause and resume methods below respectively:
public void timerPause() {
timer.cancel();
}
private void timerResume() {
Log.i("min", Long.toString(min));
Log.i("Sec", Long.toString(sec));
timerStart(milliLeft);
}
Here is the code for the button FYI:
startPause.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(startPause.getText().equals("Start")){
Log.i("Started", startPause.getText().toString());
startPause.setText("Pause");
timerStart(15*1000);
} else if (startPause.getText().equals("Pause")){
Log.i("Paused", startPause.getText().toString());
startPause.setText("Resume");
timerPause();
} else if (startPause.getText().equals("Resume")){
startPause.setText("Pause");
timerResume();
}

Well there are no API to pause or resume it. What you should do is cancel() the timer and store the time remaining in a variable. When the resume button is hit again, restart the timer with the value from the variable.
You know Chronometers may be of interest to you.

I wrote a extended CountDownTimer here. Let's try this and leave your comment:
Eg:
// Init timer
lateinit var timerExt: CountDownTimerExt
timerExt = object : CountDownTimerExt(TIMER_DURATION, TIMER_INTERVAL) {
override fun onTimerTick(millisUntilFinished: Long) {
Log.d("MainActivity", "onTimerTick $millisUntilFinished")
}
override fun onTimerFinish() {
Log.d("MainActivity", "onTimerFinish")
}
}
// Start/Resume timer
timerExt.start()
// Pause timer
timerExt.pause()
// Restart timer
timerExt.restart()

You can try using a library I created, Hourglass
Hourglass hourglass = new Hourglass(50000, 1000) {
#Override
public void onTimerTick(long timeRemaining) {
// Update UI
Toast.show(MainActivity.this, String.valueOf(timeRemaining), Toast.LENGTH_SHORT).show();
}
#Override
public void onTimerFinish() {
// Timer finished
Toast.show(MainActivity.this, "Timer finished", Toast.LENGTH_SHORT).show();
}
};
Use hourglass.startTimer(); to start the timer.
It has helper methods which allow to pause and resume the timer.
hourglass.pauseTimer();
AND
hourglass.resumeTimer();

I have a simple solution. All you have to do is add an extra variable which stores the current time. The only addition is currentMillis at top and in onTick(). Use cancel() to pause.
PS: I'm using butterknife library, it's used to avoid using findviewbyid and setonclicklisteners. If you do not want to use it then you can just use the basic way of setting listeners and findviewbyid.
#BindView(R.id.play_btn) ImageButton play;
#BindView(R.id.pause_btn) ImageButton pause;
#BindView(R.id.close_btn) ImageButton close;
#BindView(R.id.time) TextView time;
private CountDownTimer countDownTimer;
private long currentMillis=10;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game);
ButterKnife.bind(this);
}
#OnClick(R.id.play_btn) void play_game(){
this.play();
}
#OnClick(R.id.pause_btn) void pause_game(){
this.pause();
}
#OnClick(R.id.close_btn) void close_game(){
this.close();
}
void play(){
play.setVisibility(View.GONE);
close.setVisibility(View.GONE);
pause.setVisibility(View.VISIBLE);
time.setText(""+currentMillis);
countDownTimer = new CountDownTimer(currentMillis*1000,1000) {
#Override
public void onTick(long millisUntilFinish) {
currentMillis=millisUntilFinish/1000;
time.setText(""+millisUntilFinish/1000);
}
#Override
public void onFinish() {
time.setText("Done!");
}
};
countDownTimer.start();
}
void pause(){
play.setVisibility(View.VISIBLE);
close.setVisibility(View.VISIBLE);
pause.setVisibility(View.GONE);
countDownTimer.cancel();
}
void close(){
if(countDownTimer!=null){
countDownTimer.cancel();
countDownTimer=null;
}
Intent intent = new Intent(this,MainActivity.class);
startActivity(intent);
}
}

Although the question doesn't include the Kotlin tag, I still think this answer belongs here due to Android tag.
I've made a very simple library that uses the CountDownTimer internally and inspired by this answer. So far, it works as intended with my very basic use case:
Pause the timer when the app/activity is paused, and resume when the app/activity resumes.
Usage:
class GameActivity : Activity() {
private var timer = CustomTimer(30000, 1000) // 30 seconds duration at 1 second interval
override fun onCreate(savedInstanceState: Bundle?) {
...
timer.onTick = { millisUntilFinished ->
textView.text = "seconds remaining: " + millisUntilFinished / 1000
}
timer.onFinish = {
TODO("do something here")
}
}
override fun onResume() {
super.onResume()
timer.resume()
}
override fun onPause() {
super.onPause()
timer.pause()
}
}
Source code:
Resumable CustomTimer.kt (50 lines)

public static void setTimerMillis(Context context, long millis)
{ SharedPreferences sp =
context.getSharedPreferences(SessionManager.FILE_USER, Context.MODE_PRIVATE);
SharedPreferences.Editor spe = sp.edit(); spe.putLong(SessionManager.TIMER_MILLIS, millis); spe.apply(); }
void setExamTimer() {
setTimerColors();
final long maxTimeToShow = 60000; //testing
final long lastTimeinMillisLeftSaved = TestyBookHelper.getTimerMillis(context); //take saved value from sharedpref
final long intervalTime = 1000;
donut_exam_timer.setMax((int) maxTimeToShow);
CountDownTimer countDownTimer = new CountDownTimer(lastTimeinMillisLeftSaved, intervalTime) {
#Override
public void onTick(long millis) {
TestyBookHelper.setTimerMillis(context, millis);
String hms = String.format("%02d:%02d:%02d", TimeUnit.MILLISECONDS.toHours(millis),
TimeUnit.MILLISECONDS.toMinutes(millis) - TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(millis)),
TimeUnit.MILLISECONDS.toSeconds(millis) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis))); System.out.println(hms);
donut_exam_timer.setText(hms);
donut_exam_timer.setProgress(millis);
}
#Override
public void onFinish() {
}
};
countDownTimer.start();
}
public static long getTimerMillis(Context context) {
SharedPreferences sp = context.getSharedPreferences(SessionManager.FILE_USER, Context.MODE_PRIVATE);
return sp.getLong(SessionManager.TIMER_MILLIS, 60000L);}

Related

handler.postDelayed not working correctly

I have a simple stopwatch code piece. Thread is running in custom class, it connects to the main activity via Interface
public class MainActivity extends AppCompatActivity implements MainActivityInteractionInterface{
public static boolean isRunning = false;
Stopwatch stopWatch;
private TextView textViewMilliSeconds;
private TextView textViewSeconds;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textViewMilliSeconds = findViewById(R.id.textViewStopwatchMilliseconds);
textViewSeconds = findViewById(R.id.textViewStopwatchSeconds);
stopWatch = new Stopwatch(this, getApplicationContext());
stopWatch.runThread();
}
#Override
public void updateUI() {
String time = String.format(Locale.getDefault(), "%03d", stopWatch.getMilliseconds());
textViewMilliSeconds.setText(time);
String timeSeconds = String.format(Locale.getDefault(), "%02d", stopWatch.getSeconds());
textViewSeconds.setText(timeSeconds);
}
public void startTimer(View view) {
isRunning = !isRunning;
}
public class Stopwatch {
private int milliseconds = 0;
private int seconds = 0;
public int getMilliseconds() {
return milliseconds;
}
public int getSeconds() {
return seconds;
}
private MainActivityInteractionInterface interactionInterface;
private Context applicationContext;
public Stopwatch(MainActivityInteractionInterface interactionInterface, Context applicationContext){
this.interactionInterface = interactionInterface;
this.applicationContext = applicationContext;
}
public void runThread(){
final Handler handler = new Handler();
handler.post(new Runnable(){
#Override
public void run(){
if(isRunning) {
milliseconds++;
if (milliseconds == 1000) {
milliseconds = 0;
seconds++;
if(seconds == 60){
seconds = 0;
}
}
}
interactionInterface.updateUI();
handler.postDelayed(this, 1);
}
});
}
handler should update every 1 millisec, when there is 1000 milliseconds, 1 second passes by
If I set handler.postDelayed delay anything below 15 reaching 1000 milliseconds would take exactly 18 seconds, why?
I don't know why it would take up to 18seconds, but I can tell you this: Android refresh the UI every 16msec (to have a rate of 60fps), so setting the handler to updateUI in a lesser time would make no sense and maybe also interfier with it.
In my humble opinion, make it to update in 20msec and change the counter values according, like this:
handler.post(new Runnable(){
#Override
public void run(){
if(isRunning) {
milliseconds++;
if (milliseconds == 50) {
milliseconds = 0;
seconds++;
if(seconds == 60){
seconds = 0;
}
}
}
interactionInterface.updateUI();
handler.postDelayed(this, 20);
}
});
Look at the second argument of handler.postDelayed(this, 1);
Change it according to the way you increment your milliseconds.

pause a reversed countdown timer in java for android

I'm making a timer which has a button I can control either pick the count-up mode or countdown mode. The count down mode is working perfectly, start, pause, reset. The count-up timer is made by reversing the countdown timer in Android studio. the problem is I can't pause the count-up timer and resume from where it stops.
What's the proper way to do that? I know it could be done by chronometer for count-up mode, but I'm trying to make a customized timer widget that as I mentioned before, a button could be pressed to let the user decide which mode he wants. So if possible, I don't want to use another widget in my current set up.
public void startBuiltInTimer(int millisecondCountingInterval) {
if (getTimeInMillis() > 0) { //getTimeInMillis will read the time value in the current Timer widget, which is a textView
builtInTimerIsCounting = true; //set the timer is running flag
showAsCounting(true);
if (getTimerCountUpOrDown() == TIMER_COUNT_DOWN) { //countdown mode
countDownTimer = new CountDownTimer(getTimeInMillis(), millisecondCountingInterval) {
#Override
public void onTick(long millisUntilFinished) {
setTime(millisUntilFinished);
}
}
#Override
public void onFinish() {
setTime(0);
builtInTimerIsCounting = false;
showAsCounting(false);
}
}
}.start();
} else if (getTimerCountUpOrDown() == TIMER_COUNT_UP) { //count up mode
long tempTimeInMillis = getTimeInMillis() + lastTimeMillisUntilFinished; //trying to save the last time value in millis and keep the total time value as user defined.
setTime(0); //set initial value of the textView to show the count-up timer start from zero, initially.
countDownTimer = new CountDownTimer(tempTimeInMillis, millisecondCountingInterval) {
#Override
public void onTick(long millisUntilFinished) {
long temp = tempTimeInMillis - millisUntilFinished;
setTime(temp); //reverse the countdown to count-up
lastTimeMillisUntilFinished = millisUntilFinished;//keep the millisUntilFinished for next start.
}
#Override
public void onFinish() {
setTime(tempTimeInMillis);
builtInTimerIsCounting = false;
showAsCounting(false);
}
}
}.start();
}
}
}
Edit:
I end up to use SystemClock to keep tracking and record the count up timer. Here is the code.
else if (getTimerCountUpOrDown() == TIMER_COUNT_UP) {
startCountUpTimer(millisecondCountingInterval);
}
public void startCountUpTimer(int millisecInterval) {
if (!countUpTimerIsRunning) {
handler = new Handler();
myRunnable = new MyRunnable(millisecInterval); //MyRunnable can pass a variable thru to let handler have more flexibility with postDelay method. You can still use regular Runnable with hard coded time interval in millisec.
startTime = SystemClock.uptimeMillis(); //use startTime to mark the time when the count up timer start ticking
handler.postDelayed(myRunnable, millisecInterval);
countUpTimerIsRunning = true;
}
}
public class MyRunnable implements Runnable {
private int interval;
public MyRunnable(int t) {
this.interval = t;
}
#Override
public void run() {
millisecondTime = SystemClock.uptimeMillis() - startTime; //this is the time value measured by the count up timer
updateTime = timeBuff + millisecondTime;
setTime(updateTime);
if (updateTime <= countUpMode_Threshold) {
handler.postDelayed(this, interval);
} else {
setTime(countUpMode_Threshold);
handler.removeCallbacks(myRunnable);
builtInTimerIsCounting = false;
showAsCounting(false);
if (onBuiltInTimerStatusChangeListener != null) {
onBuiltInTimerStatusChangeListener.onStatusChange(OnBuiltInTimerStatusChangeListener.STATUS_FINISHED);
}
}
}
}
public void pauseCountUpTimer() {
timeBuff += millisecondTime;
handler.removeCallbacks(myRunnable);
countUpTimerIsRunning = false;
}
One way is that you should keep track of the pause time manually and start timer from that time again. Or you can try using the utility at this link It has the functionality you are looking for.
Pretty helpful
Maybe something like this:
private CountDownTimer timer;
private long COUNT_DOWN = -1;
private long COUNT_UP = 1;
public void startBuiltInTimer(boolean isDown) {
if (isdown) {
launchTimer(COUNT_DOWN);
} else {
launchTimer(COUNT_UP);
}
}
public void launchTimer(long interval) {
builtInTimerIsCounting = true; //set the timer is running flag
showAsCounting(true);
timer = new CountDownTimer(getTimeInMillis(), TimeUnit.SECONDS.toMillis(interval) {
#Override
public void onTick(long time) {
setCountdownText(time.intValue());
setTimeInMillis(time);
}
#Override
public void onFinish() {
setCountdownText(time.intValue());
builtInTimerIsCounting = false;
showAsCounting(false);
}
}.start();
public void stopTimer() {
timer.cancel();
}
...

How to start a timer using another timer in Android Studio

I'm a novice in Java (Less than 3 months experience), and for a project I've been working on I need to create a timer.
I've done this before, however I do not know how to do one thing.
I want to start a timer when a second timer ends. What I mean by this is that instead of using a start/stop button to start a timer, I want to have a second timer (that starts at 3 seconds) determine when the first timer starts. For example, if the first timer is at 30 seconds, it will start counting down when the second timer finishes counting down from 3-0.
I know there has to be other classes or methods/listeners to do this, but as I've stated earlier, it's my first time ever working with Java (I normally use C++).
Any help/guidance/code on how to achieve this would be awesome. Here is the code I was toying around with to try and achieve this.
Java
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.os.CountDownTimer;
public class Timer extends AppCompatActivity
{
TextView timer;
TextView timerStart;
Button multi;
int track;
int seconds;
CountDownTimer countDownTimer;
CountDownTimer start;
View.OnClickListener btnListen = new View.OnClickListener(){
#Override
public void onClick(View v)
{
switch(v.getId())
{
case R.id.multi : start();
break;
}
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_timer);
timer = (TextView) findViewById(R.id.timer);
timerStart = (TextView) findViewById(R.id.timerStart);
multi = (Button) findViewById(R.id.multi);
multi.setOnClickListener(btnListen);
multi.setText("Start");
}
public void start_timer()
{
track = 3;
start = new CountDownTimer(3*1000,1000) {
#Override
public void onTick(long millisUntilFinished) {
timerStart.setText("" + millisUntilFinished / 1000);
}
#Override
public void onFinish() {
timerStart.setText("Begin");
track = 0;
}
}.start();
seconds = 30;
if (timerStart.getText().equals("Begin"))
{
start.cancel();
countDownTimer = new CountDownTimer(30 * 1000, 1000) {
#Override
public void onTick(long millisUntilFinished) {
timer.setText("" + millisUntilFinished / 1000);
}
#Override
public void onFinish() {
timer.setText("BEEP");
}
}.start();
}
else
{
System.out.println("Nothing");
}
}
public void start()
{
start_timer();
/*seconds = 30;
if (timerStart.getText().equals("Begin"))
{
countDownTimer = new CountDownTimer(seconds * 1000, 1000) {
#Override
public void onTick(long millisUntilFinished) {
timer.setText("" + millisUntilFinished / 1000);
}
#Override
public void onFinish() {
timer.setText("BEEP");
}
}.start();
}*/
}
}
Again, this is just something I'm toying around with. If there is a different way to do this (Like using a Runnable or Handler), then I'm open to it. My goal is to learn Java.
How about this? I modified CountDownTimer to enable to be chained.
public abstract class ChainedCountDownTimer {
/**
* Millis since epoch when alarm should stop.
*/
private final long mMillisInFuture;
/**
* The interval in millis that the user receives callbacks
*/
private final long mCountdownInterval;
private long mStopTimeInFuture;
/**
* boolean representing if the timer was cancelled
*/
private boolean mCancelled = false;
/**
* First timer in chaining
*/
private ChainedCountDownTimer first;
/**
* Next timer
*/
private ChainedCountDownTimer next;
/**
* #param millisInFuture The number of millis in the future from the call
* to {#link #start()} until the countdown is done and {#link #onFinish()}
* is called.
* #param countDownInterval The interval along the way to receive
* {#link #onTick(long)} callbacks.
*/
public ChainedCountDownTimer(long millisInFuture, long countDownInterval) {
mMillisInFuture = millisInFuture;
mCountdownInterval = countDownInterval;
first = this;
}
/**
* Cancel the countdown.
*/
public synchronized final void cancel() {
first.mCancelled = true;
mHandler.removeMessages(MSG);
}
public void start() {
first.startInternal();
}
/**
* Start the countdown.
*/
public synchronized final ChainedCountDownTimer startInternal() {
mCancelled = false;
if (mMillisInFuture <= 0) {
onFinish();
if (next != null) {
next.startInternal();
}
return this;
}
mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture;
mHandler.sendMessage(mHandler.obtainMessage(MSG));
return this;
}
/**
* Callback fired on regular interval.
* #param millisUntilFinished The amount of time until finished.
*/
public abstract void onTick(long millisUntilFinished);
/**
* Callback fired when the time is up.
*/
public abstract void onFinish();
public ChainedCountDownTimer setNext(ChainedCountDownTimer next) {
this.next = next;
next.first = this.first;
return this.next;
}
private static final int MSG = 1;
// handles counting down
private Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
synchronized (ChainedCountDownTimer.this) {
if (first.mCancelled) {
return;
}
final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();
if (millisLeft <= 0) {
onFinish();
if (next != null) {
next.startInternal();
}
} else if (millisLeft < mCountdownInterval) {
// no tick, just delay until done
sendMessageDelayed(obtainMessage(MSG), millisLeft);
} else {
long lastTickStart = SystemClock.elapsedRealtime();
onTick(millisLeft);
// take into account user's onTick taking time to execute
long delay = lastTickStart + mCountdownInterval - SystemClock.elapsedRealtime();
// special case: user's onTick took more than interval to
// complete, skip to next interval
while (delay < 0) delay += mCountdownInterval;
sendMessageDelayed(obtainMessage(MSG), delay);
}
}
}
};
}
You can use it like this.
ChainedCountDownTimer timer1 = new ChainedCountDownTimer(3 * 1000, 1000) {
#Override
public void onTick(long millisUntilFinished) {
Log.d(TAG, "timer1 onTick");
}
#Override
public void onFinish() {
Log.d(TAG, "timer1 onFinish");
}
};
ChainedCountDownTimer timer2 = new ChainedCountDownTimer(30 * 1000, 1000) {
#Override
public void onTick(long millisUntilFinished) {
Log.d(TAG, "timer2 onTick");
}
#Override
public void onFinish() {
Log.d(TAG, "timer2 onFinish");
}
};
timer1.setNext(timer2).start();

synchronized countdown timer- android development

I have an app that has one countdown timer that should show up the same for every user when they open the app. In order to do this, I have based the time that the users' phones show on Epoch time. I do the following calculations to (what I thought would...) ensure that each phone shows the same time, and that the countdown clock is continuous and accurate. However, every time I open the app up, the clock is at a totally different time, when I think it should be continuously counting down and resetting. What's wrong? I have included my code below:
private static final int COUNTDOWN_DURATION = 30; //time in seconds
private static final long BASE_TIME = 1470729402L; //an arbitrary Epoch time that I have picked as a reference point
private TextView tvTimer;
private Long currentTimeMillis;
private int finalTime;
private boolean firstTime;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState)
//set up basics
...
//set up timer
tvTimer = (TextView) findViewById(R.id.tvTimer);
firstTime = true;
setCurrentTime();
}
private void setCurrentTime() {
currentTimeMillis = System.currentTimeMillis();
long currentTimeSecs = currentTimeMillis/1000;
long timeDiff = currentTimeSecs - BASE_TIME;
//determines what spot the countdown timer is at when the app is started
finalTime = (int) (timeDiff % COUNTDOWN_DURATION);
resetTimer();
}
public void resetTimer(){
if (firstTime) {
CountDownTimer countDownTimer = new CountDownTimer(finalTime * 1000, 1000) {
public void onTick(long millisUntilFinished) {
tvTimer.setText(" " + millisUntilFinished / 1000 + " ");
}
public void onFinish() {
resetTimer();
}
};
countDownTimer.start();
firstTime = false;
}
else {
CountDownTimer countDownTimer = new CountDownTimer(COUNTDOWN_DURATION * 1000, 1000) {
public void onTick(long millisUntilFinished) {
tvTimer.setText(" " + millisUntilFinished / 1000 + " ");
}
public void onFinish() {
resetTimer();
}
};
countDownTimer.start();
}
}

sequential countdowntimer with handler wont update textView correctly

I was trying to build some sort of sequential countdown. Meaning, that I build up a queue of "exercises", each one containing a specific duration, which is the countdown time. In a custom Countdown class, I pop these exercises off the queue and use the duration as countdown.
I want these countdowns to run one after another. For this I built a Countdown class, based on the code basis of the abstract class CountDownTimer.
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Locale;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.widget.Button;
import android.widget.TextView;
public class ExerciseMeCountDownTimer {
private static final int MSG_COUNTDOWN = 100;
private static final int MSG_FINISH = 99;
private ArrayDeque<Exercise> eq;
private long mMillisInFuture;
private int mCountdownInterval;
private String name;
private long mStopTimeInFuture;
CountdownHandler cHandler;
public ExerciseMeCountDownTimer(ArrayList<Exercise> elist,
Button startStopButton, TextView countdownText,
CountdownHandler cHandler) {
this.cHandler = cHandler;
eq = new ArrayDeque<Exercise>(elist);
this.start();
}
public final void cancel() {
mHandler.removeMessages(MSG);
}
private synchronized final ExerciseMeCountDownTimer start() {
if (!eq.isEmpty()) {
Exercise e = eq.pop();
this.mMillisInFuture = Long.parseLong(e.getDuration());
this.mCountdownInterval = 30;
this.name = e.getName();
} else {
onFinish();
return this;
}
if (mMillisInFuture <= 0) {
onFinish();
return this;
}
mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture;
mHandler.sendMessage(mHandler.obtainMessage(MSG));
return this;
}
public void onTick(long millisUntilFinished) {
Message msg = cHandler.obtainMessage(MSG_COUNTDOWN);
Bundle data = new Bundle();
String text = String.format(Locale.GERMANY, "%02d:%02d:%03d",
millisUntilFinished / 100000, millisUntilFinished / 1000,
millisUntilFinished % 1000);
data.putString("countdown", text);
msg.setData(data);
cHandler.sendMessage(msg);
}
public void onFinish() {
if (!eq.isEmpty()) {
this.start();
}
Message msg = cHandler.obtainMessage(MSG_FINISH);
Bundle data = new Bundle();
String text = String.format(Locale.GERMANY, "00:00:000");
data.putString("finish", text);
msg.setData(data);
cHandler.sendMessage(msg);
}
private static final int MSG = 1;
// handles counting down
private Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
final long millisLeft = mStopTimeInFuture
- SystemClock.elapsedRealtime();
if (millisLeft <= 0) {
onFinish();
} else if (millisLeft < mCountdownInterval) {
// no tick, just delay until done
sendMessageDelayed(obtainMessage(MSG), millisLeft);
} else {
long lastTickStart = SystemClock.elapsedRealtime();
onTick(millisLeft);
// take into account user's onTick taking time to
// execute
long delay = lastTickStart + mCountdownInterval
- SystemClock.elapsedRealtime();
// special case: user's onTick took more than interval
// to
// complete, skip to next interval
while (delay < 0)
delay += mCountdownInterval;
sendMessageDelayed(obtainMessage(MSG), delay);
}
}
};
#Override
public String toString() {
return this.name;
}
}
The important part is the sendMessage part, where I send the time left on the countdown to a handler of my MainActivity, which then, should update a textview.
import android.os.Handler;
import android.os.Message;
class CountdownHandler extends Handler {
private static final int MSG_COUNTDOWN = 100;
private static final int MSG_FINISH = 99;
private MainActivity mActivity;
CountdownHandler(MainActivity activity) {
this.mActivity = activity;
}
#Override
public void handleMessage(Message msg) {
if (msg.what == MSG_COUNTDOWN) {
String text = msg.getData().getString("countdown");
this.mActivity.sayLog(text);
}
if (msg.what == MSG_FINISH) {
String text = msg.getData().getString("finish");
this.mActivity.sayLog(text);
}
}
And finally updates the textView in MainActivty
public void sayLog(String text) {
countdown.setText(text);
}
ExerciseMeCountDownTimer is called by
new ExerciseMeCountDownTimer(elist, cHandler);
on some onClick().
The problem is, that sometimes (actually most of the time) the textView is not updated properly. It stops updating on random times like 00:05:211 etc.
Would anyone mind telling me why this is keeps happening? Maybe also adding a solution or at least some literature (maybe pointing out some sections) which I should read to understand the problem? I am also upen for alternative approaches, as I am new to this "handler", "threads" thing in android.
EDIT
the textview was updating, but the textview was clickable. Whenever I clicked on the textview it stopped updating!
as the accepted answer shows, I decided to use the direkt approach of updating the appropriate textview inside the onTick() method.
Using Handler and things in this situation is making it overly complicated.
CountDownTimers onTick() and onFinish() both run on the UI Thread so updating TextViews and other Views from either method can be done easily just by passing a reference of the View to the constructor of the class, as you are already doing. Then you simply update it in the method needed.
// could create a member variable for the TextView with your other member variables
...
mTV;
then in your constructor assign it
// removed reference to Handler--you already have reference to TextView here
public ExerciseMeCountDownTimer(ArrayList elist,
Button startStopButton, TextView countdownText) {
mTV = countdownText;
then update in whichever method is needed
public void onTick(long millisUntilFinished) {
String text = String.format(Locale.GERMANY, "%02d:%02d:%03d",
millisUntilFinished / 100000, millisUntilFinished / 1000,
millisUntilFinished % 1000);
mTV.setText(text); // set the text here
}
public void onFinish() {
if (!eq.isEmpty()) {
this.start();
}

Categories