Working example of countdown timer with smooth progress bar - android

I've seen many similar questions but I can't find any code that actually works so please modify this example so the progress bar moves smoothly.
activity_main.xml
I've seen some answers that increase the max and reduce the time interval but I don't see it working properly. There are other answers that use the animator class and work fine but then I don't know how to update the numbers.
package com.example.arturo_2.countdowntimertutorial;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.os.CountDownTimer;
import java.util.concurrent.TimeUnit;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private long timeCountInMilliSeconds = 1 * 60 * 1000;
private ProgressBar progressBarCircle;
private EditText editTextMinute;
private TextView textViewTime;
private Button buttonDecrease;
private Button buttonIncrease;
private Button buttonStop;
private CountDownTimer countDownTimer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// method call to initialize the views
initViews();
// method call to initialize the listeners
initListeners();
start();
}
/**
* method to initialize the views
*/
private void initViews() {
progressBarCircle = (ProgressBar) findViewById(R.id.progressBarCircle);
editTextMinute = (EditText) findViewById(R.id.editTextMinute);
textViewTime = (TextView) findViewById(R.id.textViewTime);
buttonDecrease = (Button) findViewById(R.id.buttonDecrease);
buttonIncrease = (Button) findViewById(R.id.buttonIncrease);
buttonStop = (Button) findViewById(R.id.fab_stop);
}
/**
* method to initialize the click listeners
*/
private void initListeners() {
buttonDecrease.setOnClickListener(this);
buttonIncrease.setOnClickListener(this);
buttonStop.setOnClickListener(this);
}
/**
* method to increase timer value
*/
private void increase() {
timeCountInMilliSeconds += 15 * 1000;
setProgressBarValues();
}
/**
* method to decrease timer value
*/
private void decrease() {
timeCountInMilliSeconds -= 15 * 1000;
setProgressBarValues();
}
/**
* method to cancel count down timer
*/
private void stop() {
countDownTimer.cancel();
}
/**
* implemented method to listen clicks
*
* #param view
*/
#Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.buttonDecrease:
decrease();
break;
case R.id.buttonIncrease:
increase();
break;
case R.id.fab_stop:
stop();
break;
}
}
/**
* method to initialize the values for count down timer
*/
private void setTimerValues() {
int time = 0;
if (!editTextMinute.getText().toString().isEmpty()) {
// fetching value from edit text and type cast to integer
time = Integer.parseInt(editTextMinute.getText().toString().trim());
}
// assigning values after converting to milliseconds
timeCountInMilliSeconds = time * 60 * 1000;
setProgressBarValues();
}
/**
* method to set circular progress bar values
*/
private void setProgressBarValues() {
progressBarCircle.setMax((int) timeCountInMilliSeconds / 1000);
progressBarCircle.setProgress((int) timeCountInMilliSeconds / 1000);
}
/**
* method to start count down timer
*/
private void start() {
setTimerValues();
startCountDownTimer();
}
/**
* method to start count down timer
*/
private void startCountDownTimer() {
countDownTimer = new CountDownTimer(timeCountInMilliSeconds, 1000) {
#Override
public void onTick(long millisUntilFinished) {
textViewTime.setText(hmsTimeFormatter(millisUntilFinished));
progressBarCircle.setProgress((int) (millisUntilFinished / 1000));
}
#Override
public void onFinish() {
textViewTime.setText(hmsTimeFormatter(timeCountInMilliSeconds));
setProgressBarValues(); // call to initialize the progress bar values
}
}.start();
countDownTimer.start();
}
/**
* method to convert millisecond to time format
*
* #param milliSeconds
* #return mm:ss time formatted string
*/
private String hmsTimeFormatter(long milliSeconds) {
String hms = String.format("%02d:%02d",
TimeUnit.MILLISECONDS.toMinutes(milliSeconds),
TimeUnit.MILLISECONDS.toSeconds(milliSeconds) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(milliSeconds)));
return hms;
}
}

