/*
* Copyright (C) 2014 AChep@xda <artemchep@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
package com.achep.acdisplay;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.view.animation.LinearInterpolator;
import android.widget.ProgressBar;
import com.achep.acdisplay.ui.animations.ProgressBarAnimation;
import com.achep.base.utils.MathUtils;
import java.util.ArrayList;
/**
* Created by Artem on 26.01.14.
*
* @author Artem Chepurnoy
*/
public class Timeout {
private static final String TAG = "Timeout";
public static final int EVENT_TIMEOUT = 0;
public static final int EVENT_CHANGED = 1;
public static final int EVENT_CLEARED = 2;
public static final int EVENT_PAUSED = 3;
public static final int EVENT_RESUMED = 4;
private final ArrayList<OnTimeoutEventListener> mListeners = new ArrayList<>();
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
default:
timeout();
break;
}
}
};
private long mTimeoutLockedAt;
private long mTimeoutStart;
private long mTimeoutAt;
public interface OnTimeoutEventListener {
void onTimeoutEvent(Timeout timeout, int event);
}
public final void registerListener(OnTimeoutEventListener listener) {
mListeners.add(listener);
}
public final void unregisterListener(OnTimeoutEventListener listener) {
mListeners.remove(listener);
}
private void notifyListeners(int event) {
for (OnTimeoutEventListener l : mListeners) {
l.onTimeoutEvent(this, event);
}
}
private long getTime() {
return SystemClock.uptimeMillis();
}
private void timeout() {
notifyListeners(EVENT_TIMEOUT);
mTimeoutAt = 0;
}
/**
* Pauses running timer and sends {@link #EVENT_PAUSED}.
* While it's paused all methods except of
* {@link #resume() resuming} will be ignored!
*
* @see #resume()
* @see #EVENT_PAUSED
*/
public void pause() {
if (!isPaused()) {
mTimeoutLockedAt = getTime();
mHandler.removeMessages(0);
notifyListeners(EVENT_PAUSED);
}
}
public void resume() {
if (!isPaused()) {
return;
}
long offset = getTime() - mTimeoutLockedAt;
mTimeoutLockedAt = 0;
if (mTimeoutAt > 0) {
mTimeoutStart += offset;
mTimeoutAt += offset;
mHandler.sendEmptyMessageAtTime(0, mTimeoutAt);
}
notifyListeners(EVENT_RESUMED);
}
public void clear() {
if (!isPaused()) {
mTimeoutAt = 0;
mHandler.removeMessages(0);
notifyListeners(EVENT_CLEARED);
}
}
/**
* If timer isn't {@link #pause() paused} delays current
* timeout.
*
* @param delayMillis millis to delay
* @see #pause()
* @see #EVENT_CHANGED
*/
public void delay(long delayMillis) {
if (!isPaused() && isOngoing()) {
mTimeoutStart += delayMillis;
mTimeoutAt += delayMillis;
mHandler.removeMessages(0);
mHandler.sendEmptyMessageAtTime(0, mTimeoutAt);
notifyListeners(EVENT_CHANGED);
}
}
/**
* @return True if countdown timer is active at this moment, False otherwise.
*/
public boolean isOngoing() {
return mTimeoutAt > 0;
}
/**
* @return True if countdown timer is paused, False otherwise.
*/
public boolean isPaused() {
return mTimeoutLockedAt > 0;
}
public void setTimeoutDelayed(long delayMillis) {
setTimeoutDelayed(delayMillis, false);
}
public void setTimeoutDelayed(long delayMillis, boolean resetOld) {
long timeoutStart = getTime();
long timeoutAt = timeoutStart + delayMillis;
if (isOngoing() && mTimeoutAt < timeoutAt && !resetOld || isPaused()) {
return;
}
mTimeoutStart = timeoutStart;
mTimeoutAt = timeoutAt;
mHandler.removeMessages(0);
mHandler.sendEmptyMessageAtTime(0, mTimeoutAt);
notifyListeners(EVENT_CHANGED);
}
private long getTimeoutNow() {
return isPaused() ? mTimeoutLockedAt : getTime();
}
public long getRemainingTime() {
return mTimeoutAt - getTimeoutNow();
}
public float getProgress() {
float max = mTimeoutAt - mTimeoutStart;
float now = getRemainingTime();
return MathUtils.range(1f - now / max, 0, 1);
}
/**
* Displays timeout's events in given {@link com.achep.acdisplay.ui.widgets.ProgressBar}.
*/
public static class Gui implements Timeout.OnTimeoutEventListener {
private static final int MAX = 300;
private final ProgressBarAnimation mProgressBarAnimation;
private final ProgressBar mProgressBar;
public Gui(ProgressBar progressBar) {
mProgressBar = progressBar;
mProgressBar.setMax(MAX);
mProgressBar.setProgress(MAX);
mProgressBarAnimation = new ProgressBarAnimation(mProgressBar, MAX, 0);
mProgressBarAnimation.setInterpolator(new LinearInterpolator());
}
@Override
public void onTimeoutEvent(Timeout timeout, int event) {
switch (event) {
case Timeout.EVENT_PAUSED:
mProgressBar.clearAnimation();
break;
case Timeout.EVENT_RESUMED:
case Timeout.EVENT_CHANGED:
long remainingTime = timeout.getRemainingTime();
if (remainingTime > 0 && !timeout.isPaused()) {
int progress = (int) (
mProgressBar.getMax() * (1f - timeout.getProgress())
);
mProgressBarAnimation.setRange(progress, 0);
mProgressBarAnimation.setDuration(remainingTime);
mProgressBar.setProgress(progress);
mProgressBar.startAnimation(mProgressBarAnimation);
}
break;
case Timeout.EVENT_CLEARED:
mProgressBar.clearAnimation();
mProgressBar.setProgress(mProgressBar.getMax());
break;
}
}
}
}