package cgeo.geocaching.utils;
import cgeo.geocaching.CgeoApplication;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.annotation.StringRes;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
/**
* Handler with a dispose policy. Once disposed, the handler will not handle
* any more dispose or regular message.
*/
public abstract class DisposableHandler extends Handler implements Disposable {
public static final int DONE = -1000;
protected static final int UPDATE_LOAD_PROGRESS_DETAIL = 42186;
private final CompositeDisposable disposables = new CompositeDisposable();
public DisposableHandler(final Looper serviceLooper) {
super(serviceLooper);
}
public DisposableHandler() {
super();
}
private static class CancelHolder {
// CANCEL is used to synchronously dispose the DisposableHandler and call
// the appropriate callback.
static final int CANCEL = -1;
// When dispose() has been called, CANCEL_CALLBACK is used to synchronously
// call the appropriate callback.
static final int CANCEL_CALLBACK = -2;
final int kind;
CancelHolder(final int kind) {
this.kind = kind;
}
}
@Override
public final void handleMessage(final Message message) {
if (message.obj instanceof CancelHolder) {
final CancelHolder holder = (CancelHolder) message.obj;
if (holder.kind == CancelHolder.CANCEL && !isDisposed()) {
disposables.dispose();
handleDispose();
} else if (holder.kind == CancelHolder.CANCEL_CALLBACK) {
// We have been disposed already but the callback has not been called yet.
handleDispose();
}
} else if (!isDisposed()) {
handleRegularMessage(message);
}
}
/**
* Add a disposable to the list of disposables to be disposed at disposition time.
*/
public final void add(final Disposable disposable) {
disposables.add(disposable);
}
/**
* Handle a non-dispose message.<br>
* Subclasses must implement this to handle messages.
*
* @param message
* the message to handle
*/
protected abstract void handleRegularMessage(final Message message);
/**
* Handle a dispose message.
*
* This is called on the handler looper thread when the handler gets disposed.
*/
protected void handleDispose() {
// May be overwritten by inheriting classes.
}
/**
* Get a dispose message that can later be sent to this handler to dispose it.
*
* @return a message that, when sent, will dispose the current handler.
*/
public Message disposeMessage() {
return obtainMessage(0, new CancelHolder(CancelHolder.CANCEL));
}
/**
* Cancel the current handler. This can be called from any thread. The disposables
* added with {@link #add(Disposable)} will be disposed immediately, while the
* {@link #handleDispose()} callback will be called synchronously by the handler.
*/
@Override
public void dispose() {
disposables.dispose();
obtainMessage(0, new CancelHolder(CancelHolder.CANCEL_CALLBACK)).sendToTarget();
}
/**
* Check if the current handler has been disposed.
*
* @return true if the handler has been disposed
*/
@Override
public boolean isDisposed() {
return disposables.isDisposed();
}
/**
* Check if a handler has been disposed.
*
* @param handler
* a handler, or null
* @return true if the handler is not null and has been disposed
*/
public static boolean isDisposed(final DisposableHandler handler) {
return handler != null && handler.isDisposed();
}
public static void sendLoadProgressDetail(final Handler handler, @StringRes final int resourceId) {
if (handler != null) {
handler.obtainMessage(UPDATE_LOAD_PROGRESS_DETAIL, CgeoApplication.getInstance().getString(resourceId)).sendToTarget();
}
}
}