MainActivity.java, activity_main.xml
private void setProgressBarValues() {
progressBarCircle.setMax((int) timeCountInMilliSeconds / 50);
progressBarCircle.setProgress((int) timeCountInMilliSeconds / 1000);
}
private void startCountDownTimer() {
countDownTimer = new CountDownTimer(timeCountInMilliSeconds, 50) {
#Override
public void onTick(long millisUntilFinished) {
textViewTime.setText(hmsTimeFormatter(millisUntilFinished));
progressBarCircle.setProgress((int) (millisUntilFinished / 50));
}
#Override
public void onFinish() {
textViewTime.setText(hmsTimeFormatter(timeCountInMilliSeconds));
setProgressBarValues(); // call to initialize the progress bar values
}
}.start();
countDownTimer.start();
}

Related

countdown timer in childactivity

I making an app in which i need to run Countdown timer in a Child Activity. How can i keep running timer and keep track of time when i go back to main activity(or press 'back') ?
play.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(!isRunning){
timerTextHelper.start();
isRunning =true;
}else {
timerTextHelper.stop();
long elapsedTime = timerTextHelper.getElapsedTime();
Log.v("TAG","TIME: "+ elapsedTime/1000);
isRunning =false;
I have tried using Runnable
#Override
public void run() {
long millis = System.currentTimeMillis() - startTime;
int seconds = (int) (millis / 1000);
int minutes = seconds / 60;
seconds = seconds % 60;
textView.setText(String.format("%d:%02d", minutes, seconds));
if (elapsedTime == -1) {
handler.postDelayed(this, 500);
}
}
public void start() {
this.startTime = System.currentTimeMillis();
this.elapsedTime = -1;
handler.post(this);
}
public void stop() {
this.elapsedTime = System.currentTimeMillis() - startTime;
handler.removeCallbacks(this);
}
public long getElapsedTime() {
return elapsedTime;
Implement the below class:
public class OtpTimer extends CountDownTimer {
/**
* #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.
*/
private OtpTimer(long millisInFuture, long countDownInterval) {
super(millisInFuture, countDownInterval);
}
private static OtpTimer otpTimer;
private static TextView otp_text;
private static long sec,min;
/**
*
* #param millisInFuture total time of timer
* #param countDownInterval callback time of {#link #onTick(long)}
* #param textView instace of textview to show time in text view
*/
public static synchronized void getInstance(long millisInFuture, long countDownInterval, TextView textView)
{
if(otpTimer==null) {
otpTimer = new OtpTimer(millisInFuture, countDownInterval);
otpTimer.start();
}
otp_text=textView;
if(min>=1 && sec>=10)
otp_text.setText("0"+String.valueOf(min)+":"+String.valueOf(sec));
else if(min>=1 && sec<=10)
{
otp_text.setText("0"+String.valueOf(min)+":"+"0"+String.valueOf(sec));
}
else if(sec>=10)
otp_text.setText("00"+":"+String.valueOf(sec));
else if(sec==0)
otp_text.setText("Start again");
else
otp_text.setText("00"+":"+"0"+String.valueOf(sec));
}
#Override
public void onTick(long millisUntilFinished) {
sec=millisUntilFinished/1000;
min=sec/60;
sec=sec%60;
if(min>=1 && sec>=10)
otp_text.setText("0"+String.valueOf(min)+":"+String.valueOf(sec));
else if(min>=1 && sec<=10)
{
otp_text.setText("0"+String.valueOf(min)+":"+"0"+String.valueOf(sec));
}
else if(sec>=10)
otp_text.setText("00"+":"+String.valueOf(sec));
else
otp_text.setText("00"+":"+"0"+String.valueOf(sec));
}
#Override
public void onFinish() {
sec=0;
otp_text.setText("Start again");
}
public static void Restart(long millisInFuture, long countDownInterval, TextView textView,Boolean voice,Boolean contact){
if(otpTimer!=null)
{
otpTimer.cancel();
otpTimer=null;
}
getInstance(millisInFuture, countDownInterval,textView,voice,contact);
}
public static void setNull()
{
if(otpTimer!=null)
{
otpTimer.cancel();
otpTimer=null;
}
}}
Now to start the timer, simply call the below line in the onCreate and onResume :
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
OtpTimer.getInstance(30000,1000,m_otp_timer);
}
#Override
protected void onResume() {
invalidateOptionsMenu();
super.onResume();
OtpTimer.getInstance(30000,1000,m_otp_timer);
}
Here 30sec timer and count will increase by 1sec and m_otp_timer is the textview which will display the count.
And to restart the timer simply call:
OtpTimer.Restart(30000, 1000, m_otp_timer);
create a broadCastReceiver and register it to your manifest
<receiver
android:name=".receivers.TimerReceiver">
<intent-filter>
<action android:name="YOUR.PACKAGE.NAME.action.RECEIVER_TIMER" />
</intent-filter>
</receiver>
the action is used to send intent to only your receiver
and create a class for your Receiver
class TimerReceiver : BroadcastReceiver(val callback:Callback) {
override fun onReceive(context: Context?, data: Intent?) {
callback.onNewTime(data.getIntExtra("min",0),data.getIntExtra("sec",0))
}
interface Callback {
fun onNewTime(int min,int sec)
}
}
then in your activity's body create an instance of your receiver
TimerReceiver receiver;
then in your activity's onCreate() make your object
receiver = TimerReceiver(this)
then in your activity's onStart register your receiver
registerReceiver(receiver,IntentFilter("YOUR.PACKAGE.NAME.acrion.RECEIVER_TIMER"))
and in your activity's onStop unregister your receiver
unregisterReceiver(receiver)
and at the end implement the TimerReceiver.Callback methods in your activity
override fun onNewTime(min:Int,sec:Int){
textView.setText(String.format("%d:%02d", min, sec));
}
so you are ready and in your TimeTask when you want to update timer sendBroadCast and put min and sec into intent.

Pause/Resume CountDownTimer Android

Is it possible to pause a CountDownTimer in Android? I have been looking for good solutions but I just find some ways to do this that I really don't like. As just save the left time in a variable and initialize a new CountDownTimer with that values.
That kind of solutions work but they didn't look so good because I´m using a circle Progress bar and a Textview together with my countdownTimer. Was really ugly try to look this two look good with the CountDown without be able to really "pause" it.
Here is my code for initialize the CountDownTimer with a ProgressBar and a TextView.
public void initProgress() {
if (mCountdownProgressBar == null)
mCountdownProgressBar = (CircleProgressBar) findViewById(R.id.progressBar);
mCountDownTime = 30000; //Insert your desire time in Milliseconds here
mCountdownProgressBar.setMaxProgress((int)TimeUnit.MILLISECONDS.toSeconds(mCountDownTime));
mCountDownTimer = new CustomCountDownTimer(mCountDownTime, 1000) {
#Override
public void onTick(long millisUntilFinished) {
Log.v("Log_tag", "Tick of Progress" + TimeUnit.MILLISECONDS.toSeconds(millisUntilFinished));
mCountdownProgressBar.setmProgress(TimeUnit.MILLISECONDS.toSeconds(millisUntilFinished));
mTimer.setText(Util.getTimeForTimer(millisUntilFinished, Util.TIME_FORMAT));
}
#Override
public void onFinish() {
mCountdownProgressBar.setmProgress(0);
mTimer.setText(Util.getTimeForTimer(0, Util.TIME_FORMAT));
}
};
mCountDownTimer.start();
}
With this code you will be able to set a progressBar and a TextView together with your CountDownTimer. To be able to pause and resume it pretty easy I will post next a Custom Class for CountDownTimer.
Here is the solution!
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.util.Log;
public abstract class CustomCountDownTimer {
private final String TAG = "CustomCountDownTimer";
/**
* Millis since epoch when alarm should stop.
*/
private final long mMillisInFuture;
/**
* The interval in millis that the user receives callbacks
*/
private final long mCountdownInterval;
/**
* The time in millis when the timer was paused
*/
private long mTimePaused;
/**
* The final time when the timer must to stop (actual hour + countdown in millis)
*/
private long mStopTimeInFuture;
/**
* boolean representing if the timer was cancelled
*/
private boolean mCancelled = false;
/**
* boolean representing if the timer is paused
*/
private boolean mPause = false;
/**
* #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 CustomCountDownTimer(long millisInFuture, long countDownInterval) {
mMillisInFuture = millisInFuture;
mCountdownInterval = countDownInterval;
}
/**
* Cancel the countdown.
*/
public synchronized final void cancel() {
mCancelled = true;
mHandler.removeMessages(MSG);
}
/**
* Pause the countdown.
*/
public synchronized final void pause() {
cancel();
//Save the time and hour to resume the timer correctly later.
mTimePaused = SystemClock.elapsedRealtime();
mPause = true;
}
/**
* Resume the countdown.
*/
public synchronized final void resume() {
//Booleans back to false value
mPause = false;
mCancelled = false;
//We set the time to a new one because the elapsedTime as change
mStopTimeInFuture = mStopTimeInFuture + (SystemClock.elapsedRealtime() - mTimePaused);
Log.d(TAG, "mStopTimeInFuture: " + mStopTimeInFuture);
mHandler.sendMessage(mHandler.obtainMessage(MSG));
}
/**
* Start the countdown.
*/
public synchronized final CustomCountDownTimer start() {
mCancelled = false;
mPause = false;
if (mMillisInFuture <= 0) {
onFinish();
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();
private static final int MSG = 1;
// handles counting down
private Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
synchronized (CustomCountDownTimer.this) {
if (mCancelled || mPause) {
return;
}
final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();
Log.d(TAG, "millisLeft: " + millisLeft);
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);
}
}
}
};
}

