Created
November 3, 2021 20:30
-
-
Save Bramengton/ed853d9aebb20820430128d6e975c4b7 to your computer and use it in GitHub Desktop.
CountDownTimer - a different look at the obvious!
This file contains hidden or 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
public Ticker startTimerCheckFrameDelay(){ | |
return new Ticker("TimerCheckFrameDelay") { | |
@Override | |
public void onFinish() { | |
if(BuildConfig.DEBUG) Timber.tag(TAG).i("finally we did it!"); | |
} | |
@Override | |
public void onTick(long millisLeft) { | |
if(BuildConfig.DEBUG) Timber.tag(TAG).i("Ticker left - %s", millisLeft); | |
} | |
}; | |
} | |
mTicker.reStart(TimeUnit.SECONDS.toMillis(15), TimeUnit.SECONDS.toMillis(1)); - every 15 seconds, count down 1 second before calling onFinish. | |
mTicker.onPause() - Pause | |
mTicker.onResume() - Resume | |
mTicker.onStop() - Stop | |
mTicker.close() - Close | |
mTicker.isActive() - check is active. |
This file contains hidden or 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
package eld.trackensure.utilities; | |
import android.os.SystemClock; | |
import java.util.concurrent.TimeUnit; | |
import eld.trackensure.BuildConfig; | |
import timber.log.Timber; | |
public abstract class Ticker implements Runnable, AutoCloseable { | |
private static final String TAG = Ticker.class.getSimpleName(); | |
public abstract void onFinish(); | |
public abstract void onTick(long millisLeft); | |
// private volatile boolean stop = false; | |
private final Thread mThread; | |
private final Object mLock; | |
private volatile boolean mPause; | |
private volatile boolean mStop; | |
private volatile boolean mFinish; | |
private final String mName; | |
/** | |
* Millis since epoch when alarm should stop. | |
*/ | |
private volatile long mMillisInFuture; | |
/** | |
* The interval in millis that the user receives callbacks | |
*/ | |
private volatile long mCountdownInterval; | |
private volatile long mStopTimeInFuture; | |
public Ticker() { | |
mName = "CustomTickerTimer"; | |
this.mLock = new Object(); | |
mThread = new Thread(this, String.format("%s-%s", mName, System.currentTimeMillis())); | |
} | |
public Ticker(String tag) { | |
mName = tag; | |
this.mLock = new Object(); | |
mThread = new Thread(this, String.format("%s-%s", mName, System.currentTimeMillis())); | |
} | |
// public final Ticker start() { | |
//// stop = false; | |
// | |
// mPause = false; | |
// mFinish = false; | |
// mStop = false; | |
// | |
// if (mMillisInFuture <= 0) { | |
// onFinish(); | |
// }else { | |
// mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture; | |
// if(mThread!=null) mThread.start(); | |
// } | |
// return this; | |
// } | |
public final Ticker start(long millisInFuture, long countDownInterval) { | |
log(String.format("START for interval: %s with step: %s", millisInFuture, countDownInterval)); | |
// stop = false; | |
mMillisInFuture = millisInFuture; | |
mCountdownInterval = countDownInterval; | |
mPause = false; | |
mFinish = false; | |
mStop = false; | |
if (mMillisInFuture <= 0) { | |
onFinish(); | |
}else { | |
mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture; | |
if(mThread!=null) mThread.start(); | |
} | |
return this; | |
} | |
@Override | |
public void run() { | |
try { | |
while (!mStop || !Thread.interrupted()) { | |
final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime(); | |
if (millisLeft <= 0) { | |
onFinish(); | |
return; | |
} else { | |
long lastTickStart = SystemClock.elapsedRealtime(); | |
if(!mStop)onTick(millisLeft); | |
// take into account user's onTick taking time to execute | |
long lastTickDuration = SystemClock.elapsedRealtime() - lastTickStart; | |
long delay; | |
if (millisLeft < mCountdownInterval) { | |
// just delay until done | |
delay = millisLeft - lastTickDuration; | |
// special case: user's onTick took more than interval to | |
// complete, trigger onFinish without delay | |
if (delay < 0) delay = 0; | |
} else { | |
delay = mCountdownInterval - lastTickDuration; | |
// special case: user's onTick took more than interval to | |
// complete, skip to next interval | |
while (delay < 0) delay += mCountdownInterval; | |
} | |
if(delay>0) TimeUnit.MILLISECONDS.sleep(delay); | |
else { | |
onFinish(); | |
return; | |
} | |
} | |
synchronized (mLock) { | |
boolean inPause = false; | |
long lastTickStart = SystemClock.elapsedRealtime(); | |
while (mPause) { | |
try { | |
mLock.wait(); | |
} | |
catch (InterruptedException e) { | |
return; | |
} | |
inPause = true; | |
} | |
if(mStop)break; | |
if(inPause) mStopTimeInFuture += SystemClock.elapsedRealtime() - lastTickStart; | |
} | |
} | |
} catch (InterruptedException ignored) { | |
Timber.tag(TAG).e(ignored); | |
} | |
} | |
@Override | |
public void close() { | |
synchronized (mLock) { | |
log("close"); | |
mPause = false; | |
mStop = true; | |
} | |
if(isActive()) mThread.interrupt(); | |
} | |
/** | |
* Call this on pause. | |
*/ | |
public void onPause() { | |
synchronized (mLock) { | |
log("onPause"); | |
mPause = true; | |
mStop=false; | |
} | |
} | |
/** | |
* Just stop. | |
*/ | |
public void onStop() { | |
synchronized (mLock) { | |
log("onStop"); | |
mPause = false; | |
mStop = true; | |
} | |
} | |
/** | |
* Call this on resume. | |
*/ | |
public void onResume() { | |
synchronized (mLock) { | |
log("onResume"); | |
mPause = false; | |
mStop=false; | |
mFinish = false; | |
mLock.notifyAll(); | |
} | |
} | |
/** | |
* Call this for restar - every N seconds, count down M second before calling onFinish. | |
*/ | |
public void reStart(long millisInFuture, long countDownInterval) { | |
log(String.format("reStart for interval: %s with step: %s", millisInFuture, countDownInterval)); | |
synchronized (mLock) { | |
// stop = false; | |
mMillisInFuture = millisInFuture; | |
mCountdownInterval = countDownInterval; | |
mPause = false; | |
mFinish = false; | |
mStop = false; | |
if (mMillisInFuture <= 0) { | |
onFinish(); | |
}else { | |
mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture; | |
} | |
mLock.notifyAll(); | |
} | |
} | |
public boolean isActive(){ | |
return mThread!=null && !mThread.isInterrupted() && mThread.isAlive(); | |
} | |
private void log(String text){ | |
if(BuildConfig.DEBUG) Timber.tag(TAG).i("Ticker name[%s] - %s", mName!=null ? mName : "Name - Ticker", text); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment