Search code examples
javacountdowntimeronsaveinstancestateonrestoreinstancestate

Keep the CountDownTimer running after orientation change


I have made a CountDownTimer that works perfectly - you can get the correct time for soft/medium/hard-boiling an egg. My problem is that the timer resets after orientation change. I have googled and tried so many solution, still I don't understand how to use the onSave and onRestore properly. Here's my code:

Any tips?

    package com.dohman.boilaneggbae;

import android.graphics.PorterDuff;
import android.os.CountDownTimer;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import java.util.Locale;
import java.util.concurrent.TimeUnit;

public class MainActivity extends AppCompatActivity {

    private static final String CURRENT_TIME = "currentTime";
    private static final String DURATION_TIME = "durationTime";
    private long currentTime;
    private int durationTime;
    private TextView time;
    private Button buttonLargeSize;
    private Button buttonMediumSize;
    private Button buttonSoft;
    private Button buttonMedium;
    private Button buttonHard;
    private Button buttonHellaHard;
    private CountDownTimer countDownTimer;
    private EggSize mediumOrLarge = EggSize.UNDEFINED;
    private boolean alreadyRunning = false;

    enum EggSize {
        UNDEFINED, MEDIUM, LARGE;
    }

    private View.OnClickListener btnMediumSizeClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            mediumOrLarge = EggSize.MEDIUM;
        }
    };

    private View.OnClickListener btnLargeSizeClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            mediumOrLarge = EggSize.LARGE;
        }
    };

    private View.OnClickListener btnSoftClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            if ((mediumOrLarge != EggSize.UNDEFINED) && (alreadyRunning == false)) {
                alreadyRunning = true;
                durationTime = 240;
                start(240);
            } else if (mediumOrLarge == EggSize.UNDEFINED) {
                time.setText("Choose size first");
            } else {
                alreadyRunning = false;
                cancel();
            }
        }
    };

    @Override
    protected void onSaveInstanceState(Bundle savedInstanceState) {
        savedInstanceState.putLong(CURRENT_TIME, currentTime);
        savedInstanceState.putInt(DURATION_TIME, durationTime);
        super.onSaveInstanceState(savedInstanceState);
    }

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

        if (savedInstanceState != null) {
            currentTime = savedInstanceState.getLong(CURRENT_TIME);
            durationTime = savedInstanceState.getInt(DURATION_TIME);
        }

        time = findViewById(R.id.time);
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);

        currentTime = savedInstanceState.getLong(CURRENT_TIME);
        durationTime = savedInstanceState.getInt(DURATION_TIME);

        currentTime -= durationTime;
    }

    private void start(int duration) {
        time.setText("");

        if (mediumOrLarge == EggSize.MEDIUM) {
            duration -= 60;
        }
        countDownTimer = new CountDownTimer(duration * 1000, 1000) {

            @Override
            public void onTick(long millisUntilFinished) {
                String text = String.format(Locale.getDefault(), "%02d:%02d",
                        TimeUnit.MILLISECONDS.toMinutes(millisUntilFinished) % 60,
                        TimeUnit.MILLISECONDS.toSeconds(millisUntilFinished) % 60);
                time.setText(text);
            }

        countDownTimer.start();
    }
}

Solution

  • I finally solved this! Using this link: https://codinginflow.com/code-examples/android/countdown-timer/part-2

    Those are the codes:

    package com.codinginflow.countdowntimerexample;
    
    import android.os.CountDownTimer;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.Button;
    import android.widget.TextView;
    
    import java.util.Locale;
    
    public class MainActivity extends AppCompatActivity {
        private static final long START_TIME_IN_MILLIS = 600000;
    
        private TextView mTextViewCountDown;
        private Button mButtonStartPause;
        private Button mButtonReset;
    
        private CountDownTimer mCountDownTimer;
    
        private boolean mTimerRunning;
    
        private long mTimeLeftInMillis = START_TIME_IN_MILLIS;
        private long mEndTime;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            mTextViewCountDown = findViewById(R.id.text_view_countdown);
    
            mButtonStartPause = findViewById(R.id.button_start_pause);
            mButtonReset = findViewById(R.id.button_reset);
    
            mButtonStartPause.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (mTimerRunning) {
                        pauseTimer();
                    } else {
                        startTimer();
                    }
                }
            });
    
            mButtonReset.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    resetTimer();
                }
            });
    
            updateCountDownText();
        }
    
        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();
                }
            }.start();
    
            mTimerRunning = true;
            updateButtons();
        }
    
        private void pauseTimer() {
            mCountDownTimer.cancel();
            mTimerRunning = false;
            updateButtons();
        }
    
        private void resetTimer() {
            mTimeLeftInMillis = START_TIME_IN_MILLIS;
            updateCountDownText();
            updateButtons();
        }
    
        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);
        }
    
        private void updateButtons() {
            if (mTimerRunning) {
                mButtonReset.setVisibility(View.INVISIBLE);
                mButtonStartPause.setText("Pause");
            } else {
                mButtonStartPause.setText("Start");
    
                if (mTimeLeftInMillis < 1000) {
                    mButtonStartPause.setVisibility(View.INVISIBLE);
                } else {
                    mButtonStartPause.setVisibility(View.VISIBLE);
                }
    
                if (mTimeLeftInMillis < START_TIME_IN_MILLIS) {
                    mButtonReset.setVisibility(View.VISIBLE);
                } else {
                    mButtonReset.setVisibility(View.INVISIBLE);
                }
            }
        }
    
        @Override
        protected void onSaveInstanceState(Bundle outState) {
            super.onSaveInstanceState(outState);
            outState.putLong("millisLeft", mTimeLeftInMillis);
            outState.putBoolean("timerRunning", mTimerRunning);
            outState.putLong("endTime", mEndTime);
        }
    
        @Override
        protected void onRestoreInstanceState(Bundle savedInstanceState) {
            super.onRestoreInstanceState(savedInstanceState);
    
            mTimeLeftInMillis = savedInstanceState.getLong("millisLeft");
            mTimerRunning = savedInstanceState.getBoolean("timerRunning");
            updateCountDownText();
            updateButtons();
    
            if (mTimerRunning) {
                mEndTime = savedInstanceState.getLong("endTime");
                mTimeLeftInMillis = mEndTime - System.currentTimeMillis();
                startTimer();
            }
        }
    }
    

    EDIT: For some reason I can't mark my own answer as a solution, but this problem is solved anyway. (Answered)