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(); } } }