package com.pacific.timer;
import java.util.concurrent.TimeUnit;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.annotations.NonNull;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Action;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
public final class Rx2Timer {
private Disposable disposable;
private long take;
private long period;
private long initialDelay;
private TimeUnit unit;
private OnComplete onComplete;
private OnCount onCount;
private OnError onError;
private long pauseTake = 0l;
private long resumeTake = 0l;
private boolean isPause = false;
private Rx2Timer(Builder builder) {
take = builder.take;
period = builder.period;
initialDelay = builder.initialDelay;
unit = builder.unit;
onComplete = builder.onComplete;
onCount = builder.onCount;
onError = builder.onError;
}
/**
* is in pause state
*
* @return
*/
public boolean isPause() {
return isPause;
}
/**
* restart timer , all pause states are cleaned
*
* @return
*/
public Rx2Timer restart() {
stop();
return start();
}
/**
* start timer
*
* @return
*/
public Rx2Timer start() {
if (isPause) return restart();
if (disposable == null || disposable.isDisposed()) {
disposable
= Observable.interval(initialDelay, period, unit)
.subscribeOn(Schedulers.single())
.take(take + 1)
.map(new Function<Long, Long>() {
@Override
public Long apply(@NonNull Long aLong) throws Exception {
pauseTake = aLong;
return take - aLong;
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Long>() {
@Override
public void accept(@NonNull Long aLong) throws Exception {
if (onCount != null) {
onCount.onCount(aLong);
}
}
}, new Consumer<Throwable>() {
@Override
public void accept(@NonNull Throwable throwable) throws Exception {
if (onError != null) {
onError.onError(throwable);
}
}
}, new Action() {
@Override
public void run() throws Exception {
if (onComplete != null) {
onComplete.onComplete();
}
}
});
}
return this;
}
/**
* stop timer and all pause states are cleaned
*/
public void stop() {
if (disposable != null && !disposable.isDisposed()) {
disposable.dispose();
}
if (isPause) cleanPauseState();
}
/**
* pause timer
*/
public void pause() {
if (isPause) return;
stop();
isPause = true;
resumeTake += pauseTake;
}
/**
* resume timer
*/
public void resume() {
if (!isPause) return;
isPause = false;
if (disposable == null || disposable.isDisposed()) {
disposable
= Observable.interval(initialDelay, period, unit)
.subscribeOn(Schedulers.single())
.take(take + 1 - resumeTake)
.map(new Function<Long, Long>() {
@Override
public Long apply(@NonNull Long aLong) throws Exception {
pauseTake = aLong;
return take - aLong - resumeTake;
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Long>() {
@Override
public void accept(@NonNull Long aLong) throws Exception {
if (onCount != null) {
onCount.onCount(aLong);
}
}
}, new Consumer<Throwable>() {
@Override
public void accept(@NonNull Throwable throwable) throws Exception {
cleanPauseState();
if (onError != null) {
onError.onError(throwable);
}
}
}, new Action() {
@Override
public void run() throws Exception {
cleanPauseState();
if (onComplete != null) {
onComplete.onComplete();
}
}
});
}
}
/**
* clean pause states
*/
public void cleanPauseState() {
isPause = false;
resumeTake = 0l;
pauseTake = 0l;
}
public static Builder builder() {
return new Builder();
}
public static final class Builder {
private long take = 60;
private long period = 1;
private long initialDelay = 0;
private TimeUnit unit = TimeUnit.SECONDS;
private OnComplete onComplete;
private OnCount onCount;
private OnError onError;
Builder() {
}
/**
* counting number , default value is 60
*
* @param take take value
* @return
*/
public Builder take(int take) {
this.take = take;
return this;
}
/**
* period, default value is 1
*
* @param period period value
* @return
*/
public Builder period(int period) {
this.period = period;
return this;
}
/**
* delay to begin , default value is 0
*
* @param initialDelay delay value
* @return
*/
public Builder initialDelay(int initialDelay) {
this.initialDelay = initialDelay;
return this;
}
/**
* time unit , default value is TimeUnit.SECONDS
*
* @param unit unit value
* @return
*/
public Builder unit(TimeUnit unit) {
this.unit = unit;
return this;
}
public Builder onComplete(OnComplete onComplete) {
this.onComplete = onComplete;
return this;
}
public Builder onCount(OnCount onCount) {
this.onCount = onCount;
return this;
}
public Builder onError(OnError onError) {
this.onError = onError;
return this;
}
public Rx2Timer build() {
return new Rx2Timer(this);
}
}
public interface OnComplete {
void onComplete();
}
public interface OnCount {
void onCount(Long count);
}
public interface OnError {
void onError(Throwable throwable);
}
}