package gem.kevin.task;
import gem.kevin.util.FileUtil;
import gem.kevin.util.FileUtil.CopyCallback;
import gem.kevin.util.FileUtil.DeleteCallback;
import gem.kevin.util.FileUtil.RenameCallback;
import gem.kevin.util.FileUtil.SearchCallback;
import gem.kevin.util.FileUtil.SimpleCopyCallback;
import gem.kevin.util.FileUtil.SimpleDeleteCallback;
import gem.kevin.util.FileUtil.SimpleRenameCallback;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import android.content.Context;
import android.util.Log;
public class FileOperationTask implements CopyCallback, RenameCallback,
DeleteCallback, SearchCallback {
public interface ClipOpTaskListener extends FileOpTaskListener {
public String localeSensitiveCopyName(String srcName,
int existDuplicateCount);
public void onClipOpTaskCanceled(FileOperationTask canceledTask);
public void onClipOpTaskEvaluating(FileOperationTask evaluatingTask);
public void onClipOpTaskFinished(FileOperationTask finishedTask);
public void onClipOpTaskPendingOnCopyError(
FileOperationTask pendingTask, String srcPath,
String targetPath, int errorCode);
public void onClipOpTaskPendingOnDeleteError(
FileOperationTask pendingTask, String srcPath, int errorCode);
public void onClipOpTaskPendingOnExists(FileOperationTask pendingTask,
String srcPath, String existPath);
public void onClipOpTaskPendingOnRenameError(
FileOperationTask pendingTask, String srcPath,
String targetPath, int errorCode);
public void onClipOpTaskProgressing(FileOperationTask processingTask);
public void onClipOpTaskReady(FileOperationTask readyTask);
public void onClipOpTaskRollbacked(FileOperationTask newTask);
public void onClipOpTaskStarted(FileOperationTask startedTask);
public void onClipOpTaskStopped(FileOperationTask stoppedTask);
public void onClipOpTaskUnavailable(FileOperationTask unavailableTask,
int reason);
}
public static class FileOperationOptions {
public static final String KEY_SEARCH_DIR = "search_dir";
public static final String KEY_SEARCH_RESEVERD_DIR = "search_reserved_dir";
public static final String KEY_SEARCH_OUTPUT = "search_output";
public static final String KEY_SEARCH_SCOPE = "search_scope";
public static final String KEY_SEARCH_DEPTH = "search_depth";
public static final String KEY_SEARCH_RECORD_LIMIT = "search_record_limit";
public int sourceType;
public int targetType;
public String targetDirectoryPath;
public String targetFilePath;
public String operationType;
public boolean overwriteAllExist = false;
public boolean skipAllExist = false;
public boolean skipAllCopyError = false;
public boolean skipAllDeleteError = false;
public boolean skipAllRenameError = false;
public int searchType = -1;
public HashMap<String, Object> searchParams;
public FileOperationOptions(FileOperationOptions options) {
this.operationType = options.operationType;
this.sourceType = options.sourceType;
this.targetType = options.targetType;
this.targetDirectoryPath = options.targetDirectoryPath;
this.targetFilePath = options.targetFilePath;
}
public FileOperationOptions(String operationType, int sourceType,
int targetType, String targetDirectoryPath,
String targetFilePath) {
this.operationType = operationType;
this.sourceType = sourceType;
this.targetType = targetType;
this.targetDirectoryPath = targetDirectoryPath;
this.targetFilePath = targetFilePath;
}
public void setSearchParams(HashMap<String, Object> searchParams) {
this.searchParams = searchParams;
}
public void setSearchType(int searchType) {
this.searchType = searchType;
}
}
public interface FileOpTaskListener {
}
public interface SearchOpTaskListener extends FileOpTaskListener {
public void onSearchOpTaskCancelled(FileOperationTask task);
public void onSearchOpTaskFinished(FileOperationTask task);
public void onSearchOpTaskProgressing(FileOperationTask task,
String searchDirPath, boolean dirFinished);
}
private static final String TAG = "FileOperationTask";
private static final boolean KLOG = true;
public static final String OP_TYPE_NEW_FILE = "new_file";
public static final String OP_TYPE_COPY_FILE = "copy_file";
public static final String OP_TYPE_MOVE_FILE = "move_file";
public static final String OP_TYPE_RENAME_FILE = "rename_file";
public static final String OP_TYPE_DELETE_FILE = "delete_file";
public static final String OP_TYPE_SEARCH_FILE = "search_file";
public static final int OP_AVAILABLE = 0;
public static final int OP_UNAVAILABLE_SRC_NULL = 0x0001;
public static final int OP_UNAVAILABLE_DEST_NULL = 0x0010;
public static final int OP_UNAVAILABLE_SRC_EMPTY = 0x0100;
public static final int OP_UNAVAILABLE_DEST_NOSPACE = 0x1000;
public static final int FILE_TYPE_EMPTY = FileUtil.FILE_TYPE_EMPTY;
public static final int FILE_TYPE_FILE = FileUtil.FILE_TYPE_FILE;
public static final int FILE_TYPE_DIRECTORY = FileUtil.FILE_TYPE_DIRECTORY;
public static final int FILE_TYPE_FILESET = FileUtil.FILE_TYPE_FILESET;
public static final int TASK_NEW = 1;
public static final int TASK_ROLLBACKED = 2;
public static final int TASK_EVALUATING = 3;
public static final int TASK_READY = 4;
public static final int TASK_STARTED = 5;
public static final int TASK_FINISHED = 6;
public static final int TASK_STOPPED = 7;
public static final int TASK_CANCELING = 8;
public static final int TASK_CANCELED = 9;
public static final int TASK_RUNNING = 10;
public static final int TASK_PAUSED = 11;
public static final int TASK_RESUMED = 12;
public static final FileOperationOptions DEFAULT_NEW_OPTIONS = new FileOperationOptions(
OP_TYPE_NEW_FILE, FILE_TYPE_EMPTY, FILE_TYPE_DIRECTORY, null, null);
public static final FileOperationOptions DEFAULT_COPY_OPTIONS = new FileOperationOptions(
OP_TYPE_COPY_FILE, FILE_TYPE_FILESET, FILE_TYPE_FILESET, null, null);
public static final FileOperationOptions DEFAULT_MOVE_OPTIONS = new FileOperationOptions(
OP_TYPE_MOVE_FILE, FILE_TYPE_FILESET, FILE_TYPE_FILESET, null, null);
public static final FileOperationOptions DEFAULT_RENAME_OPTIONS = new FileOperationOptions(
OP_TYPE_RENAME_FILE, FILE_TYPE_FILESET, FILE_TYPE_FILESET, null,
null);
public static final FileOperationOptions DEFAULT_DELETE_OPTIONS = new FileOperationOptions(
OP_TYPE_DELETE_FILE, FILE_TYPE_FILESET, FILE_TYPE_EMPTY, null, null);
public static final FileOperationOptions DEFAULT_SEARCH_OPTIONS = new FileOperationOptions(
OP_TYPE_SEARCH_FILE, FILE_TYPE_FILESET, FILE_TYPE_FILESET, null,
null);
public static FileOperationOptions makeDefaultOptions(String operationType) {
if (operationType.equals(OP_TYPE_NEW_FILE)) {
return new FileOperationOptions(DEFAULT_NEW_OPTIONS);
} else if (operationType.equals(OP_TYPE_COPY_FILE)) {
return new FileOperationOptions(DEFAULT_COPY_OPTIONS);
} else if (operationType.equals(OP_TYPE_MOVE_FILE)) {
return new FileOperationOptions(DEFAULT_MOVE_OPTIONS);
} else if (operationType.equals(OP_TYPE_RENAME_FILE)) {
return new FileOperationOptions(DEFAULT_RENAME_OPTIONS);
} else if (operationType.equals(OP_TYPE_DELETE_FILE)) {
return new FileOperationOptions(DEFAULT_DELETE_OPTIONS);
} else if (operationType.equals(OP_TYPE_SEARCH_FILE)) {
return new FileOperationOptions(DEFAULT_SEARCH_OPTIONS);
} else {
return null;
}
}
private final String mTaskId;
private Context mContext;
private FileOperationOptions mOptions;
private long mRequiredSpace = UNINTIALIZED;
private long mAvailableSpace = UNINTIALIZED;
private ArrayList<String> mSourceFilePaths;
private int mSubTaskNum = 0;
private String mProcessingSourceFilePath;
private int mSucceedSubTaskNum = 0;
private int mProcessedSubTaskNum = 0;
private int mSucceedPercentage = 0;
private int mProcessedPercentage = 0;
private static final long UNINTIALIZED = 0;
private long mSourceCopySize = UNINTIALIZED;
private long mSourceFileCount = UNINTIALIZED;
private boolean mDeleteSource = false;
// These fields shouldn't be used outside, they are using for
// evaluation,
// which are not represent exact properties of file in the task
private long mTotalCopiedBytes = UNINTIALIZED;
private long mTotalDeletedCount = UNINTIALIZED;
private long mTotalRenamedCount = UNINTIALIZED;
private int mTaskState;
private HashSet<FileOpTaskListener> mListeners;
private Runnable mWorkProcedure;
private Runnable mEvaluateProcedure;
private Thread mWorkThread;
private Thread mEvaluateThread;
private boolean mCanceled = false;
private boolean mPaused = false;
private final Object mPauseLock = new Object();
private boolean mSkipExistOnce = false;
private boolean mOverwriteExistOnce = false;
private boolean mSkipCopyErrorOnce = false;
private boolean mSkipRenameErrorOnce = false;
private boolean mSkipDeleteErrorOnce = false;
public FileOperationTask(Context context, String taskId,
FileOperationOptions options, ArrayList<String> sourceFilePaths) {
this(context, taskId, options, sourceFilePaths, null);
}
public FileOperationTask(Context context, String taskId,
FileOperationOptions options, ArrayList<String> sourceFilePaths,
FileOpTaskListener listener) {
mContext = context;
mTaskId = taskId;
mOptions = options;
mDeleteSource = (mOptions.operationType.equals(OP_TYPE_MOVE_FILE));
mSourceFilePaths = sourceFilePaths;
if (mSourceFilePaths != null) {
mSubTaskNum = mSourceFilePaths.size();
}
mTaskState = TASK_NEW;
mListeners = new HashSet<FileOpTaskListener>();
if (listener != null) {
registerListener(listener);
}
if (mSourceFilePaths != null) {
mSubTaskNum = mSourceFilePaths.size();
}
initEvaluateThread();
initWorkThread();
}
public long calculateSourceCount() {
long totalCount = 0;
for (String path : mSourceFilePaths) {
long count = FileUtil.getSubFileCount(path);
totalCount += (count > 1) ? count : 1;
}
return totalCount;
}
public long calculateSourceSize() {
long totalSize = 0;
for (String path : mSourceFilePaths) {
long size = FileUtil.getFileSize(path);
totalSize += (size > 0) ? size : 0;
}
return totalSize;
}
public void cancel() {
if (KLOG) {
Log.i(TAG, "File task of type:" + mOptions.operationType
+ " canceled.");
}
mCanceled = true;
notify(mTaskState = TASK_CANCELED);
// Resume working thread so necessary steps can be finished
synchronized (mPauseLock) {
mPaused = false;
mPauseLock.notifyAll();
}
}
@Override
public boolean copyProgressCanceled() {
return mCanceled;
}
@Override
public boolean copyProgressPaused() {
return mPaused;
}
@Override
public boolean deleteProgressCanceled() {
return mCanceled;
}
@Override
public boolean deleteProgressPaused() {
return mPaused;
}
public void evaluate() {
notify(mTaskState = TASK_EVALUATING);
mEvaluateThread.start();
}
public int evaluateOpAvailability() {
int result = OP_AVAILABLE;
if (mOptions.operationType.equals(OP_TYPE_COPY_FILE)
|| mOptions.operationType.equals(OP_TYPE_MOVE_FILE)) {
if (mOptions.targetDirectoryPath == null) {
result |= OP_UNAVAILABLE_DEST_NULL;
}
}
if (mSourceFilePaths == null) {
result |= OP_UNAVAILABLE_SRC_NULL;
} else {
if (mSourceFilePaths.size() == 0) {
result |= OP_UNAVAILABLE_SRC_EMPTY;
}
}
return result;
}
@Override
public String generateCopyName(String srcName, int existDuplicateCount) {
// Handle over this staff to FileOperationListener,
// make FileOperationTask pure non UI/resource code
String copyName = null;
synchronized (mListeners) {
for (FileOpTaskListener listener : mListeners) {
if (listener instanceof ClipOpTaskListener) {
copyName = ((ClipOpTaskListener) listener)
.localeSensitiveCopyName(srcName,
existDuplicateCount);
if (copyName != null) {
return copyName;
}
}
}
}
return copyName;
}
public long getAvailableSpace() {
return mAvailableSpace;
}
public String getCurrentProcessingFilePath() {
return mProcessingSourceFilePath;
}
public FileOperationOptions getOptions() {
return mOptions;
}
@Override
public Object getPauseLock() {
return mPauseLock;
}
public int getProcessedPecentage() {
return mProcessedPercentage;
}
public int getProcessedSubTaskNum() {
return mProcessedSubTaskNum;
}
public long getRequiredSpace() {
return mRequiredSpace;
}
public long getSouceCopySize() {
return mSourceCopySize;
}
public long getSourceFileCount() {
return mSourceFileCount;
}
public int getState() {
return mTaskState;
}
public int getSubTaskNum() {
return mSubTaskNum;
}
public int getSucceedPecentage() {
return mSucceedPercentage;
}
public int getSucceedSubTaskNum() {
return mSucceedSubTaskNum;
}
public boolean hasCanceled() {
return (mTaskState == TASK_CANCELED);
}
public boolean hasFinished() {
return (mTaskState == TASK_FINISHED);
}
public boolean isIdle() {
return (mTaskState == TASK_NEW) || (mTaskState == TASK_ROLLBACKED)
|| (mTaskState == TASK_READY) || (mTaskState == TASK_FINISHED)
|| (mTaskState == TASK_CANCELED)
|| (mTaskState == TASK_STOPPED);
}
public void notify(int taskState) {
synchronized (mListeners) {
for (FileOpTaskListener listener : mListeners) {
if (listener instanceof ClipOpTaskListener) {
ClipOpTaskListener clipListener = (ClipOpTaskListener) listener;
switch (taskState) {
case TASK_EVALUATING:
clipListener.onClipOpTaskEvaluating(this);
break;
case TASK_READY:
clipListener.onClipOpTaskReady(this);
break;
case TASK_STARTED:
clipListener.onClipOpTaskStarted(this);
break;
case TASK_STOPPED:
clipListener.onClipOpTaskStopped(this);
break;
case TASK_FINISHED:
clipListener.onClipOpTaskFinished(this);
break;
case TASK_RUNNING:
clipListener.onClipOpTaskProgressing(this);
break;
case TASK_CANCELED:
clipListener.onClipOpTaskCanceled(this);
break;
case TASK_ROLLBACKED:
clipListener.onClipOpTaskRollbacked(this);
break;
default:
return;
}
}
}
}
}
@Override
public void onBytesCopied(String srcPath, long copiedBytes) {
long currentCopiedBytes = mTotalCopiedBytes + copiedBytes;
if (mSourceCopySize > 0) {
int evalValue = (int) (currentCopiedBytes * 100 / mSourceCopySize);
if (evalValue > 100) {
mSucceedPercentage = 100;
} else {
mSucceedPercentage = evalValue;
}
} else {
mSucceedPercentage = 100;
}
if (mSucceedPercentage < 100) {
notify(mTaskState = TASK_RUNNING);
} else {
notify(mTaskState = TASK_FINISHED);
}
}
@Override
public void onCopyError(String srcFilePath, String targetFilePath,
int errorCode) {
synchronized (mListeners) {
for (FileOpTaskListener listener : mListeners) {
if (listener instanceof ClipOpTaskListener) {
((ClipOpTaskListener) listener)
.onClipOpTaskPendingOnCopyError(this, srcFilePath,
targetFilePath, errorCode);
}
}
}
}
@Override
public void onCopyProgressFinished(String srcFilePath,
String copiedFilePath, long copied) {
if (copiedFilePath.endsWith(FileUtil.COPYING_FILE_SUFFIX)) {
FileUtil.renameFile(
copiedFilePath,
copiedFilePath.substring(0, copiedFilePath.length()
- FileUtil.COPYING_FILE_SUFFIX.length()), null);
}
mTotalCopiedBytes += copied;
if (mSourceCopySize > 0) {
int evalValue = (int) (mTotalCopiedBytes * 100 / mSourceCopySize);
if (evalValue > 100) {
mSucceedPercentage = 100;
} else {
mSucceedPercentage = evalValue;
}
}
if (mSucceedPercentage < 100) {
notify(mTaskState = TASK_RUNNING);
} else {
notify(mTaskState = TASK_FINISHED);
}
if (mContext != null && mContext instanceof SimpleCopyCallback) {
((SimpleCopyCallback) mContext).onFileCopied(srcFilePath,
copiedFilePath);
}
}
@Override
public void onDeleteError(String filePath, int errorCode) {
synchronized (mListeners) {
for (FileOpTaskListener listener : mListeners) {
if (listener instanceof ClipOpTaskListener) {
((ClipOpTaskListener) listener)
.onClipOpTaskPendingOnDeleteError(this, filePath,
errorCode);
}
}
}
}
@Override
public void onDeleteFinished(String filePath) {
mTotalDeletedCount += 1;
if (mSourceFileCount > 1) {
int evalValue = (int) (mTotalDeletedCount * 100 / mSourceFileCount);
if (evalValue > 100) {
mSucceedPercentage = 100;
} else {
mSucceedPercentage = evalValue;
}
} else {
mSucceedPercentage = 100;
}
if (mSucceedPercentage < 100) {
notify(mTaskState = TASK_RUNNING);
} else {
notify(mTaskState = TASK_FINISHED);
}
if (mContext != null && mContext instanceof SimpleDeleteCallback) {
((SimpleDeleteCallback) mContext).onFileDeleted(filePath);
}
}
@Override
public void onDirSearched(String searchedDirPath) {
synchronized (mListeners) {
for (FileOpTaskListener listener : mListeners) {
if (listener instanceof SearchOpTaskListener) {
((SearchOpTaskListener) listener)
.onSearchOpTaskProgressing(this, searchedDirPath,
true);
}
}
}
}
@Override
public void onDirSearching(String searchingDirPath) {
synchronized (mListeners) {
for (FileOpTaskListener listener : mListeners) {
if (listener instanceof SearchOpTaskListener) {
((SearchOpTaskListener) listener)
.onSearchOpTaskProgressing(this, searchingDirPath,
false);
}
}
}
}
@Override
public void onExistDirDetected(String srcDirPath, String existDirPath) {
pause();
synchronized (mListeners) {
for (FileOpTaskListener listener : mListeners) {
if (listener instanceof ClipOpTaskListener) {
((ClipOpTaskListener) listener)
.onClipOpTaskPendingOnExists(this, srcDirPath,
existDirPath);
}
}
}
}
@Override
public void onExistFileDetected(String srcFilePath, String existFilePath) {
synchronized (mListeners) {
for (FileOpTaskListener listener : mListeners) {
if (listener instanceof ClipOpTaskListener) {
((ClipOpTaskListener) listener)
.onClipOpTaskPendingOnExists(this, srcFilePath,
existFilePath);
}
}
}
}
public void onOperationUnavailable(int reason) {
synchronized (mListeners) {
for (FileOpTaskListener listener : mListeners) {
if (listener instanceof ClipOpTaskListener) {
((ClipOpTaskListener) listener).onClipOpTaskUnavailable(
this, reason);
}
}
}
}
@Override
public void onPercentageCopied(String srcPath, int copiedPercentage) {
if (srcPath != null) {
mProcessingSourceFilePath = srcPath;
}
}
@Override
public void onRenameError(String srcFilePath, String targetFilePath,
int errorCode) {
synchronized (mListeners) {
for (FileOpTaskListener listener : mListeners) {
if (listener instanceof ClipOpTaskListener) {
((ClipOpTaskListener) listener)
.onClipOpTaskPendingOnRenameError(this,
srcFilePath, targetFilePath, errorCode);
}
}
}
}
@Override
public void onRenameFinished(String srcFilePath, String renamedFilePath) {
mTotalRenamedCount++;
if (mSourceFileCount > 1) {
int evalValue = (int) (mTotalRenamedCount * 100 / mSourceFileCount);
if (evalValue > 100) {
mSucceedPercentage = 100;
} else {
mSucceedPercentage = evalValue;
}
} else {
mSucceedPercentage = 100;
}
if (mSucceedPercentage < 100) {
notify(mTaskState = TASK_RUNNING);
} else {
notify(mTaskState = TASK_FINISHED);
}
if (mContext != null && mContext instanceof SimpleRenameCallback) {
((SimpleRenameCallback) mContext).onFileRenamed(srcFilePath,
renamedFilePath);
}
}
@Override
public void onSearchCancelled() {
synchronized (mListeners) {
for (FileOpTaskListener listener : mListeners) {
if (listener instanceof SearchOpTaskListener) {
((SearchOpTaskListener) listener)
.onSearchOpTaskCancelled(this);
}
}
}
}
@Override
public void onSearchFinished() {
synchronized (mListeners) {
for (FileOpTaskListener listener : mListeners) {
if (listener instanceof SearchOpTaskListener) {
((SearchOpTaskListener) listener)
.onSearchOpTaskFinished(this);
}
}
}
}
@Override
public boolean overwriteExist() {
if (mOptions.overwriteAllExist) {
return true;
}
if (mOverwriteExistOnce) {
mOverwriteExistOnce = false;
return true;
} else {
return false;
}
}
@Override
public void pause() {
synchronized (mPauseLock) {
mPaused = true;
}
if (KLOG) {
Log.i(TAG, "File task of type:" + mOptions.operationType
+ " paused.");
}
notify(mTaskState = TASK_PAUSED);
}
public void registerListener(FileOpTaskListener listener) {
synchronized (mListeners) {
mListeners.add(listener);
}
}
@Override
public boolean renameProgressCanceled() {
return mCanceled;
}
@Override
public boolean renameProgressPaused() {
return mPaused;
}
public void resume() {
synchronized (mPauseLock) {
mPaused = false;
mPauseLock.notifyAll();
}
if (KLOG) {
Log.i(TAG, "File task of type:" + mOptions.operationType
+ " resumed.");
}
notify(mTaskState = TASK_RESUMED);
}
@Override
public void saveCopyProgress(String srcPath, long position) {
// TODO Auto-generated method stub
// If we wan't to make file operation task resumable even after the
// application quit,
// We can save task states and serialize necessary info for the
// resuming.
}
@Override
public boolean searchProgressCanceled() {
return mCanceled;
}
public void setOverwriteExistOnce(boolean overwrite) {
mOverwriteExistOnce = overwrite;
}
public void setSkipCopyErrorOnce(boolean skip) {
mSkipCopyErrorOnce = skip;
}
public void setSkipDeleteErrorOnce(boolean skip) {
mSkipDeleteErrorOnce = skip;
}
public void setSkipExistOnce(boolean skip) {
mSkipExistOnce = skip;
}
public void setSkipRenameErrorOnce(boolean skip) {
mSkipRenameErrorOnce = skip;
}
@Override
public boolean skipCopyError() {
if (mOptions.skipAllCopyError) {
return true;
}
if (mSkipCopyErrorOnce) {
mSkipCopyErrorOnce = false;
return true;
} else {
return false;
}
}
@Override
public boolean skipDeleteError() {
if (mOptions.skipAllDeleteError) {
return true;
}
if (mSkipDeleteErrorOnce) {
mSkipDeleteErrorOnce = false;
return true;
} else {
return false;
}
}
@Override
public boolean skipExist() {
if (mOptions.skipAllExist) {
return true;
}
if (mSkipExistOnce) {
mSkipExistOnce = false;
return true;
} else {
return false;
}
}
@Override
public boolean skipRenameError() {
if (mOptions.skipAllRenameError) {
return true;
}
if (mSkipRenameErrorOnce) {
mSkipRenameErrorOnce = false;
return true;
} else {
return false;
}
}
public void start() {
// Task can only start on ready state
if (mTaskState != TASK_READY) {
evaluate();
} else {
mWorkThread.start();
}
if (KLOG) {
Log.i(TAG, "File task of type:" + mOptions.operationType
+ " started.");
}
notify(mTaskState = TASK_STARTED);
}
private long evaluateCopySize() {
if (mSourceFilePaths == null || mSourceFilePaths.size() == 0) {
return UNINTIALIZED;
} else {
if (mSourceCopySize == UNINTIALIZED) {
mSourceCopySize = calculateSourceSize();
}
return mSourceCopySize;
}
}
private long evaluateFileCount() {
if (mSourceFilePaths == null || mSourceFilePaths.size() == 0) {
return UNINTIALIZED;
} else {
if (mSourceFileCount == UNINTIALIZED) {
mSourceFileCount = calculateSourceCount();
}
return mSourceFileCount;
}
}
private void initEvaluateThread() {
mEvaluateProcedure = new Runnable() {
@Override
public void run() {
if (KLOG) {
Log.i(TAG, "Evaluating thread id: "
+ Thread.currentThread().getId());
}
int availablility = evaluateOpAvailability();
if (availablility != OP_AVAILABLE) {
FileOperationTask.this
.onOperationUnavailable(availablility);
FileOperationTask.this
.notify(FileOperationTask.this.mTaskState = TASK_ROLLBACKED);
return;
}
if (mOptions.operationType.equals(OP_TYPE_COPY_FILE)
|| mOptions.operationType.equals(OP_TYPE_MOVE_FILE)) {
mRequiredSpace = evaluateCopySize();
if (KLOG) {
Log.i(TAG, "Space: " + mRequiredSpace
+ " required by file task: "
+ FileOperationTask.this.mTaskId);
}
/*
* StorageUtil storageUtil = StorageUtil.getSingleton(null);
* if (storageUtil == null) { // If storage utility is
* unavailable, skip space status check // and rely on
* working thread to detect NOSPACE error Log.i(TAG,
* "Skip file system status evaluation due to storage utility unavailable"
* ); } else { StorageVolumeStatFs statFs =
* storageUtil.statFsContainerFileSystem
* (mOptions.targetDirectoryPath); if (statFs != null) {
* mAvailableSpace = statFs.getAvailableSize(); } }
*/
// (ToT) API 'File::getFreeSpace()' already added in API
// level 9
mAvailableSpace = new File(mOptions.targetDirectoryPath)
.getFreeSpace();
if (KLOG) {
Log.i(TAG, "Space: " + mAvailableSpace + " available.");
}
if (mRequiredSpace > mAvailableSpace) {
FileOperationTask.this
.onOperationUnavailable(OP_UNAVAILABLE_DEST_NOSPACE);
FileOperationTask.this
.notify(FileOperationTask.this.mTaskState = TASK_ROLLBACKED);
return;
}
} else if (mOptions.operationType.equals(OP_TYPE_DELETE_FILE)) {
evaluateFileCount();
} else if (mOptions.operationType.equals(OP_TYPE_RENAME_FILE)) {
evaluateFileCount();
} else {
// ...
}
FileOperationTask.this
.notify(FileOperationTask.this.mTaskState = TASK_READY);
FileOperationTask.this.start();
}
};
mEvaluateThread = new Thread(mEvaluateProcedure);
}
private void initWorkThread() {
mWorkProcedure = new Runnable() {
@Override
public void run() {
if (KLOG) {
Log.i(TAG, "Working thread id: "
+ Thread.currentThread().getId());
}
for (String srcPath : mSourceFilePaths) {
if (mCanceled) {
break;
}
if (mOptions.operationType.equals(OP_TYPE_COPY_FILE)
|| mOptions.operationType.equals(OP_TYPE_MOVE_FILE)) {
if (mOptions.targetDirectoryPath == null) {
// TODO: on operation unavailable
Log.i(TAG, "Operation unavailable.");
return;
}
int copyResult;
try {
Log.i(TAG,
"before starting interruptible copy for src path: "
+ srcPath);
copyResult = FileUtil.interruptibleCopy(srcPath,
mOptions.targetDirectoryPath, 0,
mOptions.overwriteAllExist ? true : false,
mOptions.skipAllExist ? true : false,
mOptions.skipAllCopyError ? true : false,
FileOperationTask.this,
mOptions.operationType
.equals(OP_TYPE_MOVE_FILE));
} catch (Exception e) {
e.printStackTrace();
copyResult = FileUtil.ERROR_OP_UNAVAILABLE;
}
if (copyResult == FileUtil.OP_SUCCESS) {
mSucceedSubTaskNum++;
mProcessedSubTaskNum++;
if (mDeleteSource) {
try {
FileUtil.deleteFile(srcPath, true,
FileOperationTask.this);
} catch (Exception e) {
e.printStackTrace();
}
}
} else if (copyResult == FileUtil.OP_CANCELED) {
return;
} else if (copyResult == FileUtil.OP_SKIP) {
// TODO: make use of skipped info
mProcessedSubTaskNum++;
continue;
} else if (copyResult == FileUtil.ERROR_OP_UNAVAILABLE) {
mProcessedSubTaskNum++;
return;
} else { // Failed, go to next sub task
if (KLOG) {
Log.i(TAG, "Failed to paste file: " + srcPath
+ " in dir: "
+ mOptions.targetDirectoryPath
+ " due to error:" + copyResult);
}
mProcessedSubTaskNum++;
}
} else if (mOptions.operationType
.equals(OP_TYPE_RENAME_FILE)) {
if (mOptions.targetDirectoryPath == null
&& mOptions.targetFilePath == null) {
if (KLOG) {
Log.e(TAG,
"Neither target directory or target file path is specified, can't rename.");
}
// TODO: on operation unavailable
return;
}
// Directly 'Rename'
if (mOptions.targetFilePath != null) {
int renameResult = FileUtil
.renameFile(
srcPath,
mOptions.targetFilePath,
(mContext instanceof SimpleRenameCallback) ? (SimpleRenameCallback) mContext
: null);
if (renameResult == FileUtil.OP_SUCCESS) {
mSucceedSubTaskNum++;
mProcessedSubTaskNum++;
} else {
// Failed
mProcessedSubTaskNum++;
}
// Should act on single file
break;
}
// 'Rename' for 'Cut' actions (move on same file
// system)
if (mOptions.targetDirectoryPath != null) {
String targetPath = mOptions.targetDirectoryPath
+ "/"
+ srcPath.substring(
srcPath.lastIndexOf("/") + 1,
srcPath.length());
int renameResult;
if (mOptions.targetDirectoryPath.equals(FileUtil
.getParentDirPath(srcPath))) {
Log.i(TAG, "paste on same path, skip!");
renameResult = FileUtil.OP_SUCCESS;
} else {
renameResult = FileUtil
.renameFile(
srcPath,
targetPath,
mOptions.skipAllRenameError ? true
: false,
FileOperationTask.this);
}
if (renameResult == FileUtil.OP_SUCCESS) {
if (KLOG) {
Log.i(TAG, "Move file: " + srcPath + " to "
+ mOptions.targetDirectoryPath
+ " succeed.");
}
mSucceedSubTaskNum++;
mProcessedSubTaskNum++;
} else if (renameResult == FileUtil.OP_CANCELED) {
return;
} else if (renameResult == FileUtil.OP_SKIP) {
// TODO: make use of skipped info
mProcessedSubTaskNum++;
continue;
} else if (renameResult == FileUtil.ERROR_OP_UNAVAILABLE) {
mProcessedSubTaskNum++;
return;
} else {
mProcessedSubTaskNum++;
}
}
} else if (mOptions.operationType
.equals(OP_TYPE_DELETE_FILE)) {
int deleteResult = FileUtil.ERROR_OP_UNAVAILABLE;
try {
deleteResult = FileUtil.deleteFile(srcPath,
mOptions.skipAllDeleteError ? true : false,
FileOperationTask.this);
} catch (Exception e) {
e.printStackTrace();
}
if (deleteResult == FileUtil.OP_SUCCESS) {
if (KLOG) {
Log.i(TAG, "Delete file: " + srcPath
+ " succeed.");
}
mSucceedSubTaskNum++;
mProcessedSubTaskNum++;
} else {
if (KLOG) {
Log.i(TAG, "Delete file: " + srcPath
+ " FAILED.");
}
mProcessedSubTaskNum++;
}
} else if (mOptions.operationType
.equals(OP_TYPE_SEARCH_FILE)
&& mOptions.searchParams != null) {
Object objDir = mOptions.searchParams
.get(FileOperationOptions.KEY_SEARCH_DIR);
Object objTypeList = mOptions.searchParams
.get(FileOperationOptions.KEY_SEARCH_SCOPE);
Object objResult = mOptions.searchParams
.get(FileOperationOptions.KEY_SEARCH_OUTPUT);
Object objReserved = mOptions.searchParams
.get(FileOperationOptions.KEY_SEARCH_RESEVERD_DIR);
Object objDepth = mOptions.searchParams
.get(FileOperationOptions.KEY_SEARCH_DEPTH);
Object objLimit = mOptions.searchParams
.get(FileOperationOptions.KEY_SEARCH_RECORD_LIMIT);
String searchDir = objDir != null ? (String) objDir
: null;
@SuppressWarnings("unchecked")
ArrayList<String> typeList = objTypeList != null ? (ArrayList<String>) objTypeList
: null;
@SuppressWarnings("unchecked")
ArrayList<String> searchResult = objResult != null ? (ArrayList<String>) objResult
: null;
@SuppressWarnings("unchecked")
ArrayList<String> reservedSearchDirs = objReserved != null ? (ArrayList<String>) objReserved
: null;
int searchDepth = objDepth != null ? ((Integer) objDepth)
.intValue() : -1;
int searchRecordLimit = objLimit != null ? ((Integer) objLimit)
.intValue() : -1;
if (searchDir != null && searchResult != null) {
try {
FileUtil.searchFilesByStr(srcPath, searchDir,
typeList, searchResult,
FileOperationTask.this,
reservedSearchDirs, searchDepth,
searchRecordLimit);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
if (mSucceedPercentage == 100) {
FileOperationTask.this.notify(mTaskState = TASK_FINISHED);
} else {
FileOperationTask.this.notify(mTaskState = TASK_STOPPED);
}
}
};
mWorkThread = new Thread(mWorkProcedure);
}
@Override
protected void finalize() throws Throwable {
super.finalize();
synchronized (mListeners) {
mListeners.clear();
}
}
}