Passing value of EditText to service

I have a simple Activity which creates a foreground service. I would like to pass value from EditText to Service as the amount to which the screen should be dimmed. The MainActivity looks like this:
package com.mycompany.myapp;
import android.app.*;
import android.content.*;
import android.net.*;
import android.net.wifi.*;
import android.os.*;
import android.view.*;
import android.view.View.*;
import android.widget.*;
import android.provider.*;
import android.text.*;
public class MainActivity extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final EditText edDim = (EditText) findViewById(R.id.DimVal);
edDim.setInputType(InputType.TYPE_CLASS_NUMBER);
Button btnStart =(Button) findViewById(R.id.button1);
btnStart.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v){
Intent intent = new Intent(getBaseContext(), MyService.class);
intent.putExtra("DimVal",edDim.getText());
startService(intent);
}
});
Button btnStop =(Button) findViewById(R.id.button2);
btnStop.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v){
stopService(new Intent(getBaseContext(), MyService.class));
}
});
}
}
This is the service:
package com.mycompany.myapp;
import android.app.*;
import android.content.*;
import android.net.*;
import android.os.*;
import android.provider.*;
import android.view.*;
import android.widget.*;
public class MyService extends Service
{
public int timeVal;
#Override
public IBinder onBind(Intent p1)
{
// TODO: Implement this method
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
// TODO: Implement this method
Toast.makeText(this,"Usługa uruchomiona",Toast.LENGTH_LONG).show();
timeVal = intent.getIntExtra("DimVal",100);
this.registerReceiver(this.mComnReceiver,
new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
Notification note = new Notification(0, null,
System.currentTimeMillis());note.flags = Notification.FLAG_NO_CLEAR;
this.startForeground(1,note);
return START_STICKY;
}
/*#Override
public void onDestroy()
{
// TODO: Implement this method
super.onDestroy();
Toast.makeText(this,"Usługa zatrzymana",Toast.LENGTH_LONG).show();
}*/
private BroadcastReceiver mComnReceiver = new BroadcastReceiver(){
#Override
public void onReceive(Context p1, Intent p2)
{
ConnectivityManager manager = (ConnectivityManager)
getSystemService(CONNECTIVITY_SERVICE);
boolean isWiFi = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).isConnectedOrConnecting();
//NetworkInfo currentNetworkInfo = (NetworkInfo)
//p2.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
if(isWiFi){
Settings.System.putInt(getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, timeVal);
}else{
Settings.System.putInt(getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, 300);
}
}
};
}
But the above code does not work. Also how can I make the service check if the WiFi is on every 5 minutes? I realize that this code may not be the best solution of the problem but this is the first Android program I've written (thus the commented code).
Thank you in advance.
public void timerForWifiCheck(long intervalVal, long countVal) {
new SCBP_TimerClass(intervalVal, countVal) {
#Override
public void onTick(long tickTime) {
//Wifi checking code write here
if(SCBP_NetworkUtil.checkInternetConnection(Context)){
//Do what you wnat e.g
finish()
}else{
//Return with some error code or msg
}
}
}
#Override
public void onFinish() {
//code when the timer finshes
}
}.start();
}
For this You need to add the below two classes:
public abstract class TimerClass {
/**
* 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;
private long mPauseTime;
private boolean mCancelled = false;
private boolean mPaused = false;
/**
* #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 SCBP_TimerClass(long millisInFuture, long countDownInterval) {
mMillisInFuture = millisInFuture;
mCountdownInterval = countDownInterval;
}
/**
* Cancel the countdown.
*
* Do not call it from inside CountDownTimer threads
*/
public final void cancel() {
mHandler.removeMessages(MSG);
mCancelled = true;
}
/**
* Start the countdown.
*/
public synchronized final SCBP_TimerClass start() {
if (mMillisInFuture <= 0) {
onFinish();
return this;
}
mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture;
mHandler.sendMessage(mHandler.obtainMessage(MSG));
mCancelled = false;
mPaused = false;
return this;
}
/**
* Pause the countdown.
*/
public long pause() {
mPauseTime = mStopTimeInFuture - SystemClock.elapsedRealtime();
mPaused = true;
return mPauseTime;
}
/**
* Resume the countdown.
*/
public long resume() {
mStopTimeInFuture = mPauseTime + SystemClock.elapsedRealtime();
mPaused = false;
mHandler.sendMessage(mHandler.obtainMessage(MSG));
return mPauseTime;
}
/**
* 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 (SCBP_TimerClass.this) {
if (!mPaused) {
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;
if (!mCancelled) {
sendMessageDelayed(obtainMessage(MSG), delay);
}
}
}
}
}
};
}
public class NetworkUtil {
public static boolean checkInternetConnection(Context context) {
ConnectivityManager con_manager = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
if (con_manager.getActiveNetworkInfo() != null
&& con_manager.getActiveNetworkInfo().isAvailable()
&& con_manager.getActiveNetworkInfo().isConnected()) {
return true;
} else {
return false;
}
}
}
Hope this will help you to some extent.Cheers!
I see issue is in this line of code
intent.putExtra("DimVal",edDim.getText());
Here you should do as
intent.putExtra("DimVal",Integer.valueOf(edDim.getText().toString));
EditText getText i.e. edDim.getText() will give you Editable not the value inside. Also as you will do edDim.getText().toString will give you String value which you require to change to Integer before passing.
Note: You require to put null and check validation for edDim before passing value in intent; else it may give you runtimeerror.
Regarding the checking of WiFi it would be nice to create a broadcast receiver which receives updates for connection check and in that if connectivity is there then you can check whether it is Wifi or Mobile data and return false if mobile data. This way you do not require to check every five minutes which may lead to battery drainage.
You can check this android developer post for Determining and Monitoring the Connectivity Status.

Pause CountDownTimer in Android when activity is not in front

I have an activity that uses a CountDownTimer that counts down from 10. How do I pause that timer when the activity is no longer in focus, like if the user get a call or something, then resume the timer when the user goes back to the activity? Is this even possible?
I would add something to the onTick handler to save the progress of the timer in your class (number of milliseconds left).
In the onPause() method for the activity call cancel() on the timer.
In the onResume() method for the activity create a new timer with the saved number of milliseconds left.
You can use pause() to pause the timer and later on Start or Resume the countDownTimer by calling start().
/**
* This class uses the native CountDownTimer to
* create a timer which could be paused and then
* started again from the previous point. You can
* provide implementation for onTick() and onFinish()
* then use it in your projects.
*/
public abstract class CountDownTimerPausable {
long millisInFuture = 0;
long countDownInterval = 0;
long millisRemaining = 0;
CountDownTimer countDownTimer = null;
boolean isPaused = true;
public CountDownTimerPausable(long millisInFuture, long countDownInterval) {
super();
this.millisInFuture = millisInFuture;
this.countDownInterval = countDownInterval;
this.millisRemaining = this.millisInFuture;
}
private void createCountDownTimer(){
countDownTimer = new CountDownTimer(millisRemaining,countDownInterval) {
#Override
public void onTick(long millisUntilFinished) {
millisRemaining = millisUntilFinished;
CountDownTimerPausable.this.onTick(millisUntilFinished);
}
#Override
public void onFinish() {
CountDownTimerPausable.this.onFinish();
}
};
}
/**
* 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();
/**
* Cancel the countdown.
*/
public final void cancel(){
if(countDownTimer!=null){
countDownTimer.cancel();
}
this.millisRemaining = 0;
}
/**
* Start or Resume the countdown.
* #return CountDownTimerPausable current instance
*/
public synchronized final CountDownTimerPausable start(){
if(isPaused){
createCountDownTimer();
countDownTimer.start();
isPaused = false;
}
return this;
}
/**
* Pauses the CountDownTimerPausable, so it could be resumed(start)
* later from the same point where it was paused.
*/
public void pause()throws IllegalStateException{
if(isPaused==false){
countDownTimer.cancel();
} else{
throw new IllegalStateException("CountDownTimerPausable is already in pause state, start counter before pausing it.");
}
isPaused = true;
}
public boolean isPaused() {
return isPaused;
}
}
No need to create a new Timer, just set the millisUntilFinished = total. For instance
private CountDownTimer cdTimer;
private long total = 30000;
...
toggleButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view){
if(toggleButton.isChecked()) {
startCountDownTimer();
}else{
cdTimer.cancel();
}
}
});
...
private void startCountDownTimer() {
cdTimer = new CountDownTimer(total, 1000) {
public void onTick(long millisUntilFinished) {
//update total with the remaining time left
total = millisUntilFinished;
nTimeLabel.setText("seconds remaining: " + millisUntilFinished/ 1000);
}
public void onFinish() {
nTimeLabel.setText("done!");
}
}.start();
}
This must be exactly what you're looking for. Source is this Gist.
package alt.android.os;
import android.os.Handler;
import android.os.SystemClock;
import android.os.Message;
public abstract class CountDownTimer {
/**
* 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;
private long mPauseTime;
private boolean mCancelled = false;
private boolean mPaused = false;
/**
* #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 CountDownTimer(long millisInFuture, long countDownInterval) {
mMillisInFuture = millisInFuture;
mCountdownInterval = countDownInterval;
}
/**
* Cancel the countdown.
*
* Do not call it from inside CountDownTimer threads
*/
public final void cancel() {
mHandler.removeMessages(MSG);
mCancelled = true;
}
/**
* Start the countdown.
*/
public synchronized final CountDownTimer start() {
if (mMillisInFuture <= 0) {
onFinish();
return this;
}
mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture;
mHandler.sendMessage(mHandler.obtainMessage(MSG));
mCancelled = false;
mPaused = false;
return this;
}
/**
* Pause the countdown.
*/
public long pause() {
mPauseTime = mStopTimeInFuture - SystemClock.elapsedRealtime();
mPaused = true;
return mPauseTime;
}
/**
* Resume the countdown.
*/
public long resume() {
mStopTimeInFuture = mPauseTime + SystemClock.elapsedRealtime();
mPaused = false;
mHandler.sendMessage(mHandler.obtainMessage(MSG));
return mPauseTime;
}
/**
* 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 (CountDownTimer.this) {
if (!mPaused) {
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;
if (!mCancelled) {
sendMessageDelayed(obtainMessage(MSG), delay);
}
}
}
}
}
};
}
You can try using 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();
Here is the code below .Use it in your activities it works fine.
public class MainActivity extends AppCompatActivity {
TextView textview;
final static long INTERVAL = 1000;
final static long TIMEOUT = 11000;
static long millisecondsleft;
boolean isPause =false;
CountDownTimer countDownTimer;
CountDownTimer countDownTimeronResume;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textview=findViewById(R.id.textviewcheck);
}
#Override
protected void onResume() {
super.onResume();
if(isPause == false) {
countDownTimer = new CountDownTimer(TIMEOUT, INTERVAL) {
#Override
public void onTick(long millisUntilFinished) {
millisecondsleft = millisUntilFinished;
textview.setText(":" + String.format("%02d", millisUntilFinished / 1000));
}
#Override
public void onFinish() {
textview.setText("DONE!");
}
}.start();
} else{
countDownTimeronResume = new CountDownTimer(millisecondsleft, INTERVAL) {
#Override
public void onTick(long millisUntilFinished) {
Toast.makeText(MainActivity.this, "ONPAUSED", Toast.LENGTH_SHORT).show();
millisecondsleft = millisUntilFinished;
textview.setText(":" + String.format("%02d", millisUntilFinished / 1000));
}
#Override
public void onFinish() {
textview.setText("DONE!");
}
}.start();
}
}
#Override
protected void onPause() {
super.onPause();
if(countDownTimer!=null){
countDownTimer.cancel();
isPause = true;
}
if(countDownTimeronResume!=null){
countDownTimeronResume.cancel();
}
}
}
For Kotlin user, checkout this
For 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()

Android: chronometer as a persistent stopwatch. How to set starting time? What is Chronometer "Base"?

I do have one service running in the background. Whenever it starts I store in memory the starting time in milliseconds:
startingTime = new Date().getTime();
I want to display a chronometer that starts counting when the service starts and never stops until the user presses a button. I want to allow the user to leave the activity rendering the chronometer, do some stuff and then return. But the idea is that when the user returns I dont want the chronometer to go to 0:00 again. Insted I want it to show the exact time that has passed ever since the service has started.
I can calculate elapsedTime every time the user return to the chronometer activity:
elapsedTime = new Date().getTime() - startingTime;
The thing is that i dont know how to tell the chronometer to start counting from that time!
Setting it as the chronometer base does not work. Can someon explain what exactly "base" means or how to accomplish this?
thanks a lot!
BYE
You can use Chronometer.
You should also check this thread.
EDIT: The solution:
public class ChronoExample extends Activity {
Chronometer mChronometer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LinearLayout layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
mChronometer = new Chronometer(this);
// Set the initial value
mChronometer.setText("00:10");
layout.addView(mChronometer);
Button startButton = new Button(this);
startButton.setText("Start");
startButton.setOnClickListener(mStartListener);
layout.addView(startButton);
Button stopButton = new Button(this);
stopButton.setText("Stop");
stopButton.setOnClickListener(mStopListener);
layout.addView(stopButton);
Button resetButton = new Button(this);
resetButton.setText("Reset");
resetButton.setOnClickListener(mResetListener);
layout.addView(resetButton);
setContentView(layout);
}
private void showElapsedTime() {
long elapsedMillis = SystemClock.elapsedRealtime() - mChronometer.getBase();
Toast.makeText(ChronoExample.this, "Elapsed milliseconds: " + elapsedMillis,
Toast.LENGTH_SHORT).show();
}
View.OnClickListener mStartListener = new OnClickListener() {
public void onClick(View v) {
int stoppedMilliseconds = 0;
String chronoText = mChronometer.getText().toString();
String array[] = chronoText.split(":");
if (array.length == 2) {
stoppedMilliseconds = Integer.parseInt(array[0]) * 60 * 1000
+ Integer.parseInt(array[1]) * 1000;
} else if (array.length == 3) {
stoppedMilliseconds = Integer.parseInt(array[0]) * 60 * 60 * 1000
+ Integer.parseInt(array[1]) * 60 * 1000
+ Integer.parseInt(array[2]) * 1000;
}
mChronometer.setBase(SystemClock.elapsedRealtime() - stoppedMilliseconds);
mChronometer.start();
}
};
View.OnClickListener mStopListener = new OnClickListener() {
public void onClick(View v) {
mChronometer.stop();
showElapsedTime();
}
};
View.OnClickListener mResetListener = new OnClickListener() {
public void onClick(View v) {
mChronometer.setBase(SystemClock.elapsedRealtime());
showElapsedTime();
}
};
}
To start the chronometer you should use the setBase() method of your chronometer with SystemClock.elapsedRealTime(). Just like this:
mChronometer.setBase(SystemClock.elapsedRealTime())
But if you want to start at another time, you have to subtract the time you want in milliseconds. For example, you want to start your Chronometer at 10 seconds:
mChronometer.setBase(SystemClock.elapsedRealTime() - 10*1000);
At 2 minutes:
mChronometer.setBase(SystemClock.elapsedRealTime() - 2*60*1000);
But the problem here is that the Chronometer will show "00:00" time before it start to count, to change it to your time you have to do that :
mChronometer.setText("02:00");
The base time is the time that the Chronometer started ticking at. You can set it using Chronometer.setBase(). You should get the base time by using SystemClock.getElapsedTime(). Call setBase() with the start time each time the Chronometer is started. If there is potential for the Activity to be destroyed and recreated while the timer is still active then you will need to hold the base time somewhere outside of the Activity that owns the Chronometer.
This works for me :
Date now = new Date();
long elapsedTime = now.getTime() - startTime.getTime(); //startTime is whatever time you want to start the chronometer from. you might have stored it somwehere
myChronometer.setBase(SystemClock.elapsedRealtime() - elapsedTime);
myChronometer.start();
Some strange with SystemClock.getElapsedTime(), I did some changes for normal using with start date, like
myChron.setBase(startDate.getTime());
Here child of Chronometer below, TimeView
import android.R;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.text.format.DateUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.Chronometer;
import android.widget.RemoteViews;
import java.util.Formatter;
import java.util.IllegalFormatException;
import java.util.Locale;
#RemoteViews.RemoteView
public class TimeView extends Chronometer {
private static final String TAG = "TimeView";
private long mBase;
private boolean mVisible;
private boolean mStarted;
private boolean mRunning;
private boolean mLogged;
private String mFormat;
private Formatter mFormatter;
private Locale mFormatterLocale;
private Object[] mFormatterArgs = new Object[1];
private StringBuilder mFormatBuilder;
private OnChronometerTickListener mOnChronometerTickListener;
private StringBuilder mRecycle = new StringBuilder(8);
private static final int TICK_WHAT = 2;
/**
* Initialize this Chronometer object.
* Sets the base to the current time.
*/
public TimeView(Context context) {
this(context, null, 0);
}
/**
* Initialize with standard view layout information.
* Sets the base to the current time.
*/
public TimeView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
/**
* Initialize with standard view layout information and style.
* Sets the base to the current time.
*/
public TimeView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
mBase = System.currentTimeMillis();
updateText(mBase);
}
public void setBase(long base) {
mBase = base;
dispatchChronometerTick();
updateText(System.currentTimeMillis());
}
/**
* Return the base time as set through {#link #setBase}.
*/
public long getBase() {
return mBase;
}
public void start() {
mStarted = true;
updateRunning();
}
/**
* Stop counting up. This does not affect the base as set from {#link #setBase}, just
* the view display.
* <p/>
* This stops the messages to the handler, effectively releasing resources that would
* be held as the chronometer is running, via {#link #start}.
*/
public void stop() {
mStarted = false;
updateRunning();
}
/**
* The same as calling {#link #start} or {#link #stop}.
*
* #hide pending API council approval
*/
public void setStarted(boolean started) {
mStarted = started;
updateRunning();
}
#Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mVisible = false;
updateRunning();
}
#Override
protected void onWindowVisibilityChanged(int visibility) {
super.onWindowVisibilityChanged(visibility);
mVisible = visibility == VISIBLE;
updateRunning();
}
private synchronized void updateText(long now) {
long seconds = now - mBase;
seconds /= 1000;
String text = DateUtils.formatElapsedTime(mRecycle, seconds);
if (mFormat != null) {
Locale loc = Locale.getDefault();
if (mFormatter == null || !loc.equals(mFormatterLocale)) {
mFormatterLocale = loc;
mFormatter = new Formatter(mFormatBuilder, loc);
}
mFormatBuilder.setLength(0);
mFormatterArgs[0] = text;
try {
mFormatter.format(mFormat, mFormatterArgs);
text = mFormatBuilder.toString();
} catch (IllegalFormatException ex) {
if (!mLogged) {
Log.w(TAG, "Illegal format string: " + mFormat);
mLogged = true;
}
}
}
setText(text);
}
private void updateRunning() {
boolean running = mVisible && mStarted;
if (running != mRunning) {
if (running) {
updateText(System.currentTimeMillis());
dispatchChronometerTick();
mHandler.sendMessageDelayed(Message.obtain(mHandler, TICK_WHAT), 1000);
} else {
mHandler.removeMessages(TICK_WHAT);
}
mRunning = running;
}
}
private Handler mHandler = new Handler() {
public void handleMessage(Message m) {
if (mRunning) {
updateText(System.currentTimeMillis());
dispatchChronometerTick();
sendMessageDelayed(Message.obtain(this, TICK_WHAT), 1000);
}
}
};
void dispatchChronometerTick() {
if (mOnChronometerTickListener != null) {
mOnChronometerTickListener.onChronometerTick(this);
}
}
}
Just copy and use, it works for me
When you set the basetime with .setBase(SystemClock.elapsedRealTime()) the chronomether starts to count from 00.00 but the time it stores is the amount of milisecs from boot.
When you use .stop the internal count does not stop, just the time you see on the clock. So, if you use .start again, the clock count jumps to the real count. If you want to store the time that has passed from the start, you have to get again the System elapsed time and make the difference with the .setTime
How to set starting time? What is Chronometer “Base”?
Use SystemClock.elapsedRealtime() for this purpose:
myChronometer.setBase(SystemClock.elapsedRealtime());

Categories

Resources