Last active
November 12, 2015 08:17
-
-
Save SyllaJay/4a5e5670e13818b05c53 to your computer and use it in GitHub Desktop.
A CountDownTimer! More accurate than android.os.CountDownTimer!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (C) 2008 The Android Open Source Project | |
* | |
* 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. | |
*/ | |
package com.yourpackage; | |
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. | |
* | |
* Example of showing a 30 second countdown in a text field: | |
* | |
* <pre class="prettyprint"> | |
* new CountDownTimer(30000, 1000) { | |
* | |
* public void onTick(long millisUntilFinished) { | |
* mTextField.setText("seconds remaining: " + millisUntilFinished / 1000); | |
* } | |
* | |
* public void onFinish() { | |
* mTextField.setText("done!"); | |
* } | |
* }.start(); | |
* </pre> | |
* | |
* 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 CountDownTimerPrecise { | |
/** | |
* 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; | |
private long mNextTime; | |
/** | |
* @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 CountDownTimerPrecise(long millisInFuture, long countDownInterval) { | |
mMillisInFuture = millisInFuture; | |
mCountdownInterval = countDownInterval; | |
} | |
/** | |
* Cancel the countdown. | |
*/ | |
public synchronized final void cancel() { | |
mCancelled = true; | |
mHandler.removeMessages(MSG); | |
} | |
/** | |
* Start the countdown. | |
*/ | |
public synchronized final CountDownTimerPrecise start() { | |
mCancelled = false; | |
if (mMillisInFuture <= 0) { | |
onFinish(); | |
return this; | |
} | |
/*mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture; | |
mHandler.sendMessage(mHandler.obtainMessage(MSG));*/ | |
mNextTime = SystemClock.uptimeMillis(); | |
mStopTimeInFuture = mNextTime + mMillisInFuture; | |
mNextTime += mCountdownInterval; | |
mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG), mNextTime); | |
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 (CountDownTimerPrecise.this) { | |
if (mCancelled) { | |
return; | |
} | |
/*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); | |
}*/ | |
final long millisLeft = mStopTimeInFuture - SystemClock.uptimeMillis(); | |
if (millisLeft <= 0) { | |
onFinish(); | |
} else { | |
onTick(millisLeft); | |
// Calculate next tick by adding the countdown interval from the original start time | |
// If user's onTick() took too long, skip the intervals that were already missed | |
long currentTime = SystemClock.uptimeMillis(); | |
do { | |
mNextTime += mCountdownInterval; | |
} while (currentTime > mNextTime); | |
// Make sure this interval doesn't exceed the stop time | |
if(mNextTime < mStopTimeInFuture) | |
sendMessageAtTime(obtainMessage(MSG), mNextTime); | |
else | |
sendMessageAtTime(obtainMessage(MSG), mStopTimeInFuture); | |
} | |
} | |
} | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment