From: David A. Velasco Date: Tue, 26 Aug 2014 14:11:52 +0000 (+0200) Subject: Refactored MoveFileOperation to handle local update after remote move (WIP) X-Git-Tag: oc-android-1.7.0_signed~197^2~17 X-Git-Url: http://git.linex4red.de/pub/Android/ownCloud.git/commitdiff_plain/5480c0b52f6e81ab9d046a20743c563d75ef2dd1?ds=inline Refactored MoveFileOperation to handle local update after remote move (WIP) --- diff --git a/src/com/owncloud/android/datamodel/FileDataStorageManager.java b/src/com/owncloud/android/datamodel/FileDataStorageManager.java index c8c0d24d..5dfdb987 100644 --- a/src/com/owncloud/android/datamodel/FileDataStorageManager.java +++ b/src/com/owncloud/android/datamodel/FileDataStorageManager.java @@ -41,6 +41,7 @@ import android.content.ContentProviderResult; import android.content.ContentResolver; import android.content.ContentUris; import android.content.ContentValues; +import android.content.Intent; import android.content.OperationApplicationException; import android.database.Cursor; import android.net.Uri; @@ -612,6 +613,142 @@ public class FileDataStorageManager { } + //public void moveFolder(OCFile folder, String newPath) { + public void moveLocalFile(OCFile file, String targetPath) { + // TODO check newPath + if (file != null && file.fileExists()) { + + if (//file.isFolder() && // should work for regular files!! + !OCFile.ROOT_PATH.equals(file.getFileName())) { + + /// 0. move in local file system + + /// 1. get all the descendants in a single QUERY (including the folder) + Cursor c = null; + if (getContentProviderClient() != null) { + try { + c = getContentProviderClient().query( + ProviderTableMeta.CONTENT_URI, + null, + ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " + + ProviderTableMeta.FILE_PATH + " LIKE ? ", + new String[] { + mAccount.name, + file.getRemotePath() + "%" + }, + ProviderTableMeta.FILE_PATH + " ASC " + ); + } catch (RemoteException e) { + Log_OC.e(TAG, e.getMessage()); + } + + } else { + c = getContentResolver().query( + ProviderTableMeta.CONTENT_URI, + null, + ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " + + ProviderTableMeta.FILE_PATH + " LIKE ? ", + new String[] { + mAccount.name, + file.getRemotePath() + "%" + }, + ProviderTableMeta.FILE_PATH + " ASC " + ); + } + + /// 2. prepare a batch of update operations to change all the descendants + ArrayList operations = + new ArrayList(c.getCount()); + if (c.moveToFirst()) { + int lengthOfOldPath = file.getRemotePath().length(); + String defaultSavePath = FileStorageUtils.getSavePath(mAccount.name); + int lengthOfOldStoragePath = defaultSavePath.length() + lengthOfOldPath; + do { + ContentValues cv = new ContentValues(); // keep construction in the loop + OCFile child = createFileInstance(c); + cv.put( + ProviderTableMeta.FILE_PATH, + targetPath + child.getRemotePath().substring(lengthOfOldPath) + ); + if (child.getStoragePath() != null && + child.getStoragePath().startsWith(defaultSavePath)) { + // update link to downloaded content - but local move is not done here! + cv.put( + ProviderTableMeta.FILE_STORAGE_PATH, + defaultSavePath + targetPath + + child.getStoragePath().substring(lengthOfOldStoragePath) + ); + } + operations.add( + ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI). + withValues(cv). + withSelection( + ProviderTableMeta._ID + "=?", + new String[] { String.valueOf(child.getFileId()) } + ) + .build()); + + } while (c.moveToNext()); + } + c.close(); + + /// 3. apply updates in batch + try { + if (getContentResolver() != null) { + getContentResolver().applyBatch(MainApp.getAuthority(), operations); + + } else { + getContentProviderClient().applyBatch(operations); + } + + } catch (Exception e) { + Log_OC.e( + TAG, + "Fail to update " + file.getFileId() + " and descendants in database", + e + ); + } + + } + } + + /* + private void saveLocalDirectory() { + * TODO implement local movement of folder + getStorageManager().moveFolder(mFile, mNewPath); + String localPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, mFile); + File localDir = new File(localPath); + if (localDir.exists()) { + localDir.renameTo(new File(FileStorageUtils.getSavePath(mAccount.name) + mNewPath)); + // TODO - if renameTo fails, children files that are already down will result unlinked + } + } + */ + + /* + private void saveLocalFile() { + mFile.setRFileName(mNewName); <<< NO > + + // try to move the local copy of the file + if (mFile.isDown()) { + File f = new File(mFile.getStoragePath()); + String parentStoragePath = f.getParent(); + if (!parentStoragePath.endsWith(File.separator)) + parentStoragePath += File.separator; + if (f.renameTo(new File())) { + mFile.setStoragePath(parentStoragePath + mNewName); + } + // else - NOTHING: the link to the local file is kept although the local name can't be updated + // TODO - study conditions when this could be a problem + } + + getStorageManager().saveFile(mFile); + } + */ + + } + + private Vector getFolderContent(long parentId) { Vector ret = new Vector(); @@ -1302,4 +1439,5 @@ public class FileDataStorageManager { */ //} } + } diff --git a/src/com/owncloud/android/operations/MoveFileOperation.java b/src/com/owncloud/android/operations/MoveFileOperation.java index 620a058c..a4838ecd 100644 --- a/src/com/owncloud/android/operations/MoveFileOperation.java +++ b/src/com/owncloud/android/operations/MoveFileOperation.java @@ -17,18 +17,12 @@ package com.owncloud.android.operations; -import java.io.File; -import java.io.IOException; - import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; import com.owncloud.android.lib.resources.files.MoveRemoteFileOperation; -import com.owncloud.android.lib.resources.files.RenameRemoteFileOperation; import com.owncloud.android.operations.common.SyncOperation; -import com.owncloud.android.utils.FileStorageUtils; -import com.owncloud.android.utils.Log_OC; import android.accounts.Account; @@ -40,13 +34,12 @@ import android.accounts.Account; */ public class MoveFileOperation extends SyncOperation { - private static final String TAG = MoveFileOperation.class.getSimpleName(); + //private static final String TAG = MoveFileOperation.class.getSimpleName(); + + private String mSrcPath; + private String mTargetParentPath; - private String mPath; - private Account mAccount; - private String mNewParentPath; private OCFile mFile; - private String mNewPath; @@ -57,20 +50,16 @@ public class MoveFileOperation extends SyncOperation { * @param newParentPath Path to the folder where the file will be moved into. * @param account OwnCloud account containing both the file and the target folder */ - public MoveFileOperation(String path, String newParentPath, Account account) { - mPath = path; - mNewParentPath = newParentPath; - mAccount = account; + public MoveFileOperation(String srcPath, String targetParentPath, Account account) { + mSrcPath = srcPath; + mTargetParentPath = targetParentPath; + if (!mTargetParentPath.endsWith(OCFile.PATH_SEPARATOR)) { + mTargetParentPath += OCFile.PATH_SEPARATOR; + } mFile = null; - mNewPath = ""; } - public OCFile getFile() { - return mFile; - } - - /** * Performs the operation. * @@ -79,140 +68,34 @@ public class MoveFileOperation extends SyncOperation { @Override protected RemoteOperationResult run(OwnCloudClient client) { RemoteOperationResult result = null; - - mFile = getStorageManager().getFileByPath(mPath); - - try { - /// 1. check move validity - - - /// 2. remove move - MoveRemoteFileOperation operation = new MoveRemoteFileOperation( - mFile.getRemotePath(), - mNewPath, - false - ); - result = operation.execute(client); - - - /// 3. local move - if (result.isSuccess()) { - //moveLocaly(); - /* - if (mFile.isFolder()) { - saveLocalDirectory(); - - } else { - saveLocalFile(); - } - */ - } - - - String parentPath = (new File(mFile.getRemotePath())).getParent(); - parentPath = (parentPath.endsWith(OCFile.PATH_SEPARATOR)) - ? parentPath - : parentPath + OCFile.PATH_SEPARATOR; - - mNewPath = mNewParentPath + mFile.getFileName(); - if (mFile.isFolder() && !mNewPath.endsWith(OCFile.PATH_SEPARATOR)) { - mNewPath += OCFile.PATH_SEPARATOR; - } - - // check local overwrite - if (getStorageManager().getFileByPath(mPath) != null) { - return new RemoteOperationResult(ResultCode.INVALID_OVERWRITE); - } - - /* - } catch (IOException e) { - Log_OC.e(TAG, "Move " + mFile.getRemotePath() + " to " + - (mNewParentPath==null) + ": " + - ((result!= null) ? result.getLogMessage() : ""), e);*/ - } finally { - + /// 1. check move validity + if (mTargetParentPath.startsWith(mSrcPath)) { + return new RemoteOperationResult(ResultCode.INVALID_MOVE_INTO_DESCENDANT); } - - return result; - } - - - private void saveLocalDirectory() { - getStorageManager().moveFolder(mFile, mNewPath); - String localPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, mFile); - File localDir = new File(localPath); - if (localDir.exists()) { - localDir.renameTo(new File(FileStorageUtils.getSavePath(mAccount.name) + mNewPath)); - // TODO - if renameTo fails, children files that are already down will result unlinked + mFile = getStorageManager().getFileByPath(mSrcPath); + if (mFile == null) { + return new RemoteOperationResult(ResultCode.FILE_NOT_FOUND); } - } - - private void saveLocalFile() { - /* - mFile.setRFileName(mNewName); <<< NO > - - // try to move the local copy of the file - if (mFile.isDown()) { - File f = new File(mFile.getStoragePath()); - String parentStoragePath = f.getParent(); - if (!parentStoragePath.endsWith(File.separator)) - parentStoragePath += File.separator; - if (f.renameTo(new File())) { - mFile.setStoragePath(parentStoragePath + mNewName); - } - // else - NOTHING: the link to the local file is kept although the local name can't be updated - // TODO - study conditions when this could be a problem - } - - getStorageManager().saveFile(mFile); - */ - } - - /** - * Checks if the new name to set is valid in the file system - * - * The only way to be sure is trying to create a file with that name. It's made in the temporal directory - * for downloads, out of any account, and then removed. - * - * IMPORTANT: The test must be made in the same file system where files are download. The internal storage - * could be formatted with a different file system. - * - * TODO move this method, and maybe FileDownload.get***Path(), to a class with utilities specific for the interactions with the file system - * - * @return 'True' if a temporal file named with the name to set could be created in the file system where - * local files are stored. - * @throws IOException When the temporal folder can not be created. - */ - private boolean isValidNewName() throws IOException { - return true; - /* - // check tricky names - if (mNewName == null || mNewName.length() <= 0 || mNewName.contains(File.separator) || mNewName.contains("%")) { - return false; - } - // create a test file - String tmpFolderName = FileStorageUtils.getTemporalPath(""); - File testFile = new File(tmpFolderName + mNewName); - File tmpFolder = testFile.getParentFile(); - tmpFolder.mkdirs(); - if (!tmpFolder.isDirectory()) { - throw new IOException("Unexpected error: temporal directory could not be created"); - } - try { - testFile.createNewFile(); // return value is ignored; it could be 'false' because the file already existed, that doesn't invalidate the name - } catch (IOException e) { - Log_OC.i(TAG, "Test for validity of name " + mNewName + " in the file system failed"); - return false; - } - boolean result = (testFile.exists() && testFile.isFile()); + /// 2. remote move + String targetPath = mTargetParentPath + mFile.getFileName(); + MoveRemoteFileOperation operation = new MoveRemoteFileOperation( + mSrcPath, + targetPath, + false + ); + result = operation.execute(client); - // cleaning ; result is ignored, since there is not much we could do in case of failure, but repeat and repeat... - testFile.delete(); + /// 3. local move + if (result.isSuccess()) { + // TODO work in progress + //getStorageManager().moveLocalFile(mFile, targetPath); + } + // TODO handle ResultCode.PARTIAL_MOVE_DONE in client Activity, for the moment return result; - */ } + }