javaandroidcountdowntimer

CountDownTimer problem when app is closed


I made a CountDownTimer code, I would like the CountDownTimer to restart when it is finished even if the app is closed, but it only restarts if the app is running or when the app is re-launched. So if I close the app when the countdown is 00:10 (min: sec) and reopen the app after 30 sec, the counter should be 00:40, but it starts from 1 minute... But if I close the app at 00:40 and reopen it after 10 sec, it starts at 00:30 so it's good, but the problem is that it restarts from 1 min when the app closed and reopened.... can somebody help me?

My code:

package com.example.countdown_implement;

import android.content.SharedPreferences;
import android.os.CountDownTimer;
import android.os.Bundle;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

import java.util.Locale;

public class MainActivity extends AppCompatActivity {
    private static final long START_TIME_IN_MILLIS = 60000;
    private TextView mTextViewCountDown;
    private CountDownTimer mCountDownTimer;
    private boolean mTimerRunning;
    private long mTimeLeftInMillis;
    private long mEndTime;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    mTextViewCountDown = findViewById(R.id.text_view_countdown);

}

private void startTimer() {
    mEndTime = System.currentTimeMillis() + mTimeLeftInMillis;

    mCountDownTimer = new CountDownTimer(mTimeLeftInMillis, 1000) {
        @Override
        public void onTick(long millisUntilFinished) {
            mTimeLeftInMillis = millisUntilFinished;
            updateCountDownText();
        }

        @Override
        public void onFinish() {
            //mTimerRunning = false;
            //updateButtons();

            updateCountDownText();
            resetTimer();
            startTimer();

        }
    }.start();

    //mTimerRunning = true;

}


private void resetTimer() {
    mTimeLeftInMillis = START_TIME_IN_MILLIS;
    updateCountDownText();

}

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);

    mTextViewCountDown.setText(timeLeftFormatted);
}


@Override
protected void onStop() {
    super.onStop();

    SharedPreferences prefs = getSharedPreferences("prefs", MODE_PRIVATE);
    SharedPreferences.Editor editor = prefs.edit();

    editor.putLong("millisLeft", mTimeLeftInMillis);
    editor.putBoolean("timerRunning", mTimerRunning);
    editor.putLong("endTime", mEndTime);

    editor.apply();

}

@Override
protected void onStart() {
    super.onStart();

    SharedPreferences prefs = getSharedPreferences("prefs", MODE_PRIVATE);

    mTimeLeftInMillis = prefs.getLong("millisLeft", START_TIME_IN_MILLIS);
    mTimerRunning = prefs.getBoolean("timerRunning", false);
    mEndTime = prefs.getLong("endTime", 0);
    mTimeLeftInMillis = mEndTime - System.currentTimeMillis();

    updateCountDownText();
    startTimer();
    if (mTimeLeftInMillis < 0) {
        updateCountDownText();
        startTimer();
    }
  }
}

Solution

  • UPDATED

    Below is your code converted into a code snippet for a CountdownTimer which will keep working even when the app is closed, pushed to background or restarted.

    set START_TIME_IN_MILLIS as the Timer start time, in the following example it is set to 15 seconds.

    import android.content.SharedPreferences;
    import android.os.CountDownTimer;
    import android.os.Bundle;
    import android.util.Log;
    import android.widget.TextView;
    
    import androidx.appcompat.app.AppCompatActivity;
    
    import java.util.Locale;
    
    public class MainActivity2 extends AppCompatActivity {
        private static final long START_TIME_IN_MILLIS = 15000;
        private TextView mTextViewCountDown;
        private CountDownTimer mCountDownTimer;
        private boolean mTimerRunning;
        private long mTimeLeftInMillis;
        private long mEndTime;
        private long remainingTimeInMillis;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_new);
    
            mTextViewCountDown = findViewById(R.id.tv);
        }
    
        private void startTimer() {
            mCountDownTimer = new CountDownTimer(remainingTimeInMillis, 1000) {
                @Override
                public void onTick(long millisUntilFinished) {
                    remainingTimeInMillis = millisUntilFinished;
                    mTimeLeftInMillis = millisUntilFinished;
                    updateCountDownText();
                }
    
                @Override
                public void onFinish() {
                    //mTimerRunning = false;
                    //updateButtons();
    
                    updateCountDownText();
                    resetTimer();
                    startTimer();
    
                }
            }.start();
    
            //mTimerRunning = true;
    
        }
    
    
        private void resetTimer() {
            remainingTimeInMillis = START_TIME_IN_MILLIS;
            updateCountDownText();
    
        }
    
        private void updateCountDownText() {
    
    
            int minutes = (int) (remainingTimeInMillis / 1000) / 60;
            int seconds = (int) (remainingTimeInMillis / 1000) % 60;
    
            String timeLeftFormatted = String.format(Locale.getDefault(), "%02d:%02d", minutes, seconds);
    
            mTextViewCountDown.setText(timeLeftFormatted);
        }
    
    
        @Override
        protected void onStop() {
            super.onStop();
    
            SharedPreferences prefs = getSharedPreferences("prefs", MODE_PRIVATE);
            SharedPreferences.Editor editor = prefs.edit();
    
            editor.putLong("millisLeft", mTimeLeftInMillis);
            editor.putBoolean("timerRunning", mTimerRunning);
            editor.putLong("endTime", System.currentTimeMillis());
            editor.apply();
    
        }
    
        @Override
        protected void onStart() {
            super.onStart();
    
            SharedPreferences prefs = getSharedPreferences("prefs", MODE_PRIVATE);
    
            mTimeLeftInMillis = prefs.getLong("millisLeft", START_TIME_IN_MILLIS);
            mTimerRunning = prefs.getBoolean("timerRunning", false);
            mEndTime = prefs.getLong("endTime", 0);
            if (mEndTime == 0L) {
                remainingTimeInMillis = (mTimeLeftInMillis);
            } else {
                Long timeDiff = (mEndTime - System.currentTimeMillis());
                //to convert into positive number
                timeDiff = Math.abs(timeDiff);
    
                long timeDiffInSeconds = (timeDiff / 1000) % 60;
               long timeDiffInMillis = timeDiffInSeconds * 1000;
                Long timeDiffInMillisPlusTimerRemaining = remainingTimeInMillis = mTimeLeftInMillis - timeDiffInMillis;
    
                if (timeDiffInMillisPlusTimerRemaining < 0) {
                    timeDiffInMillisPlusTimerRemaining = Math.abs(timeDiffInMillisPlusTimerRemaining);
                    remainingTimeInMillis = START_TIME_IN_MILLIS - timeDiffInMillisPlusTimerRemaining;
                }
            }
            updateCountDownText();
            startTimer();
        }
    }