package com.circlegate.liban.task;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Queue;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import com.circlegate.liban.task.TaskErrors.BaseError;
import com.circlegate.liban.task.TaskInterfaces.ITask;
import com.circlegate.liban.task.TaskInterfaces.ITaskContext;
import com.circlegate.liban.task.TaskInterfaces.ITaskExecutor;
import com.circlegate.liban.task.TaskInterfaces.ITaskParam;
import com.circlegate.liban.task.TaskInterfaces.ITaskProgressListener;
import com.circlegate.liban.task.TaskInterfaces.ITaskResult;
import com.circlegate.liban.task.TaskInterfaces.ITaskResultListener;
import com.circlegate.liban.utils.LogUtils;
public class TaskSerialExecutor implements ITaskExecutor {
private static final String TAG = TaskSerialExecutor.class.getSimpleName();
private final Queue<Task> tasks = new LinkedBlockingQueue<Task>();
private final Handler handlerUi = new Handler(Looper.getMainLooper());
private final ITaskContext context;
private final Executor executor;
private Task active;
public TaskSerialExecutor(ITaskContext context, Executor executor) {
this.context = context;
this.executor = executor;
}
@Override
public synchronized void executeTask(String id, ITaskParam param, Bundle bundle, boolean canCacheReferenceToParamResult, ITaskResultListener listener, ITaskProgressListener optProgressListener) {
tasks.offer(new Task(id, param, bundle, canCacheReferenceToParamResult, listener, optProgressListener) {
public void run() {
if (headSameAsActive()) {
ITaskResult result = null;
LogUtils.i(TAG, "Executing task: " + getId());
try {
result = getParam().createResult(context, this);
try {
LogUtils.i(TAG, "Finished task: " + getId() + ", "
+ (result == null ? "result is null"
: (result.isValidResult() ? "result is valid" : ("error: " + result.getError().getMsg(context).toString()))));
}
catch (Exception ex) {}
}
catch (Exception ex) {
LogUtils.e(TAG, "Error while processing task", ex);
result = getParam().createErrorResult(context, this, BaseError.ERR_UNKNOWN_ERROR);
}
if (headSameAsActive() && result != null) {
final ITaskResult resultFinal = result;
handlerUi.post(new Runnable() {
@Override
public void run() {
if (removeHeadIfSameAsActive()) {
getListener().onTaskCompleted(getId(), resultFinal, getBundle());
}
scheduleNext();
}
});
}
else {
removeHeadIfSameAsActive();
scheduleNext();
}
}
else {
scheduleNext();
}
}
});
if (active == null) {
scheduleNext();
}
}
public int getTasksCount() {
return tasks.size();
}
@Override
public synchronized boolean containsTask(String id, ITaskResultListener listener) {
return getTask(id, listener) != null;
}
@Override
public boolean containsAnyTaskByResultHandler(ITaskResultListener listener) {
for (Task t : tasks) {
if (t.getListener().equals(listener)) {
return true;
}
}
return false;
}
@Override
public synchronized ITask getTask(String id, ITaskResultListener listener) {
for (Task t : tasks) {
if (t.getId().equals(id) && t.getListener().equals(listener)) {
return t;
}
}
return null;
}
@Override
public synchronized boolean cancelTask(String id, ITaskResultListener listener) {
for (Iterator<Task> iter = tasks.iterator(); iter.hasNext();) {
Task t = iter.next();
if (t.getId().equals(id) && t.getListener().equals(listener)) {
t.setCanceled();
iter.remove();
return true;
}
}
return false;
}
@Override
public synchronized void cancelTasksByResultHandler(ITaskResultListener listener) {
for (Iterator<Task> iter = tasks.iterator(); iter.hasNext();) {
Task t = iter.next();
if (t.getListener().equals(listener)) {
t.setCanceled();
iter.remove();
}
}
}
@Override
public synchronized boolean addSkipCount(String id, ITaskResultListener listener, int skipCount) {
for (Iterator<Task> iter = tasks.iterator(); iter.hasNext();) {
Task t = iter.next();
if (t.getId().equals(id) && t.getListener().equals(listener)) {
t.addSkipCount(skipCount);
return true;
}
}
return false;
}
private synchronized boolean headSameAsActive() {
return active != null && active == tasks.peek();
}
private synchronized boolean removeHeadIfSameAsActive() {
if (headSameAsActive()) {
tasks.remove();
return true;
}
else
return false;
}
private synchronized void scheduleNext() {
if ((active = tasks.peek()) != null) {
executor.execute(active);
}
}
private abstract class Task implements ITask, Runnable {
private final String id;
private final ITaskParam param;
private final Bundle bundle;
private final boolean canCacheReferenceToParamResult;
private final ITaskResultListener listener;
private final ITaskProgressListener optProgressListener;
private boolean canceled;
private int skipCount;
private int progress = ITaskProgressListener.INDETERMINATE_PROGRESS;
private String progressState;
private HashMap<String, Object> processObjects; // lazy loaded
public Task(String id, ITaskParam param, Bundle bundle, boolean canCacheReferenceToParamResult, ITaskResultListener listener, ITaskProgressListener optProgressListener) {
this.id = id;
this.param = param;
this.bundle = bundle;
this.canCacheReferenceToParamResult = canCacheReferenceToParamResult;
this.listener = listener;
this.optProgressListener = optProgressListener;
}
public String getId() {
return this.id;
}
public ITaskParam getParam() {
return this.param;
}
public Bundle getBundle() {
return this.bundle;
}
@Override
public boolean canCacheReferenceToParamResult() {
return canCacheReferenceToParamResult;
}
public ITaskResultListener getListener() {
return this.listener;
}
@Override
public Object putProcessObj(String key, Object obj) {
if (processObjects == null)
processObjects = new HashMap<>();
return processObjects.put(key, obj);
}
@Override
public <T> T getProcessObj(String key) {
if (processObjects == null)
return null;
else
return (T)processObjects.get(key);
}
@Override
public void onTaskProgress(final int progress, final String progressState) {
synchronized (this) {
this.progress = progress;
this.progressState = progressState;
}
if (optProgressListener != null) {
handlerUi.post(new Runnable() {
@Override
public void run() {
if (headSameAsActive() && active == Task.this) {
optProgressListener.onTaskProgress(id, param, bundle, progress, progressState);
}
}
});
}
}
public synchronized boolean isCanceled() {
return canceled;
}
private synchronized void setCanceled() {
this.canceled = true;
}
@Override
public synchronized int getSkipCount() {
return this.skipCount;
}
private synchronized void addSkipCount(int skipCount) {
this.skipCount += skipCount;
}
@Override
public synchronized int getProgress() {
return this.progress;
}
@Override
public synchronized String getProgressState() {
return this.progressState;
}
}
}