From: David A. Velasco Date: Tue, 28 Jul 2015 14:27:07 +0000 (+0200) Subject: ETag used to detect changes in remote files (not only in folders) X-Git-Tag: oc-android-1.9^2~37^2~18 X-Git-Url: http://git.linex4red.de/pub/Android/ownCloud.git/commitdiff_plain/5f41bb14d370e0c9c0a8003e4ed122f466b0ce71?ds=inline;hp=-c ETag used to detect changes in remote files (not only in folders) --- 5f41bb14d370e0c9c0a8003e4ed122f466b0ce71 diff --git a/src/com/owncloud/android/datamodel/OCFile.java b/src/com/owncloud/android/datamodel/OCFile.java index 4baf1ea5..39666dfe 100644 --- a/src/com/owncloud/android/datamodel/OCFile.java +++ b/src/com/owncloud/android/datamodel/OCFile.java @@ -498,10 +498,9 @@ public class OCFile implements Parcelable, Comparable { } public void setEtag(String etag) { - this.mEtag = etag; + this.mEtag = (etag != null ? etag : ""); } - public boolean isShareByLink() { return mShareByLink; } @@ -602,4 +601,5 @@ public class OCFile implements Parcelable, Comparable { // TODO real implementation return false; } + } diff --git a/src/com/owncloud/android/files/FileMenuFilter.java b/src/com/owncloud/android/files/FileMenuFilter.java index b363f558..11cd37f1 100644 --- a/src/com/owncloud/android/files/FileMenuFilter.java +++ b/src/com/owncloud/android/files/FileMenuFilter.java @@ -34,7 +34,6 @@ import com.owncloud.android.files.services.FileDownloader; import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder; import com.owncloud.android.files.services.FileUploader; import com.owncloud.android.files.services.FileUploader.FileUploaderBinder; -import com.owncloud.android.services.OperationsService; import com.owncloud.android.services.OperationsService.OperationsServiceBinder; import com.owncloud.android.ui.activity.ComponentsGetter; diff --git a/src/com/owncloud/android/files/FileOperationsHelper.java b/src/com/owncloud/android/files/FileOperationsHelper.java index c0b00e55..848f8cf4 100644 --- a/src/com/owncloud/android/files/FileOperationsHelper.java +++ b/src/com/owncloud/android/files/FileOperationsHelper.java @@ -230,30 +230,6 @@ public class FileOperationsHelper { } } - - /** - * Request the synchronization of a file or the DOWNLOAD OF A FOLDER, including its contents. - * - * For files, it's the same as syncFile(OCFile file); for folders, this method does not trigger uploads for - * file locally modified. - * - * Kept 'til synchronization of full folders is considered good enough. - * - * @param file The file or folder to synchronize - */ - public void downloadFile(OCFile file) { - if (!file.isFolder()){ - syncFile(file); - - } else { - Intent intent = new Intent(mFileActivity, OperationsService.class); - intent.setAction(OperationsService.ACTION_DOWNLOAD_FOLDER); - intent.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount()); - intent.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath()); - mFileActivity.startService(intent); - } - } - public void toggleFavorite(OCFile file, boolean isFavorite) { file.setFavorite(isFavorite); mFileActivity.getStorageManager().saveFile(file); @@ -327,15 +303,6 @@ public class FileOperationsHelper { FileDownloaderBinder downloaderBinder = mFileActivity.getFileDownloaderBinder(); if (downloaderBinder != null && downloaderBinder.isDownloading(account, file)) { downloaderBinder.cancel(account, file); - - // TODO - review why is this here, and solve in a better way - // Remove etag for parent, if file is a favorite - if (file.isFavorite()) { - OCFile parent = mFileActivity.getStorageManager().getFileById(file.getParentId()); - parent.setEtag(""); - mFileActivity.getStorageManager().saveFile(parent); - } - } FileUploaderBinder uploaderBinder = mFileActivity.getFileUploaderBinder(); if (uploaderBinder != null && uploaderBinder.isUploading(account, file)) { diff --git a/src/com/owncloud/android/files/services/FileDownloader.java b/src/com/owncloud/android/files/services/FileDownloader.java index 71f4bdbb..797053d6 100644 --- a/src/com/owncloud/android/files/services/FileDownloader.java +++ b/src/com/owncloud/android/files/services/FileDownloader.java @@ -475,7 +475,7 @@ public class FileDownloader extends Service file.setNeedsUpdateThumbnail(true); file.setModificationTimestamp(mCurrentDownload.getModificationTimestamp()); file.setModificationTimestampAtLastSyncForData(mCurrentDownload.getModificationTimestamp()); - // file.setEtag(mCurrentDownload.getEtag()); // TODO Etag, where available + file.setEtag(mCurrentDownload.getEtag()); file.setMimetype(mCurrentDownload.getMimeType()); file.setStoragePath(mCurrentDownload.getSavePath()); file.setFileLength((new File(mCurrentDownload.getSavePath()).length())); diff --git a/src/com/owncloud/android/files/services/FileUploader.java b/src/com/owncloud/android/files/services/FileUploader.java index 445274c4..2695b773 100644 --- a/src/com/owncloud/android/files/services/FileUploader.java +++ b/src/com/owncloud/android/files/services/FileUploader.java @@ -688,6 +688,8 @@ public class FileUploader extends Service if (result.isSuccess()) { updateOCFile(file, (RemoteFile) result.getData().get(0)); file.setLastSyncDateForProperties(syncDate); + } else { + Log_OC.e(TAG, "Error reading properties of file after successful upload; this is gonna hurt..."); } // / maybe this would be better as part of UploadFileOperation... or @@ -712,7 +714,7 @@ public class FileUploader extends Service file.setMimetype(remoteFile.getMimeType()); file.setModificationTimestamp(remoteFile.getModifiedTimestamp()); file.setModificationTimestampAtLastSyncForData(remoteFile.getModifiedTimestamp()); - // file.setEtag(remoteFile.getEtag()); // TODO Etag, where available + file.setEtag(remoteFile.getEtag()); file.setRemoteId(remoteFile.getRemoteId()); } diff --git a/src/com/owncloud/android/operations/DownloadFileOperation.java b/src/com/owncloud/android/operations/DownloadFileOperation.java index 0cb303ce..54c315c5 100644 --- a/src/com/owncloud/android/operations/DownloadFileOperation.java +++ b/src/com/owncloud/android/operations/DownloadFileOperation.java @@ -52,6 +52,7 @@ public class DownloadFileOperation extends RemoteOperation { private OCFile mFile; private Set mDataTransferListeners = new HashSet(); private long mModificationTimestamp = 0; + private String mEtag = ""; private final AtomicBoolean mCancellationRequested = new AtomicBoolean(false); private DownloadRemoteFileOperation mDownloadOperation; @@ -127,6 +128,10 @@ public class DownloadFileOperation extends RemoteOperation { mFile.getModificationTimestamp(); } + public String getEtag() { + return mEtag; + } + @Override protected RemoteOperationResult run(OwnCloudClient client) { RemoteOperationResult result = null; @@ -154,6 +159,7 @@ public class DownloadFileOperation extends RemoteOperation { if (result.isSuccess()) { mModificationTimestamp = mDownloadOperation.getModificationTimestamp(); + mEtag = mDownloadOperation.getEtag(); newFile = new File(getSavePath()); newFile.getParentFile().mkdirs(); moved = tmpFile.renameTo(newFile); diff --git a/src/com/owncloud/android/operations/DownloadFolderOperation.java b/src/com/owncloud/android/operations/DownloadFolderOperation.java index 02f0445b..4f379b4c 100644 --- a/src/com/owncloud/android/operations/DownloadFolderOperation.java +++ b/src/com/owncloud/android/operations/DownloadFolderOperation.java @@ -540,7 +540,7 @@ public class DownloadFolderOperation extends SyncOperation { private void startSyncFolderOperation(String path){ Intent intent = new Intent(mContext, OperationsService.class); - intent.setAction(OperationsService.ACTION_DOWNLOAD_FOLDER); + intent.setAction(OperationsService.ACTION_SYNC_FOLDER); intent.putExtra(OperationsService.EXTRA_ACCOUNT, mAccount); intent.putExtra(OperationsService.EXTRA_REMOTE_PATH, path); mContext.startService(intent); diff --git a/src/com/owncloud/android/operations/RefreshFolderOperation.java b/src/com/owncloud/android/operations/RefreshFolderOperation.java index 977f72a8..f0ba3d51 100644 --- a/src/com/owncloud/android/operations/RefreshFolderOperation.java +++ b/src/com/owncloud/android/operations/RefreshFolderOperation.java @@ -20,26 +20,17 @@ package com.owncloud.android.operations; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Vector; -import org.apache.http.HttpStatus; import android.accounts.Account; import android.content.Context; import android.content.Intent; import android.util.Log; -//import android.support.v4.content.LocalBroadcastManager; -import com.owncloud.android.MainApp; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; @@ -50,7 +41,6 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.resources.shares.GetRemoteSharesForFileOperation; -import com.owncloud.android.lib.resources.files.FileUtils; import com.owncloud.android.lib.resources.files.ReadRemoteFileOperation; import com.owncloud.android.lib.resources.files.ReadRemoteFolderOperation; import com.owncloud.android.lib.resources.files.RemoteFile; @@ -120,7 +110,10 @@ public class RefreshFolderOperation extends RemoteOperation { /** 'True' means that Etag will be ignored */ private boolean mIgnoreETag; - + private List mFilesToSyncContents; + // this will be used for every file when 'folder synchronization' replaces 'folder download' + + /** * Creates a new instance of {@link RefreshFolderOperation}. * @@ -154,6 +147,7 @@ public class RefreshFolderOperation extends RemoteOperation { mForgottenLocalFiles = new HashMap(); mRemoteFolderChanged = false; mIgnoreETag = ignoreETag; + mFilesToSyncContents = new Vector(); } @@ -191,7 +185,7 @@ public class RefreshFolderOperation extends RemoteOperation { mConflictsFound = 0; mForgottenLocalFiles.clear(); - if (FileUtils.PATH_SEPARATOR.equals(mLocalFolder.getRemotePath()) && !mSyncFullAccount) { + if (OCFile.ROOT_PATH.equals(mLocalFolder.getRemotePath()) && !mSyncFullAccount) { updateOCVersion(client); } @@ -201,9 +195,14 @@ public class RefreshFolderOperation extends RemoteOperation { if (mRemoteFolderChanged) { result = fetchAndSyncRemoteFolder(client); } else { - // TODO Enable when "On Device" is recovered ? + fetchFavoritesToSyncFromLocalData(); mChildren = mStorageManager.getFolderContent(mLocalFolder/*, false*/); } + + if (result.isSuccess()) { + // request for the synchronization of KEPT-IN-SYNC file contents + startContentSynchronizations(mFilesToSyncContents, client); + } } if (!mSyncFullAccount) { @@ -239,9 +238,8 @@ public class RefreshFolderOperation extends RemoteOperation { private RemoteOperationResult checkForChanges(OwnCloudClient client) { mRemoteFolderChanged = true; RemoteOperationResult result = null; - String remotePath = null; + String remotePath = mLocalFolder.getRemotePath(); - remotePath = mLocalFolder.getRemotePath(); Log_OC.d(TAG, "Checking changes in " + mAccount.name + remotePath); // remote request @@ -264,7 +262,7 @@ public class RefreshFolderOperation extends RemoteOperation { result = new RemoteOperationResult(ResultCode.OK); - Log_OC.i(TAG, "Checked " + mAccount.name + remotePath + " : " + + Log_OC.i(TAG, "Checked " + mAccount.name + remotePath + " : " + (mRemoteFolderChanged ? "changed" : "not changed")); } else { @@ -337,15 +335,15 @@ public class RefreshFolderOperation extends RemoteOperation { mLocalFolder = mStorageManager.getFileByPath(mLocalFolder.getRemotePath()); // parse data from remote folder - OCFile remoteFolder = fillOCFile((RemoteFile)folderAndFiles.get(0)); + OCFile remoteFolder = FileStorageUtils.fillOCFile((RemoteFile) folderAndFiles.get(0)); remoteFolder.setParentId(mLocalFolder.getParentId()); remoteFolder.setFileId(mLocalFolder.getFileId()); - Log_OC.d(TAG, "Remote folder " + mLocalFolder.getRemotePath() + Log_OC.d(TAG, "Remote folder " + mLocalFolder.getRemotePath() + " changed - starting update of local data "); List updatedFiles = new Vector(folderAndFiles.size() - 1); - List filesToSyncContents = new Vector(); + mFilesToSyncContents.clear(); // get current data about local contents of the folder to synchronize // TODO Enable when "On Device" is recovered ? @@ -359,7 +357,7 @@ public class RefreshFolderOperation extends RemoteOperation { OCFile remoteFile = null, localFile = null; for (int i=1; i 0){ - out.write(buf, 0, len); - } - file.setStoragePath(expectedPath); - - } catch (Exception e) { - Log_OC.e(TAG, "Exception while copying foreign file " + expectedPath, e); - mForgottenLocalFiles.put(file.getRemotePath(), storagePath); - file.setStoragePath(null); - - } finally { - try { - if (in != null) in.close(); - } catch (Exception e) { - Log_OC.d(TAG, "Weird exception while closing input stream for " - + storagePath + " (ignoring)", e); - } - try { - if (out != null) out.close(); - } catch (Exception e) { - Log_OC.d(TAG, "Weird exception while closing output stream for " - + expectedPath + " (ignoring)", e); - } - } - } - } - } - - private RemoteOperationResult refreshSharesForFolder(OwnCloudClient client) { RemoteOperationResult result = null; @@ -573,24 +473,6 @@ public class RefreshFolderOperation extends RemoteOperation { /** - * Scans the default location for saving local copies of files searching for - * a 'lost' file with the same full name as the {@link OCFile} received as - * parameter. - * - * @param file File to associate a possible 'lost' local file. - */ - private void searchForLocalFileInDefaultPath(OCFile file) { - if (file.getStoragePath() == null && !file.isFolder()) { - File f = new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file)); - if (f.exists()) { - file.setStoragePath(f.getAbsolutePath()); - file.setLastSyncDateForData(f.lastModified()); - } - } - } - - - /** * Sends a message to any application component interested in the progress * of the synchronization. * @@ -614,8 +496,20 @@ public class RefreshFolderOperation extends RemoteOperation { } - public boolean getRemoteFolderChanged() { - return mRemoteFolderChanged; + private void fetchFavoritesToSyncFromLocalData() { + List children = mStorageManager.getFolderContent(mLocalFolder); + for (OCFile child : children) { + if (!child.isFolder() && child.isFavorite()) { + SynchronizeFileOperation operation = new SynchronizeFileOperation( + child, + child, // cheating with the remote file to get an update to server; to refactor + mAccount, + true, + mContext + ); + mFilesToSyncContents.add(operation); + } + } } } diff --git a/src/com/owncloud/android/operations/SynchronizeFileOperation.java b/src/com/owncloud/android/operations/SynchronizeFileOperation.java index d7c49f04..ebc9a3fe 100644 --- a/src/com/owncloud/android/operations/SynchronizeFileOperation.java +++ b/src/com/owncloud/android/operations/SynchronizeFileOperation.java @@ -22,7 +22,6 @@ package com.owncloud.android.operations; -import com.owncloud.android.MainApp; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.files.services.FileDownloader; import com.owncloud.android.files.services.FileUploader; @@ -208,15 +207,13 @@ public class SynchronizeFileOperation extends SyncOperation { /// check changes in server and local file boolean serverChanged = false; - /* time for eTag is coming, but not yet - if (mServerFile.getEtag() != null) { - serverChanged = (!mServerFile.getEtag().equals(mLocalFile.getEtag())); - } else { */ - serverChanged = ( - mServerFile.getModificationTimestamp() != - mLocalFile.getModificationTimestampAtLastSyncForData() - ); - //} + if (mLocalFile.getEtag() == null || mLocalFile.getEtag().length() == 0) { + // file uploaded (null) or downloaded ("") before upgrade to version 1.8.0; check the old condition + serverChanged = mServerFile.getModificationTimestamp() != + mLocalFile.getModificationTimestampAtLastSyncForData(); + } else { + serverChanged = (!mServerFile.getEtag().equals(mLocalFile.getEtag())); + } boolean localChanged = ( mLocalFile.getLocalModificationTimestamp() > mLocalFile.getLastSyncDateForData() ); diff --git a/src/com/owncloud/android/operations/SynchronizeFolderOperation.java b/src/com/owncloud/android/operations/SynchronizeFolderOperation.java index 36955a51..82ce3132 100644 --- a/src/com/owncloud/android/operations/SynchronizeFolderOperation.java +++ b/src/com/owncloud/android/operations/SynchronizeFolderOperation.java @@ -278,7 +278,7 @@ public class SynchronizeFolderOperation extends SyncOperation { FileDataStorageManager storageManager = getStorageManager(); // parse data from remote folder - OCFile remoteFolder = fillOCFile((RemoteFile)folderAndFiles.get(0)); + OCFile remoteFolder = FileStorageUtils.fillOCFile((RemoteFile) folderAndFiles.get(0)); remoteFolder.setParentId(mLocalFolder.getParentId()); remoteFolder.setFileId(mLocalFolder.getFileId()); @@ -305,7 +305,7 @@ public class SynchronizeFolderOperation extends SyncOperation { OCFile remoteFile = null, localFile = null; for (int i=1; i MAX_FAILED_RESULTS || isFinisher(mLastFailedResult)) return; - /* - OCFile folder, - long currentSyncTime, - boolean updateFolderProperties, - boolean syncFullAccount, - DataStorageManager dataStorageManager, - Account account, - Context context ) { - } - */ // folder synchronization RefreshFolderOperation synchFolderOp = new RefreshFolderOperation( folder, mCurrentSyncTime, @@ -308,7 +295,7 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter { // synchronize children folders List children = synchFolderOp.getChildren(); // beware of the 'hidden' recursion here! - fetchChildren(folder, children, synchFolderOp.getRemoteFolderChanged()); + syncChildren(children); } } else { @@ -351,25 +338,19 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter { /** * Triggers the synchronization of any folder contained in the list of received files. + * + * No consideration of etag here because it MUST walk down anyway, in case that kept-in-sync files + * have local changes. * * @param files Files to recursively synchronize. */ - private void fetchChildren(OCFile parent, List files, boolean parentEtagChanged) { + private void syncChildren(List files) { int i; - OCFile newFile = null; - //String etag = null; - //boolean syncDown = false; + OCFile newFile; for (i=0; i < files.size() && !mCancellation; i++) { newFile = files.get(i); if (newFile.isFolder()) { - /* - etag = newFile.getEtag(); - syncDown = (parentEtagChanged || etag == null || etag.length() == 0); - if(syncDown) { */ - synchronizeFolder(newFile); - //sendLocalBroadcast(EVENT_FULL_SYNC_FOLDER_SIZE_SYNCED, parent.getRemotePath(), - // null); - //} + synchronizeFolder(newFile); } } diff --git a/src/com/owncloud/android/ui/dialog/RemoveFileDialogFragment.java b/src/com/owncloud/android/ui/dialog/RemoveFileDialogFragment.java index 28419e62..f8310a1d 100644 --- a/src/com/owncloud/android/ui/dialog/RemoveFileDialogFragment.java +++ b/src/com/owncloud/android/ui/dialog/RemoveFileDialogFragment.java @@ -25,7 +25,6 @@ package com.owncloud.android.ui.dialog; * * Triggers the removal according to the user response. */ -import java.util.Vector; import android.app.Dialog; import android.os.Bundle; @@ -106,34 +105,6 @@ implements ConfirmationDialogFragmentListener { public void onCancel(String callerTag) { ComponentsGetter cg = (ComponentsGetter)getActivity(); cg.getFileOperationsHelper().removeFile(mTargetFile, true); - - FileDataStorageManager storageManager = cg.getStorageManager(); - - boolean containsFavorite = false; - if (mTargetFile.isFolder()) { - // TODO Enable when "On Device" is recovered ? - Vector files = storageManager.getFolderContent(mTargetFile/*, false*/); - for(OCFile file: files) { - containsFavorite = file.isFavorite() || containsFavorite; - - if (containsFavorite) - break; - } - } - - // Remove etag for parent, if file is a favorite - // or is a folder and contains favorite - if (mTargetFile.isFavorite() || containsFavorite) { - OCFile folder = null; - if (mTargetFile.isFolder()) { - folder = mTargetFile; - } else { - folder = storageManager.getFileById(mTargetFile.getParentId()); - } - - folder.setEtag(""); - storageManager.saveFile(folder); - } } @Override diff --git a/src/com/owncloud/android/ui/fragment/OCFileListFragment.java b/src/com/owncloud/android/ui/fragment/OCFileListFragment.java index 6f7b2d8a..23590b5e 100644 --- a/src/com/owncloud/android/ui/fragment/OCFileListFragment.java +++ b/src/com/owncloud/android/ui/fragment/OCFileListFragment.java @@ -363,10 +363,7 @@ public class OCFileListFragment extends ExtendedListFragment implements FileActi dialog.show(getFragmentManager(), ConfirmationDialogFragment.FTAG_CONFIRMATION); return true; } - case R.id.action_download_file: { - mContainerActivity.getFileOperationsHelper().downloadFile(mTargetFile); - return true; - } + case R.id.action_download_file: case R.id.action_sync_file: { mContainerActivity.getFileOperationsHelper().syncFile(mTargetFile); return true; diff --git a/src/com/owncloud/android/utils/FileStorageUtils.java b/src/com/owncloud/android/utils/FileStorageUtils.java index e70302fc..0851b035 100644 --- a/src/com/owncloud/android/utils/FileStorageUtils.java +++ b/src/com/owncloud/android/utils/FileStorageUtils.java @@ -32,6 +32,7 @@ import com.owncloud.android.R; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.lib.resources.files.RemoteFile; +import android.accounts.Account; import android.annotation.SuppressLint; import android.content.Context; import android.content.SharedPreferences; @@ -120,7 +121,7 @@ public class FileStorageUtils { * Creates and populates a new {@link OCFile} object with the data read from the server. * * @param remote remote file read from the server (remote file or folder). - * @return New OCFile instance representing the remote resource described by we. + * @return New OCFile instance representing the remote resource described by remote. */ public static OCFile fillOCFile(RemoteFile remote) { OCFile file = new OCFile(remote.getRemotePath()); @@ -302,5 +303,33 @@ public class FileStorageUtils { String result = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension.toLowerCase()); return (result != null) ? result : ""; } - + + /** + * Scans the default location for saving local copies of files searching for + * a 'lost' file with the same full name as the {@link OCFile} received as + * parameter. + * + * This method helps to keep linked local copies of the files when the app is uninstalled, and then + * reinstalled in the device. OR after the cache of the app was deleted in system settings. + * + * The method is assuming that all the local changes in the file where synchronized in the past. This is dangerous, + * but assuming the contrary could lead to massive unnecessary synchronizations of downloaded file after deleting + * the app cache. + * + * This should be changed in the near future to avoid any chance of data loss, but we need to add some options + * to limit hard automatic synchronizations to wifi, unless the user wants otherwise. + * + * @param file File to associate a possible 'lost' local file. + * @param account Account holding file. + */ + public static void searchForLocalFileInDefaultPath(OCFile file, Account account) { + if (file.getStoragePath() == null && !file.isFolder()) { + File f = new File(FileStorageUtils.getDefaultSavePathFor(account.name, file)); + if (f.exists()) { + file.setStoragePath(f.getAbsolutePath()); + file.setLastSyncDateForData(f.lastModified()); + } + } + } + }