/*
* Copyright (C) 2016 Brian Wernick
*
* 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.devbrackets.android.exomedia.util;
import android.os.Handler;
import android.os.HandlerThread;
import android.support.annotation.Nullable;
/**
* A simple stopwatch to keep a correct and updated record of the running duration
* of processes.
*/
public class StopWatch {
private static final String HANDLER_THREAD_NAME = "ExoMedia_StopWatch_HandlerThread";
private static final int DEFAULT_TICK_DELAY = 33; // ~30 fps
private volatile boolean isRunning = false;
private int tickDelay = DEFAULT_TICK_DELAY;
private Handler delayedHandler;
private HandlerThread handlerThread;
private boolean useHandlerThread = false;
private TickListener listener;
private TickRunnable tickRunnable = new TickRunnable();
private long startTime = 0;
private long currentTime = 0;
private long storedTime = 0;
public StopWatch() {
this(true);
}
/**
* @param processOnStartingThread True if the repeating process should be handled on the same thread that created the Repeater
*/
public StopWatch(boolean processOnStartingThread) {
if (processOnStartingThread) {
delayedHandler = new Handler();
return;
}
useHandlerThread = true;
}
/**
* @param handler The Handler to use for the repeating process
*/
public StopWatch(Handler handler) {
delayedHandler = handler;
}
/**
* Sets the approximate duration between time updates.
*
* @param milliSeconds The approximate duration between time updates [default: {@value #DEFAULT_TICK_DELAY}]
*/
public void setTickDelay(int milliSeconds) {
tickDelay = milliSeconds;
}
/**
* Retrieves the approximate duration between time updates.
*
* @return The approximate duration in milliseconds between time updates
*/
public int getTickDelay() {
return tickDelay;
}
/**
* Starts the stopwatch. This will continue from where we last left off,
* if you need to start from 0 call {@link #reset()} first.
*/
public void start() {
if (isRunning()) {
return;
}
isRunning = true;
startTime = System.currentTimeMillis();
if (useHandlerThread) {
handlerThread = new HandlerThread(HANDLER_THREAD_NAME);
handlerThread.start();
delayedHandler = new Handler(handlerThread.getLooper());
}
tickRunnable.performTick();
}
/**
* Stops the stopwatch, capturing the ending time
*/
public void stop() {
if (!isRunning()) {
return;
}
delayedHandler.removeCallbacksAndMessages(null);
if (handlerThread != null) {
handlerThread.quit();
}
isRunning = false;
currentTime = 0;
storedTime += System.currentTimeMillis() - startTime;
}
/**
* Resets the current time for the stopWatch
*/
public void reset() {
currentTime = 0;
storedTime = 0;
startTime = System.currentTimeMillis();
}
/**
* Forcefully sets the current time for the stopwatch.
*
* @param time The new stopwatch time in milliseconds
*/
public void overrideCurrentTime(long time) {
startTime = System.currentTimeMillis();
currentTime = 0;
storedTime = time;
}
/**
* Determines if the stopwatch is currently running
*
* @return True if the stopwatch is currently running
*/
public boolean isRunning() {
return isRunning;
}
/**
* Retrieves the current time for the stopwatch. If the stopwatch is stopped then the
* ending time will be returned.
*
* @return The time in milliseconds
*/
public long getTime() {
return currentTime + storedTime;
}
/**
* Retrieves the current time for the stopwatch. If the stopwatch is stopped then the
* ending time will be returned.
*
* @return The time in milliseconds
*/
public int getTimeInt() {
long time = currentTime + storedTime;
return time < Integer.MAX_VALUE ? (int) time : Integer.MAX_VALUE;
}
/**
* Sets the listener to be notified for each time update (tick)
*
* @param listener The listener or null
*/
public void setTickListener(@Nullable TickListener listener) {
this.listener = listener;
}
public interface TickListener {
void onStopWatchTick(long currentTime);
}
private class TickRunnable implements Runnable {
@Override
public void run() {
currentTime = System.currentTimeMillis() - startTime;
if (isRunning) {
performTick();
}
if (listener != null) {
listener.onStopWatchTick(currentTime + storedTime);
}
}
public void performTick() {
delayedHandler.postDelayed(tickRunnable, tickDelay);
}
}
}