package com.circlegate.liban.task; import java.util.ArrayList; import java.util.HashMap; import android.app.Activity; import android.os.Bundle; import android.os.SystemClock; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; import com.circlegate.liban.base.GlobalContextLib; import com.circlegate.liban.fragment.BaseRetainFragment; import com.circlegate.liban.task.TaskInterfaces.ITask; 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.EqualsUtils; import com.circlegate.liban.utils.FragmentUtils; public class TaskFragment extends BaseRetainFragment { private static final String FRAGMENT_TAG = "TaskFragment"; private ITaskExecutor executor; private final ArrayList<PendingTask> pendingPausedNewTasks = new ArrayList<PendingTask>(); private final ArrayList<PendingTask> pendingCompletedTasks = new ArrayList<PendingTask>(); private final ArrayList<ProgressTask> pendingProgressTasks = new ArrayList<ProgressTask>(); private boolean readyToCompleteTasks = false; private boolean pauseNewTasks = false; public static <T extends FragmentActivity & ITaskFragmentActivity> TaskFragment getInstance(T activity) { FragmentManager fm = activity.getSupportFragmentManager(); TaskFragment f = (TaskFragment)fm.findFragmentByTag(FRAGMENT_TAG); if (f == null) { f = new TaskFragment(); setupNewFragment(f, activity); fm.beginTransaction().add(f, FRAGMENT_TAG).commit(); } return f; } protected static void setupNewFragment(TaskFragment f, Activity activity) { f.executor = GlobalContextLib.get().getTaskExecutor(); } // // Lifecycle methods // @Override public void onAttach(Activity activity) { super.onAttach(activity); this.executor = GlobalContextLib.get().getTaskExecutor(); } @Override public void onResume() { super.onResume(); this.readyToCompleteTasks = true; // musim udelat tak, aby tesne pred spustenim onTaskProgress byl task z pendingProgressTasks odstranen while (!this.pendingProgressTasks.isEmpty()) { ProgressTask t = this.pendingProgressTasks.get(0); this.pendingProgressTasks.remove(0); onTaskProgress(t.getId(), t.getParam(), t.getBundle(), t.getProgress(), t.getProgressState(), t.getFragmentTag()); } finishPendingCompletedTasksIfCan(); } @Override public void onPause() { this.readyToCompleteTasks = false; super.onPause(); } @Override public void onSaveInstanceState(Bundle outState) { this.readyToCompleteTasks = false; super.onSaveInstanceState(outState); } @Override public void onDestroy() { super.onDestroy(); this.executor.cancelTasksByResultHandler(new TaskResultListener(this, true, null)); } // // Public methods // public boolean isPauseNewTasks() { return this.pauseNewTasks; } public void setPauseNewTasks(boolean pauseNewTasks) { if (this.pauseNewTasks != pauseNewTasks) { this.pauseNewTasks = pauseNewTasks; if (!pauseNewTasks) { PendingTask[] tmp = this.pendingPausedNewTasks.toArray(new PendingTask[this.pendingPausedNewTasks.size()]); this.pendingPausedNewTasks.clear(); for (PendingTask p : tmp) { executeTask(p.getId(), p.getParam(), p.getBundle(), p.canCacheReferenceToParamResult(), p.getFragmentTag()); } } } } public void executeTask(String id, ITaskParam param, Bundle bundle, boolean canCacheReferenceToParamResult, String fragmentTag) { if (pauseNewTasks) { pendingPausedNewTasks.add(new PendingTask(this, id, param, bundle, canCacheReferenceToParamResult, fragmentTag, null)); } else { TaskResultListener t = new TaskResultListener(this, false, fragmentTag); this.executor.executeTask(id, param, bundle, canCacheReferenceToParamResult, t, t); } } public boolean containsTask(String id, String fragmentTag) { return getTask(id, fragmentTag) != null; } public boolean containsAnyTaskByFragmentTag(String fragmentTag) { if (getPendingTaskIndByFragmentTag(pendingPausedNewTasks, fragmentTag) >= 0 || this.executor.containsAnyTaskByResultHandler(new TaskResultListener(this, false, fragmentTag)) || getPendingTaskIndByFragmentTag(pendingCompletedTasks, fragmentTag) >= 0) return true; else return false; } public ITask getTask(String id, String fragmentTag) { int pausedNewTaskInd = getPendingTaskInd(pendingPausedNewTasks, id, fragmentTag); if (pausedNewTaskInd >= 0) { return pendingPausedNewTasks.get(pausedNewTaskInd); } ITask ret = this.executor.getTask(id, new TaskResultListener(this, false, fragmentTag)); if (ret != null) { return ret; } int completedTaskInd = getPendingTaskInd(pendingCompletedTasks, id, fragmentTag); if (completedTaskInd >= 0) { return this.pendingCompletedTasks.get(completedTaskInd); } else return null; } public boolean cancelTask(String id, String fragmentTag) { int pausedNewTaskInd = getPendingTaskInd(pendingPausedNewTasks, id, fragmentTag); if (pausedNewTaskInd >= 0) { this.pendingPausedNewTasks.remove(pausedNewTaskInd); return true; } // Chci mit opravdu jistotu, ze ruseny task uz nezavola ani onTaskProgress for (int i = 0; i < this.pendingProgressTasks.size(); i++) { ProgressTask p = this.pendingProgressTasks.get(i); if (EqualsUtils.equalsCheckNull(p.getId(), id) && EqualsUtils.equalsCheckNull(p.getFragmentTag(), fragmentTag)) { pendingProgressTasks.remove(i); i--; } } boolean ret = this.executor.cancelTask(id, new TaskResultListener(this, false, fragmentTag)); if (ret) return ret; int completedTaskInd = getPendingTaskInd(pendingCompletedTasks, id, fragmentTag); if (completedTaskInd >= 0) { this.pendingCompletedTasks.remove(completedTaskInd); return true; } else return false; } public void cancelTasksByFragmentId(String fragmentTag) { this.executor.cancelTasksByResultHandler(new TaskResultListener(this, false, fragmentTag)); removePendingTasksByFragmentId(pendingPausedNewTasks, fragmentTag); removePendingTasksByFragmentId(pendingProgressTasks, fragmentTag); removePendingTasksByFragmentId(pendingCompletedTasks, fragmentTag); } public boolean addSkipCount(String id, String fragmentTag, int skipCount) { return this.executor.addSkipCount(id, new TaskResultListener(this, false, fragmentTag), skipCount); } // POZOR Pri pripadnem pridavani dalsich public metod myslet na WearableTaskFragment!! // // Callbacks // protected void onPreTaskProgress(String id, ITaskParam param, Bundle bundle, int progress, String progressState, String fragmentTag) { if (!this.readyToCompleteTasks) pendingProgressTasks.add(new ProgressTask(id, param, bundle, progress, progressState, fragmentTag)); else onTaskProgress(id, param, bundle, progress, progressState, fragmentTag); } protected void onTaskProgress(String id, ITaskParam param, Bundle bundle, int progress, String progressState, String fragmentTag) { final Object listener; if (fragmentTag == null) { listener = getActivity(); } else { listener = findTargetFragment(fragmentTag); } if (listener instanceof ITaskProgressListener) { ((ITaskProgressListener)listener).onTaskProgress(id, param, bundle, progress, progressState); } } protected void onPreTaskCompleted(String id, ITaskResult result, Bundle bundle, String fragmentTag) { if (!this.readyToCompleteTasks) { addPendingCompletedTask(id, result, bundle, false, fragmentTag); } else { onTaskCompleted(id, result, bundle, fragmentTag); } } protected void onTaskCompleted(String id, ITaskResult result, Bundle bundle, String fragmentTag) { final ITaskResultListener listener; if (fragmentTag == null) { listener = (ITaskResultListener)getActivity(); } else { listener = findTargetFragment(fragmentTag); } if (listener != null) { listener.onTaskCompleted(id, result, bundle); } } protected void addPendingCompletedTask(String id, ITaskResult result, Bundle bundle, boolean canCacheReferenceToParamResult, String fragmentTag) { pendingCompletedTasks.add(new PendingTask(this, id, result.getParam(), bundle, canCacheReferenceToParamResult, fragmentTag, result)); } protected void finishPendingCompletedTasksIfCan() { if (readyToCompleteTasks) { // musim udelat tak, aby tesne pred spustenim onTaskCompleted byl task z pendindCompletedTasks odstranen while (!this.pendingCompletedTasks.isEmpty()) { PendingTask t = this.pendingCompletedTasks.get(0); this.pendingCompletedTasks.remove(0); onTaskCompleted(t.getId(), t.getResult(), t.getBundle(), t.getFragmentTag()); } } } // // PRIVATE // private ITaskResultListener findTargetFragment(String fragmentTag) { return (ITaskResultListener)FragmentUtils.findFragmentByNestedTag(getActivity(), fragmentTag); } protected static int getPendingTaskInd(ArrayList<PendingTask> tasks, String id, String fragmentTag) { for (int i = 0; i < tasks.size(); i++) { PendingTask p = tasks.get(i); if (EqualsUtils.equalsCheckNull(p.getId(), id) && EqualsUtils.equalsCheckNull(p.getFragmentTag(), fragmentTag)) { return i; } } return -1; } protected static int getPendingTaskIndByFragmentTag(ArrayList<PendingTask> tasks, String fragmentTag) { for (int i = 0; i < tasks.size(); i++) { PendingTask p = tasks.get(i); if (EqualsUtils.equalsCheckNull(p.getFragmentTag(), fragmentTag)) { return i; } } return -1; } protected static void removePendingTasksByFragmentId(ArrayList<? extends IHasFragmentTag> tasks, String fragmentTag) { for (int i = 0; i < tasks.size(); i++) { IHasFragmentTag p = tasks.get(i); if (EqualsUtils.equalsCheckNull(p.getFragmentTag(), fragmentTag)) { tasks.remove(i); i--; } } } // // INNER CLASSES // private static class TaskResultListener implements ITaskResultListener, ITaskProgressListener { private final TaskFragment taskFragment; private final boolean ignoreFragmentTagForEquals; private final String fragmentTag; private TaskResultListener(TaskFragment taskFragment, boolean ignoreFragmentTagForEquals, String fragmentTag) { this.taskFragment = taskFragment; this.ignoreFragmentTagForEquals = ignoreFragmentTagForEquals; this.fragmentTag = fragmentTag; } @Override public void onTaskCompleted(String id, ITaskResult result, Bundle bundle) { this.taskFragment.onPreTaskCompleted(id, result, bundle, this.fragmentTag); } @Override public void onTaskProgress(String id, ITaskParam param, Bundle bundle, int progress, String progressState) { this.taskFragment.onPreTaskProgress(id, param, bundle, progress, progressState, fragmentTag); } @Override public int hashCode() { // Nemuzu brat v potaz fragmentTag kvuli ignoreFragmentTagForEquals return taskFragment.hashCode(); } /** * Pokud je ignoreFragmentTagForEquals jednoho z obou objektu = true, tak se nebere v potaz fragmentTag */ @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof TaskResultListener)) { return false; } TaskResultListener lhs = (TaskResultListener) o; return lhs != null && taskFragment.equals(lhs.taskFragment) && (ignoreFragmentTagForEquals || lhs.ignoreFragmentTagForEquals || (EqualsUtils.equalsCheckNull(fragmentTag, lhs.fragmentTag))); } } protected interface IHasFragmentTag { String getFragmentTag(); } protected static class PendingTask implements IHasFragmentTag, ITask, ITaskResultListener, ITaskProgressListener { private final TaskFragment taskFragment; private final String id; private final ITaskParam param; private final Bundle bundle; private final boolean canCacheReferenceToParamResult; private final String fragmentTag; private final ITaskResult result; // optional - jenom u uz hotovych tasku... private final long timeStamp; private HashMap<String, Object> processObjects; // lazy loaded // Upraveno! public PendingTask(TaskFragment taskFragment, String id, ITaskParam param, Bundle bundle, boolean canCacheReferenceToParamResult, String fragmentTag, ITaskResult result) { this.taskFragment = taskFragment; this.id = id; this.param = param; this.bundle = bundle; this.canCacheReferenceToParamResult = canCacheReferenceToParamResult; this.fragmentTag = fragmentTag; this.result = result; this.timeStamp = SystemClock.elapsedRealtime(); } public String getId() { return this.id; } public ITaskParam getParam() { return this.param; } public Bundle getBundle() { return this.bundle; } @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); } public String getFragmentTag() { return this.fragmentTag; } public ITaskResult getResult() { return this.result; } public long getTimeStamp() { return this.timeStamp; } @Override public boolean isCanceled() { return false; } @Override public int getSkipCount() { return 0; } @Override public int getProgress() { return 0; } @Override public String getProgressState() { return null; } @Override public ITaskResultListener getListener() { return this; } @Override public boolean canCacheReferenceToParamResult() { return canCacheReferenceToParamResult; } @Override public void onTaskProgress(int progress, String progressState) { onTaskProgress(id, param, bundle, progress, progressState); } @Override public void onTaskCompleted(String id, ITaskResult result, Bundle bundle) { this.taskFragment.onPreTaskCompleted(id, result, bundle, this.fragmentTag); } @Override public void onTaskProgress(String id, ITaskParam param, Bundle bundle, int progress, String progressState) { this.taskFragment.onPreTaskProgress(id, param, bundle, progress, progressState, fragmentTag); } } private static class ProgressTask implements IHasFragmentTag { private final String id; private final ITaskParam param; private final Bundle bundle; private final int progress; private final String progressState; private final String fragmentTag; public ProgressTask(String id, ITaskParam param, Bundle bundle, int progress, String progressState, String fragmentTag) { this.id = id; this.param = param; this.bundle = bundle; this.progress = progress; this.progressState = progressState; this.fragmentTag = fragmentTag; } public String getId() { return this.id; } public ITaskParam getParam() { return this.param; } public Bundle getBundle() { return this.bundle; } public int getProgress() { return this.progress; } public String getProgressState() { return this.progressState; } public String getFragmentTag() { return this.fragmentTag; } } public interface ITaskFragmentActivity { TaskFragment getTaskFragment(); } }