1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.os;
18 
19 /**
20  * Schedule a countdown until a time in the future, with
21  * regular notifications on intervals along the way.
22  *
23  * Example of showing a 30 second countdown in a text field:
24  *
25  * <div>
26  * <div class="ds-selector-tabs"><section><h3 id="kotlin">Kotlin</h3>
27  * <pre class="prettyprint lang-kotlin">
28  * object : CountDownTimer(30000, 1000) {
29  *
30  *     override fun onTick(millisUntilFinished: Long) {
31  *         mTextField.setText("seconds remaining: " + millisUntilFinished / 1000)
32  *     }
33  *
34  *     override fun onFinish() {
35  *         mTextField.setText("done!")
36  *     }
37  * }.start()
38  * </pre>
39  * </section><section><h3 id="java">Java</h3>
40  * <pre class="prettyprint lang-java">
41  * new CountDownTimer(30000, 1000) {
42  *
43  *     public void onTick(long millisUntilFinished) {
44  *         mTextField.setText("seconds remaining: " + millisUntilFinished / 1000);
45  *     }
46  *
47  *     public void onFinish() {
48  *         mTextField.setText("done!");
49  *     }
50  * }.start();
51  * </pre></section></div></div>
52  *
53  * The calls to {@link #onTick(long)} are synchronized to this object so that
54  * one call to {@link #onTick(long)} won't ever occur before the previous
55  * callback is complete.  This is only relevant when the implementation of
56  * {@link #onTick(long)} takes an amount of time to execute that is significant
57  * compared to the countdown interval.
58  */
59 public abstract class CountDownTimer {
60 
61     /**
62      * Millis since epoch when alarm should stop.
63      */
64     private final long mMillisInFuture;
65 
66     /**
67      * The interval in millis that the user receives callbacks
68      */
69     private final long mCountdownInterval;
70 
71     private long mStopTimeInFuture;
72 
73     /**
74     * boolean representing if the timer was cancelled
75     */
76     private boolean mCancelled = false;
77 
78     /**
79      * @param millisInFuture The number of millis in the future from the call
80      *   to {@link #start()} until the countdown is done and {@link #onFinish()}
81      *   is called.
82      * @param countDownInterval The interval along the way to receive
83      *   {@link #onTick(long)} callbacks.
84      */
CountDownTimer(long millisInFuture, long countDownInterval)85     public CountDownTimer(long millisInFuture, long countDownInterval) {
86         mMillisInFuture = millisInFuture;
87         mCountdownInterval = countDownInterval;
88     }
89 
90     /**
91      * Cancel the countdown.
92      */
cancel()93     public synchronized final void cancel() {
94         mCancelled = true;
95         mHandler.removeMessages(MSG);
96     }
97 
98     /**
99      * Start the countdown.
100      */
start()101     public synchronized final CountDownTimer start() {
102         mCancelled = false;
103         if (mMillisInFuture <= 0) {
104             onFinish();
105             return this;
106         }
107         mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture;
108         mHandler.sendMessage(mHandler.obtainMessage(MSG));
109         return this;
110     }
111 
112 
113     /**
114      * Callback fired on regular interval.
115      * @param millisUntilFinished The amount of time until finished.
116      */
onTick(long millisUntilFinished)117     public abstract void onTick(long millisUntilFinished);
118 
119     /**
120      * Callback fired when the time is up.
121      */
onFinish()122     public abstract void onFinish();
123 
124 
125     private static final int MSG = 1;
126 
127 
128     // handles counting down
129     private Handler mHandler = new Handler() {
130 
131         @Override
132         public void handleMessage(Message msg) {
133 
134             synchronized (CountDownTimer.this) {
135                 if (mCancelled) {
136                     return;
137                 }
138 
139                 final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();
140 
141                 if (millisLeft <= 0) {
142                     onFinish();
143                 } else {
144                     long lastTickStart = SystemClock.elapsedRealtime();
145                     onTick(millisLeft);
146 
147                     // take into account user's onTick taking time to execute
148                     long lastTickDuration = SystemClock.elapsedRealtime() - lastTickStart;
149                     long delay;
150 
151                     if (millisLeft < mCountdownInterval) {
152                         // just delay until done
153                         delay = millisLeft - lastTickDuration;
154 
155                         // special case: user's onTick took more than interval to
156                         // complete, trigger onFinish without delay
157                         if (delay < 0) delay = 0;
158                     } else {
159                         delay = mCountdownInterval - lastTickDuration;
160 
161                         // special case: user's onTick took more than interval to
162                         // complete, skip to next interval
163                         while (delay < 0) delay += mCountdownInterval;
164                     }
165 
166                     sendMessageDelayed(obtainMessage(MSG), delay);
167                 }
168             }
169         }
170     };
171 }
172