package cgeo.geocaching.command; import cgeo.geocaching.utils.AsyncTaskWithProgress; import android.app.Activity; import android.os.Parcelable; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.StringRes; import com.jensdriller.libs.undobar.UndoBar; import com.jensdriller.libs.undobar.UndoBar.Listener; import org.apache.commons.lang3.StringUtils; /** * Default implementation of the command interface with undo support and background processing. */ public abstract class AbstractCommand implements Command { private static final int UNDO_DURATION_SECONDS = 5; @NonNull private final Activity context; private String progressMessage = null; protected AbstractCommand(@NonNull final Activity context, @StringRes final int progressMessageId) { this.context = context; if (progressMessageId != 0) { this.progressMessage = context.getString(progressMessageId); } } protected AbstractCommand(final Activity context) { this(context, 0); } /** * checks whether the command can be executed, before running the {@link #execute()} method */ protected boolean canExecute() { return true; } /** * Implements the actual work of the command. * <p> * This runs in a <b>non</b> UI thread. * </p> */ protected abstract void doCommand(); /** * Reverse operation to undo the effects of {@link #doCommand()}. * <p> * This runs in a <b>non</b> UI thread. * </p> */ protected abstract void undoCommand(); /** * Called after the execution of {@link #doCommand()} or {@link #undoCommand()} finished. * <p> * This runs in the UI thread. * </p> */ protected abstract void onFinished(); /** * Called after the execution of {@link #undoCommand()} finished. By default, calls {@link #onFinished()}. * <p> * This runs in the UI thread. * </p> */ protected void onFinishedUndo() { onFinished(); } /** * Message to be shown as toast when the command finished. Can be {@code null}, if the operation does not support * undo. */ @Nullable protected abstract String getResultMessage(); @NonNull protected Activity getContext() { return context; } @Override public void execute() { if (!canExecute()) { return; } final AsyncTaskWithProgress<Void, Void> task = new ActionAsyncTask(context, null, progressMessage, true); task.execute(); } public final void setProgressMessage(final String progressMessage) { this.progressMessage = progressMessage; } private final class ActionAsyncTask extends AsyncTaskWithProgress<Void, Void> implements Listener { private ActionAsyncTask(final Activity activity, final String progressTitle, final String progressMessage, final boolean indeterminate) { super(activity, progressTitle, progressMessage, indeterminate); } @Override protected Void doInBackgroundInternal(final Void[] params) { doCommand(); return null; } @Override protected void onPostExecuteInternal(final Void result) { onFinished(); final String resultMessage = getResultMessage(); showUndoToast(resultMessage); } private void showUndoToast(final String resultMessage) { if (StringUtils.isNotEmpty(resultMessage)) { new UndoBar.Builder(context) .setMessage(resultMessage) .setListener(this) .setDuration(UNDO_DURATION_SECONDS * 1000) .show(); } } @Override public void onHide() { // do nothing } @Override public void onUndo(final Parcelable arg0) { new UndoTask(context).execute(); } } private final class UndoTask extends AsyncTaskWithProgress<Void, Void> { UndoTask(final Activity activity) { super(activity, null, null, true); } @Override protected Void doInBackgroundInternal(final Void[] params) { undoCommand(); return null; } @Override protected void onPostExecuteInternal(final Void result) { onFinishedUndo(); } } }