mirror of
https://github.com/nextcloud/android.git
synced 2024-11-29 10:49:04 +03:00
Merge pull request #1109 from owncloud/sync_full_folder
Replaced 'download folder' with 'synchronize', triggering the synchronization in two-ways of all the files in a folder
This commit is contained in:
commit
eaa0612bfa
27 changed files with 636 additions and 891 deletions
|
@ -1 +1 @@
|
||||||
Subproject commit ecc3415e3e3c13fa8f73fdd51a88c1ab7087b199
|
Subproject commit f02dffb1d3c46305c70d246f696cde7b8c3b0971
|
BIN
res/drawable/conflict_file_indicator.png
Normal file
BIN
res/drawable/conflict_file_indicator.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.6 KiB |
BIN
res/drawable/synchronizing_file_indicator.png
Normal file
BIN
res/drawable/synchronizing_file_indicator.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.1 KiB |
|
@ -44,13 +44,8 @@
|
||||||
android:icon="@drawable/ic_action_refresh"
|
android:icon="@drawable/ic_action_refresh"
|
||||||
android:orderInCategory="1" />
|
android:orderInCategory="1" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_cancel_download"
|
android:id="@+id/action_cancel_sync"
|
||||||
android:title="@string/common_cancel_download"
|
android:title="@string/common_cancel_sync"
|
||||||
android:icon="@android:drawable/ic_menu_close_clear_cancel"
|
|
||||||
android:orderInCategory="1" />
|
|
||||||
<item
|
|
||||||
android:id="@+id/action_cancel_upload"
|
|
||||||
android:title="@string/common_cancel_upload"
|
|
||||||
android:icon="@android:drawable/ic_menu_close_clear_cancel"
|
android:icon="@android:drawable/ic_menu_close_clear_cancel"
|
||||||
android:orderInCategory="1" />
|
android:orderInCategory="1" />
|
||||||
<item
|
<item
|
||||||
|
|
|
@ -82,7 +82,7 @@
|
||||||
<string name="filedetails_created">Created:</string>
|
<string name="filedetails_created">Created:</string>
|
||||||
<string name="filedetails_modified">Modified:</string>
|
<string name="filedetails_modified">Modified:</string>
|
||||||
<string name="filedetails_download">Download</string>
|
<string name="filedetails_download">Download</string>
|
||||||
<string name="filedetails_sync_file">Refresh file</string>
|
<string name="filedetails_sync_file">Synchronize</string>
|
||||||
<string name="filedetails_renamed_in_upload_msg">File was renamed to %1$s during upload</string>
|
<string name="filedetails_renamed_in_upload_msg">File was renamed to %1$s during upload</string>
|
||||||
<string name="list_layout">List Layout</string>
|
<string name="list_layout">List Layout</string>
|
||||||
<string name="action_share_file">Share link</string>
|
<string name="action_share_file">Share link</string>
|
||||||
|
@ -90,8 +90,7 @@
|
||||||
<string name="common_yes">Yes</string>
|
<string name="common_yes">Yes</string>
|
||||||
<string name="common_no">No</string>
|
<string name="common_no">No</string>
|
||||||
<string name="common_ok">OK</string>
|
<string name="common_ok">OK</string>
|
||||||
<string name="common_cancel_download">Cancel download</string>
|
<string name="common_cancel_sync">Cancel synchronization</string>
|
||||||
<string name="common_cancel_upload">Cancel upload</string>
|
|
||||||
<string name="common_cancel">Cancel</string>
|
<string name="common_cancel">Cancel</string>
|
||||||
<string name="common_save_exit">Save & Exit</string>
|
<string name="common_save_exit">Save & Exit</string>
|
||||||
<string name="common_error">Error</string>
|
<string name="common_error">Error</string>
|
||||||
|
@ -343,7 +342,7 @@
|
||||||
<string name="prefs_category_security">Security</string>
|
<string name="prefs_category_security">Security</string>
|
||||||
|
|
||||||
<string name="prefs_instant_video_upload_path_title">Upload Video Path</string>
|
<string name="prefs_instant_video_upload_path_title">Upload Video Path</string>
|
||||||
<string name="download_folder_failed_content">Download of %1$s folder could not be completed</string>
|
<string name="sync_folder_failed_content">Synchronization of %1$s folder could not be completed</string>
|
||||||
|
|
||||||
<string name="shared_subject_header">shared</string>
|
<string name="shared_subject_header">shared</string>
|
||||||
<string name="with_you_subject_header">with you</string>
|
<string name="with_you_subject_header">with you</string>
|
||||||
|
|
|
@ -24,8 +24,10 @@ import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
|
|
||||||
import android.accounts.Account;
|
import android.accounts.Account;
|
||||||
|
@ -88,18 +90,10 @@ public class FileDataStorageManager {
|
||||||
return mAccount;
|
return mAccount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setContentResolver(ContentResolver cr) {
|
|
||||||
mContentResolver = cr;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ContentResolver getContentResolver() {
|
public ContentResolver getContentResolver() {
|
||||||
return mContentResolver;
|
return mContentResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setContentProviderClient(ContentProviderClient cp) {
|
|
||||||
mContentProviderClient = cp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ContentProviderClient getContentProviderClient() {
|
public ContentProviderClient getContentProviderClient() {
|
||||||
return mContentProviderClient;
|
return mContentProviderClient;
|
||||||
}
|
}
|
||||||
|
@ -188,7 +182,6 @@ public class FileDataStorageManager {
|
||||||
cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, file.getFileLength());
|
cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, file.getFileLength());
|
||||||
cv.put(ProviderTableMeta.FILE_CONTENT_TYPE, file.getMimetype());
|
cv.put(ProviderTableMeta.FILE_CONTENT_TYPE, file.getMimetype());
|
||||||
cv.put(ProviderTableMeta.FILE_NAME, file.getFileName());
|
cv.put(ProviderTableMeta.FILE_NAME, file.getFileName());
|
||||||
//if (file.getParentId() != DataStorageManager.ROOT_PARENT_ID)
|
|
||||||
cv.put(ProviderTableMeta.FILE_PARENT, file.getParentId());
|
cv.put(ProviderTableMeta.FILE_PARENT, file.getParentId());
|
||||||
cv.put(ProviderTableMeta.FILE_PATH, file.getRemotePath());
|
cv.put(ProviderTableMeta.FILE_PATH, file.getRemotePath());
|
||||||
if (!file.isFolder())
|
if (!file.isFolder())
|
||||||
|
@ -204,11 +197,12 @@ public class FileDataStorageManager {
|
||||||
cv.put(ProviderTableMeta.FILE_REMOTE_ID, file.getRemoteId());
|
cv.put(ProviderTableMeta.FILE_REMOTE_ID, file.getRemoteId());
|
||||||
cv.put(ProviderTableMeta.FILE_UPDATE_THUMBNAIL, file.needsUpdateThumbnail());
|
cv.put(ProviderTableMeta.FILE_UPDATE_THUMBNAIL, file.needsUpdateThumbnail());
|
||||||
cv.put(ProviderTableMeta.FILE_IS_DOWNLOADING, file.isDownloading());
|
cv.put(ProviderTableMeta.FILE_IS_DOWNLOADING, file.isDownloading());
|
||||||
|
cv.put(ProviderTableMeta.FILE_ETAG_IN_CONFLICT, file.getEtagInConflict());
|
||||||
|
|
||||||
boolean sameRemotePath = fileExists(file.getRemotePath());
|
boolean sameRemotePath = fileExists(file.getRemotePath());
|
||||||
if (sameRemotePath || fileExists(file.getFileId())) { // for renamed files; no more delete and create
|
if (sameRemotePath || fileExists(file.getFileId())) { // for renamed files; no more delete and create
|
||||||
|
|
||||||
OCFile oldFile = null;
|
OCFile oldFile;
|
||||||
if (sameRemotePath) {
|
if (sameRemotePath) {
|
||||||
oldFile = getFileByPath(file.getRemotePath());
|
oldFile = getFileByPath(file.getRemotePath());
|
||||||
file.setFileId(oldFile.getFileId());
|
file.setFileId(oldFile.getFileId());
|
||||||
|
@ -254,12 +248,6 @@ public class FileDataStorageManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (file.isFolder()) {
|
|
||||||
// updateFolderSize(file.getFileId());
|
|
||||||
// } else {
|
|
||||||
// updateFolderSize(file.getParentId());
|
|
||||||
// }
|
|
||||||
|
|
||||||
return overriden;
|
return overriden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -313,6 +301,7 @@ public class FileDataStorageManager {
|
||||||
cv.put(ProviderTableMeta.FILE_REMOTE_ID, file.getRemoteId());
|
cv.put(ProviderTableMeta.FILE_REMOTE_ID, file.getRemoteId());
|
||||||
cv.put(ProviderTableMeta.FILE_UPDATE_THUMBNAIL, file.needsUpdateThumbnail());
|
cv.put(ProviderTableMeta.FILE_UPDATE_THUMBNAIL, file.needsUpdateThumbnail());
|
||||||
cv.put(ProviderTableMeta.FILE_IS_DOWNLOADING, file.isDownloading());
|
cv.put(ProviderTableMeta.FILE_IS_DOWNLOADING, file.isDownloading());
|
||||||
|
cv.put(ProviderTableMeta.FILE_ETAG_IN_CONFLICT, file.getEtagInConflict());
|
||||||
|
|
||||||
boolean existsByPath = fileExists(file.getRemotePath());
|
boolean existsByPath = fileExists(file.getRemotePath());
|
||||||
if (existsByPath || fileExists(file.getFileId())) {
|
if (existsByPath || fileExists(file.getFileId())) {
|
||||||
|
@ -337,7 +326,6 @@ public class FileDataStorageManager {
|
||||||
for (OCFile file : filesToRemove) {
|
for (OCFile file : filesToRemove) {
|
||||||
if (file.getParentId() == folder.getFileId()) {
|
if (file.getParentId() == folder.getFileId()) {
|
||||||
whereArgs = new String[]{mAccount.name, file.getRemotePath()};
|
whereArgs = new String[]{mAccount.name, file.getRemotePath()};
|
||||||
//Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_FILE, "" + file.getFileId());
|
|
||||||
if (file.isFolder()) {
|
if (file.isFolder()) {
|
||||||
operations.add(ContentProviderOperation.newDelete(
|
operations.add(ContentProviderOperation.newDelete(
|
||||||
ContentUris.withAppendedId(
|
ContentUris.withAppendedId(
|
||||||
|
@ -434,43 +422,9 @@ public class FileDataStorageManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//updateFolderSize(folder.getFileId());
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// /**
|
|
||||||
// *
|
|
||||||
// * @param id
|
|
||||||
// */
|
|
||||||
// private void updateFolderSize(long id) {
|
|
||||||
// if (id > FileDataStorageManager.ROOT_PARENT_ID) {
|
|
||||||
// Log_OC.d(TAG, "Updating size of " + id);
|
|
||||||
// if (getContentResolver() != null) {
|
|
||||||
// getContentResolver().update(ProviderTableMeta.CONTENT_URI_DIR,
|
|
||||||
// new ContentValues(),
|
|
||||||
// won't be used, but cannot be null; crashes in KLP
|
|
||||||
// ProviderTableMeta._ID + "=?",
|
|
||||||
// new String[] { String.valueOf(id) });
|
|
||||||
// } else {
|
|
||||||
// try {
|
|
||||||
// getContentProviderClient().update(ProviderTableMeta.CONTENT_URI_DIR,
|
|
||||||
// new ContentValues(),
|
|
||||||
// won't be used, but cannot be null; crashes in KLP
|
|
||||||
// ProviderTableMeta._ID + "=?",
|
|
||||||
// new String[] { String.valueOf(id) });
|
|
||||||
//
|
|
||||||
// } catch (RemoteException e) {
|
|
||||||
// Log_OC.e(
|
|
||||||
// TAG, "Exception in update of folder size through compatibility patch " + e.getMessage());
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// Log_OC.e(TAG, "not updating size for folder " + id);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
public boolean removeFile(OCFile file, boolean removeDBData, boolean removeLocalCopy) {
|
public boolean removeFile(OCFile file, boolean removeDBData, boolean removeLocalCopy) {
|
||||||
boolean success = true;
|
boolean success = true;
|
||||||
if (file != null) {
|
if (file != null) {
|
||||||
|
@ -505,6 +459,7 @@ public class FileDataStorageManager {
|
||||||
// maybe unnecessary, but should be checked TODO remove if unnecessary
|
// maybe unnecessary, but should be checked TODO remove if unnecessary
|
||||||
file.setStoragePath(null);
|
file.setStoragePath(null);
|
||||||
saveFile(file);
|
saveFile(file);
|
||||||
|
saveConflict(file, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -942,44 +897,12 @@ public class FileDataStorageManager {
|
||||||
c.getColumnIndex(ProviderTableMeta.FILE_UPDATE_THUMBNAIL)) == 1 ? true : false);
|
c.getColumnIndex(ProviderTableMeta.FILE_UPDATE_THUMBNAIL)) == 1 ? true : false);
|
||||||
file.setDownloading(c.getInt(
|
file.setDownloading(c.getInt(
|
||||||
c.getColumnIndex(ProviderTableMeta.FILE_IS_DOWNLOADING)) == 1 ? true : false);
|
c.getColumnIndex(ProviderTableMeta.FILE_IS_DOWNLOADING)) == 1 ? true : false);
|
||||||
|
file.setEtagInConflict(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_ETAG_IN_CONFLICT)));
|
||||||
|
|
||||||
}
|
}
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns if the file/folder is shared by link or not
|
|
||||||
*
|
|
||||||
* @param path Path of the file/folder
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public boolean isShareByLink(String path) {
|
|
||||||
Cursor c = getCursorForValue(ProviderTableMeta.FILE_STORAGE_PATH, path);
|
|
||||||
OCFile file = null;
|
|
||||||
if (c.moveToFirst()) {
|
|
||||||
file = createFileInstance(c);
|
|
||||||
}
|
|
||||||
c.close();
|
|
||||||
return file.isShareByLink();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the public link of the file/folder
|
|
||||||
*
|
|
||||||
* @param path Path of the file/folder
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public String getPublicLink(String path) {
|
|
||||||
Cursor c = getCursorForValue(ProviderTableMeta.FILE_STORAGE_PATH, path);
|
|
||||||
OCFile file = null;
|
|
||||||
if (c.moveToFirst()) {
|
|
||||||
file = createFileInstance(c);
|
|
||||||
}
|
|
||||||
c.close();
|
|
||||||
return file.getPublicLink();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Methods for Shares
|
// Methods for Shares
|
||||||
public boolean saveShare(OCShare share) {
|
public boolean saveShare(OCShare share) {
|
||||||
boolean overriden = false;
|
boolean overriden = false;
|
||||||
|
@ -1310,6 +1233,7 @@ public class FileDataStorageManager {
|
||||||
ProviderTableMeta.FILE_IS_DOWNLOADING,
|
ProviderTableMeta.FILE_IS_DOWNLOADING,
|
||||||
file.isDownloading() ? 1 : 0
|
file.isDownloading() ? 1 : 0
|
||||||
);
|
);
|
||||||
|
cv.put(ProviderTableMeta.FILE_ETAG_IN_CONFLICT, file.getEtagInConflict());
|
||||||
|
|
||||||
boolean existsByPath = fileExists(file.getRemotePath());
|
boolean existsByPath = fileExists(file.getRemotePath());
|
||||||
if (existsByPath || fileExists(file.getFileId())) {
|
if (existsByPath || fileExists(file.getFileId())) {
|
||||||
|
@ -1490,44 +1414,6 @@ public class FileDataStorageManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return preparedOperations;
|
return preparedOperations;
|
||||||
|
|
||||||
/*
|
|
||||||
if (operations.size() > 0) {
|
|
||||||
try {
|
|
||||||
if (getContentResolver() != null) {
|
|
||||||
getContentResolver().applyBatch(MainApp.getAuthority(), operations);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
getContentProviderClient().applyBatch(operations);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (OperationApplicationException e) {
|
|
||||||
Log_OC.e(TAG, "Exception in batch of operations " + e.getMessage());
|
|
||||||
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
Log_OC.e(TAG, "Exception in batch of operations " + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
if (getContentResolver() != null) {
|
|
||||||
|
|
||||||
getContentResolver().delete(ProviderTableMeta.CONTENT_URI_SHARE,
|
|
||||||
where,
|
|
||||||
whereArgs);
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
getContentProviderClient().delete( ProviderTableMeta.CONTENT_URI_SHARE,
|
|
||||||
where,
|
|
||||||
whereArgs);
|
|
||||||
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
Log_OC.e(TAG, "Exception deleting shares in a folder " + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void triggerMediaScan(String path) {
|
public void triggerMediaScan(String path) {
|
||||||
|
@ -1578,4 +1464,138 @@ public class FileDataStorageManager {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void saveConflict(OCFile file, String etagInConflict) {
|
||||||
|
if (!file.isDown()) {
|
||||||
|
etagInConflict = null;
|
||||||
|
}
|
||||||
|
ContentValues cv = new ContentValues();
|
||||||
|
cv.put(ProviderTableMeta.FILE_ETAG_IN_CONFLICT, etagInConflict);
|
||||||
|
int updated = 0;
|
||||||
|
if (getContentResolver() != null) {
|
||||||
|
updated = getContentResolver().update(
|
||||||
|
ProviderTableMeta.CONTENT_URI_FILE,
|
||||||
|
cv,
|
||||||
|
ProviderTableMeta._ID + "=?",
|
||||||
|
new String[] { String.valueOf(file.getFileId())}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
updated = getContentProviderClient().update(
|
||||||
|
ProviderTableMeta.CONTENT_URI_FILE,
|
||||||
|
cv,
|
||||||
|
ProviderTableMeta._ID + "=?",
|
||||||
|
new String[]{String.valueOf(file.getFileId())}
|
||||||
|
);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log_OC.e(TAG, "Failed saving conflict in database " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log_OC.d(TAG, "Number of files updated with CONFLICT: " + updated);
|
||||||
|
|
||||||
|
if (updated > 0) {
|
||||||
|
if (etagInConflict != null) {
|
||||||
|
/// set conflict in all ancestor folders
|
||||||
|
|
||||||
|
long parentId = file.getParentId();
|
||||||
|
Set<String> ancestorIds = new HashSet<String>();
|
||||||
|
while (parentId != FileDataStorageManager.ROOT_PARENT_ID) {
|
||||||
|
ancestorIds.add(Long.toString(parentId));
|
||||||
|
parentId = getFileById(parentId).getParentId();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ancestorIds.size() > 0) {
|
||||||
|
StringBuffer whereBuffer = new StringBuffer();
|
||||||
|
whereBuffer.append(ProviderTableMeta._ID).append(" IN (");
|
||||||
|
for (int i = 0; i < ancestorIds.size() - 1; i++) {
|
||||||
|
whereBuffer.append("?,");
|
||||||
|
}
|
||||||
|
whereBuffer.append("?");
|
||||||
|
whereBuffer.append(")");
|
||||||
|
|
||||||
|
if (getContentResolver() != null) {
|
||||||
|
updated = getContentResolver().update(
|
||||||
|
ProviderTableMeta.CONTENT_URI_FILE,
|
||||||
|
cv,
|
||||||
|
whereBuffer.toString(),
|
||||||
|
ancestorIds.toArray(new String[]{})
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
updated = getContentProviderClient().update(
|
||||||
|
ProviderTableMeta.CONTENT_URI_FILE,
|
||||||
|
cv,
|
||||||
|
whereBuffer.toString(),
|
||||||
|
ancestorIds.toArray(new String[]{})
|
||||||
|
);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log_OC.e(TAG, "Failed saving conflict in database " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // else file is ROOT folder, no parent to set in conflict
|
||||||
|
|
||||||
|
} else {
|
||||||
|
/// update conflict in ancestor folders
|
||||||
|
// (not directly unset; maybe there are more conflicts below them)
|
||||||
|
String parentPath = file.getRemotePath();
|
||||||
|
if (parentPath.endsWith(OCFile.PATH_SEPARATOR)) {
|
||||||
|
parentPath = parentPath.substring(0, parentPath.length() - 1);
|
||||||
|
}
|
||||||
|
parentPath = parentPath.substring(0, parentPath.lastIndexOf(OCFile.PATH_SEPARATOR) + 1);
|
||||||
|
|
||||||
|
Log_OC.d(TAG, "checking parents to remove conflict; STARTING with " + parentPath);
|
||||||
|
while (parentPath.length() > 0) {
|
||||||
|
|
||||||
|
String where =
|
||||||
|
ProviderTableMeta.FILE_ETAG_IN_CONFLICT + " IS NOT NULL AND " +
|
||||||
|
ProviderTableMeta.FILE_CONTENT_TYPE + " != 'DIR' AND " +
|
||||||
|
ProviderTableMeta.FILE_ACCOUNT_OWNER + " = ? AND " +
|
||||||
|
ProviderTableMeta.FILE_PATH + " LIKE ?";
|
||||||
|
Cursor descendentsInConflict = getContentResolver().query(
|
||||||
|
ProviderTableMeta.CONTENT_URI_FILE,
|
||||||
|
new String[]{ProviderTableMeta._ID},
|
||||||
|
where,
|
||||||
|
new String[]{mAccount.name, parentPath + "%"},
|
||||||
|
null
|
||||||
|
);
|
||||||
|
if (descendentsInConflict == null || descendentsInConflict.getCount() == 0) {
|
||||||
|
Log_OC.d(TAG, "NO MORE conflicts in " + parentPath);
|
||||||
|
if (getContentResolver() != null) {
|
||||||
|
updated = getContentResolver().update(
|
||||||
|
ProviderTableMeta.CONTENT_URI_FILE,
|
||||||
|
cv,
|
||||||
|
ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " +
|
||||||
|
ProviderTableMeta.FILE_PATH + "=?",
|
||||||
|
new String[]{mAccount.name, parentPath}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
updated = getContentProviderClient().update(
|
||||||
|
ProviderTableMeta.CONTENT_URI_FILE,
|
||||||
|
cv,
|
||||||
|
ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " +
|
||||||
|
ProviderTableMeta.FILE_PATH + "=?"
|
||||||
|
, new String[]{mAccount.name, parentPath}
|
||||||
|
);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log_OC.e(TAG, "Failed saving conflict in database " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log_OC.d(TAG, "STILL " + descendentsInConflict.getCount() + " in " + parentPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (descendentsInConflict != null) {
|
||||||
|
descendentsInConflict.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
parentPath = parentPath.substring(0, parentPath.length() - 1); // trim last /
|
||||||
|
parentPath = parentPath.substring(0, parentPath.lastIndexOf(OCFile.PATH_SEPARATOR) + 1);
|
||||||
|
Log_OC.d(TAG, "checking parents to remove conflict; NEXT " + parentPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,6 +74,8 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
|
||||||
|
|
||||||
private boolean mIsDownloading;
|
private boolean mIsDownloading;
|
||||||
|
|
||||||
|
private String mEtagInConflict; // Save file etag in the server, when there is a conflict. No conflict = null
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create new {@link OCFile} with given path.
|
* Create new {@link OCFile} with given path.
|
||||||
|
@ -115,8 +117,9 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
|
||||||
mPublicLink = source.readString();
|
mPublicLink = source.readString();
|
||||||
mPermissions = source.readString();
|
mPermissions = source.readString();
|
||||||
mRemoteId = source.readString();
|
mRemoteId = source.readString();
|
||||||
mNeedsUpdateThumbnail = source.readInt() == 0;
|
mNeedsUpdateThumbnail = source.readInt() == 1;
|
||||||
mIsDownloading = source.readInt() == 0;
|
mIsDownloading = source.readInt() == 1;
|
||||||
|
mEtagInConflict = source.readString();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,6 +145,7 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
|
||||||
dest.writeString(mRemoteId);
|
dest.writeString(mRemoteId);
|
||||||
dest.writeInt(mNeedsUpdateThumbnail ? 1 : 0);
|
dest.writeInt(mNeedsUpdateThumbnail ? 1 : 0);
|
||||||
dest.writeInt(mIsDownloading ? 1 : 0);
|
dest.writeInt(mIsDownloading ? 1 : 0);
|
||||||
|
dest.writeString(mEtagInConflict);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -315,24 +319,6 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
|
||||||
return mMimeType;
|
return mMimeType;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a file to this directory. If this file is not a directory, an
|
|
||||||
* exception gets thrown.
|
|
||||||
*
|
|
||||||
* @param file to add
|
|
||||||
* @throws IllegalStateException if you try to add a something and this is
|
|
||||||
* not a directory
|
|
||||||
*/
|
|
||||||
public void addFile(OCFile file) throws IllegalStateException {
|
|
||||||
if (isFolder()) {
|
|
||||||
file.mParentId = mId;
|
|
||||||
mNeedsUpdating = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"This is not a directory where you can add stuff to!");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used internally. Reset all file properties
|
* Used internally. Reset all file properties
|
||||||
*/
|
*/
|
||||||
|
@ -357,6 +343,7 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
|
||||||
mRemoteId = null;
|
mRemoteId = null;
|
||||||
mNeedsUpdateThumbnail = false;
|
mNeedsUpdateThumbnail = false;
|
||||||
mIsDownloading = false;
|
mIsDownloading = false;
|
||||||
|
mEtagInConflict = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -498,10 +485,9 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setEtag(String etag) {
|
public void setEtag(String etag) {
|
||||||
this.mEtag = etag;
|
this.mEtag = (etag != null ? etag : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean isShareByLink() {
|
public boolean isShareByLink() {
|
||||||
return mShareByLink;
|
return mShareByLink;
|
||||||
}
|
}
|
||||||
|
@ -598,8 +584,11 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
|
||||||
this.mIsDownloading = isDownloading;
|
this.mIsDownloading = isDownloading;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSynchronizing() {
|
public String getEtagInConflict() {
|
||||||
// TODO real implementation
|
return mEtagInConflict;
|
||||||
return false;
|
}
|
||||||
|
|
||||||
|
public void setEtagInConflict(String etagInConflict) {
|
||||||
|
mEtagInConflict = etagInConflict;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ import com.owncloud.android.MainApp;
|
||||||
public class ProviderMeta {
|
public class ProviderMeta {
|
||||||
|
|
||||||
public static final String DB_NAME = "filelist";
|
public static final String DB_NAME = "filelist";
|
||||||
public static final int DB_VERSION = 10;
|
public static final int DB_VERSION = 11;
|
||||||
|
|
||||||
private ProviderMeta() {
|
private ProviderMeta() {
|
||||||
}
|
}
|
||||||
|
@ -72,6 +72,7 @@ public class ProviderMeta {
|
||||||
public static final String FILE_REMOTE_ID = "remote_id";
|
public static final String FILE_REMOTE_ID = "remote_id";
|
||||||
public static final String FILE_UPDATE_THUMBNAIL = "update_thumbnail";
|
public static final String FILE_UPDATE_THUMBNAIL = "update_thumbnail";
|
||||||
public static final String FILE_IS_DOWNLOADING= "is_downloading";
|
public static final String FILE_IS_DOWNLOADING= "is_downloading";
|
||||||
|
public static final String FILE_ETAG_IN_CONFLICT = "etag_in_conflict";
|
||||||
|
|
||||||
public static final String FILE_DEFAULT_SORT_ORDER = FILE_NAME
|
public static final String FILE_DEFAULT_SORT_ORDER = FILE_NAME
|
||||||
+ " collate nocase asc";
|
+ " collate nocase asc";
|
||||||
|
|
|
@ -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.FileDownloader.FileDownloaderBinder;
|
||||||
import com.owncloud.android.files.services.FileUploader;
|
import com.owncloud.android.files.services.FileUploader;
|
||||||
import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
|
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.services.OperationsService.OperationsServiceBinder;
|
||||||
import com.owncloud.android.ui.activity.ComponentsGetter;
|
import com.owncloud.android.ui.activity.ComponentsGetter;
|
||||||
|
|
||||||
|
@ -106,21 +105,25 @@ public class FileMenuFilter {
|
||||||
* @param toHide List to save the options that must be shown in the menu.
|
* @param toHide List to save the options that must be shown in the menu.
|
||||||
*/
|
*/
|
||||||
private void filter(List<Integer> toShow, List <Integer> toHide) {
|
private void filter(List<Integer> toShow, List <Integer> toHide) {
|
||||||
boolean downloading = false;
|
boolean synchronizing = false;
|
||||||
boolean uploading = false;
|
|
||||||
if (mComponentsGetter != null && mFile != null && mAccount != null) {
|
if (mComponentsGetter != null && mFile != null && mAccount != null) {
|
||||||
FileDownloaderBinder downloaderBinder = mComponentsGetter.getFileDownloaderBinder();
|
|
||||||
downloading = (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, mFile));
|
|
||||||
OperationsServiceBinder opsBinder = mComponentsGetter.getOperationsServiceBinder();
|
OperationsServiceBinder opsBinder = mComponentsGetter.getOperationsServiceBinder();
|
||||||
downloading |= (opsBinder != null && opsBinder.isSynchronizing(mAccount, mFile.getRemotePath()));
|
|
||||||
FileUploaderBinder uploaderBinder = mComponentsGetter.getFileUploaderBinder();
|
FileUploaderBinder uploaderBinder = mComponentsGetter.getFileUploaderBinder();
|
||||||
uploading = (uploaderBinder != null && uploaderBinder.isUploading(mAccount, mFile));
|
FileDownloaderBinder downloaderBinder = mComponentsGetter.getFileDownloaderBinder();
|
||||||
|
synchronizing = (
|
||||||
|
// comparing local and remote
|
||||||
|
(opsBinder != null && opsBinder.isSynchronizing(mAccount, mFile.getRemotePath())) ||
|
||||||
|
// downloading
|
||||||
|
(downloaderBinder != null && downloaderBinder.isDownloading(mAccount, mFile)) ||
|
||||||
|
// uploading
|
||||||
|
(uploaderBinder != null && uploaderBinder.isUploading(mAccount, mFile))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// decision is taken for each possible action on a file in the menu
|
/// decision is taken for each possible action on a file in the menu
|
||||||
|
|
||||||
// DOWNLOAD
|
// DOWNLOAD
|
||||||
if (mFile == null || mFile.isDown() || downloading || uploading) {
|
if (mFile == null || mFile.isDown() || mFile.isFolder() || synchronizing) {
|
||||||
toHide.add(R.id.action_download_file);
|
toHide.add(R.id.action_download_file);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -128,7 +131,7 @@ public class FileMenuFilter {
|
||||||
}
|
}
|
||||||
|
|
||||||
// RENAME
|
// RENAME
|
||||||
if (mFile == null || downloading || uploading) {
|
if (mFile == null || synchronizing) {
|
||||||
toHide.add(R.id.action_rename_file);
|
toHide.add(R.id.action_rename_file);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -136,7 +139,7 @@ public class FileMenuFilter {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MOVE & COPY
|
// MOVE & COPY
|
||||||
if (mFile == null || downloading || uploading) {
|
if (mFile == null || synchronizing) {
|
||||||
toHide.add(R.id.action_move);
|
toHide.add(R.id.action_move);
|
||||||
toHide.add(R.id.action_copy);
|
toHide.add(R.id.action_copy);
|
||||||
} else {
|
} else {
|
||||||
|
@ -145,7 +148,7 @@ public class FileMenuFilter {
|
||||||
}
|
}
|
||||||
|
|
||||||
// REMOVE
|
// REMOVE
|
||||||
if (mFile == null || downloading || uploading) {
|
if (mFile == null || synchronizing) {
|
||||||
toHide.add(R.id.action_remove_file);
|
toHide.add(R.id.action_remove_file);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -153,31 +156,25 @@ public class FileMenuFilter {
|
||||||
}
|
}
|
||||||
|
|
||||||
// OPEN WITH (different to preview!)
|
// OPEN WITH (different to preview!)
|
||||||
if (mFile == null || mFile.isFolder() || !mFile.isDown() || downloading || uploading) {
|
if (mFile == null || mFile.isFolder() || !mFile.isDown() || synchronizing) {
|
||||||
toHide.add(R.id.action_open_file_with);
|
toHide.add(R.id.action_open_file_with);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
toShow.add(R.id.action_open_file_with);
|
toShow.add(R.id.action_open_file_with);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CANCEL SYNCHRONIZATION
|
||||||
|
if (mFile == null || !synchronizing) {
|
||||||
|
toHide.add(R.id.action_cancel_sync);
|
||||||
|
|
||||||
// CANCEL DOWNLOAD
|
|
||||||
if (mFile == null || !downloading) {
|
|
||||||
toHide.add(R.id.action_cancel_download);
|
|
||||||
} else {
|
} else {
|
||||||
toShow.add(R.id.action_cancel_download);
|
toShow.add(R.id.action_cancel_sync);
|
||||||
}
|
}
|
||||||
|
|
||||||
// CANCEL UPLOAD
|
// SYNC CONTENTS (BOTH FILE AND FOLDER)
|
||||||
if (mFile == null || !uploading || mFile.isFolder()) {
|
if (mFile == null || (!mFile.isFolder() && !mFile.isDown()) || synchronizing) {
|
||||||
toHide.add(R.id.action_cancel_upload);
|
|
||||||
} else {
|
|
||||||
toShow.add(R.id.action_cancel_upload);
|
|
||||||
}
|
|
||||||
|
|
||||||
// SYNC FILE CONTENTS
|
|
||||||
if (mFile == null || mFile.isFolder() || !mFile.isDown() || downloading || uploading) {
|
|
||||||
toHide.add(R.id.action_sync_file);
|
toHide.add(R.id.action_sync_file);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
toShow.add(R.id.action_sync_file);
|
toShow.add(R.id.action_sync_file);
|
||||||
}
|
}
|
||||||
|
@ -210,21 +207,21 @@ public class FileMenuFilter {
|
||||||
// SEND
|
// SEND
|
||||||
boolean sendAllowed = (mContext != null &&
|
boolean sendAllowed = (mContext != null &&
|
||||||
mContext.getString(R.string.send_files_to_other_apps).equalsIgnoreCase("on"));
|
mContext.getString(R.string.send_files_to_other_apps).equalsIgnoreCase("on"));
|
||||||
if (mFile == null || !sendAllowed || mFile.isFolder() || uploading || downloading) {
|
if (mFile == null || !sendAllowed || mFile.isFolder() || synchronizing) {
|
||||||
toHide.add(R.id.action_send_file);
|
toHide.add(R.id.action_send_file);
|
||||||
} else {
|
} else {
|
||||||
toShow.add(R.id.action_send_file);
|
toShow.add(R.id.action_send_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FAVORITES
|
// FAVORITES
|
||||||
if (mFile == null || downloading || uploading || mFile.isFolder() || mFile.isFavorite()) {
|
if (mFile == null || synchronizing || mFile.isFolder() || mFile.isFavorite()) {
|
||||||
toHide.add(R.id.action_favorite_file);
|
toHide.add(R.id.action_favorite_file);
|
||||||
} else {
|
} else {
|
||||||
toShow.add(R.id.action_favorite_file);
|
toShow.add(R.id.action_favorite_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
// UNFAVORITES
|
// UNFAVORITES
|
||||||
if (mFile == null || downloading || uploading || mFile.isFolder() || !mFile.isFavorite()) {
|
if (mFile == null || synchronizing || mFile.isFolder() || !mFile.isFavorite()) {
|
||||||
toHide.add(R.id.action_unfavorite_file);
|
toHide.add(R.id.action_unfavorite_file);
|
||||||
} else {
|
} else {
|
||||||
toShow.add(R.id.action_unfavorite_file);
|
toShow.add(R.id.action_unfavorite_file);
|
||||||
|
|
|
@ -236,9 +236,12 @@ public class FileOperationsHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request the synchronization of a file or folder with the OC server, including its contents.
|
||||||
|
*
|
||||||
|
* @param file The file or folder to synchronize
|
||||||
|
*/
|
||||||
public void syncFile(OCFile file) {
|
public void syncFile(OCFile file) {
|
||||||
|
|
||||||
if (!file.isFolder()){
|
if (!file.isFolder()){
|
||||||
Intent intent = new Intent(mFileActivity, OperationsService.class);
|
Intent intent = new Intent(mFileActivity, OperationsService.class);
|
||||||
intent.setAction(OperationsService.ACTION_SYNC_FILE);
|
intent.setAction(OperationsService.ACTION_SYNC_FILE);
|
||||||
|
@ -254,6 +257,7 @@ public class FileOperationsHelper {
|
||||||
intent.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
|
intent.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
|
||||||
intent.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
|
intent.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
|
||||||
mFileActivity.startService(intent);
|
mFileActivity.startService(intent);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -328,19 +332,11 @@ public class FileOperationsHelper {
|
||||||
|
|
||||||
// for both files and folders
|
// for both files and folders
|
||||||
FileDownloaderBinder downloaderBinder = mFileActivity.getFileDownloaderBinder();
|
FileDownloaderBinder downloaderBinder = mFileActivity.getFileDownloaderBinder();
|
||||||
FileUploaderBinder uploaderBinder = mFileActivity.getFileUploaderBinder();
|
|
||||||
if (downloaderBinder != null && downloaderBinder.isDownloading(account, file)) {
|
if (downloaderBinder != null && downloaderBinder.isDownloading(account, file)) {
|
||||||
downloaderBinder.cancel(account, file);
|
downloaderBinder.cancel(account, file);
|
||||||
|
}
|
||||||
// TODO - review why is this here, and solve in a better way
|
FileUploaderBinder uploaderBinder = mFileActivity.getFileUploaderBinder();
|
||||||
// Remove etag for parent, if file is a favorite
|
if (uploaderBinder != null && uploaderBinder.isUploading(account, file)) {
|
||||||
if (file.isFavorite()) {
|
|
||||||
OCFile parent = mFileActivity.getStorageManager().getFileById(file.getParentId());
|
|
||||||
parent.setEtag("");
|
|
||||||
mFileActivity.getStorageManager().saveFile(parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (uploaderBinder != null && uploaderBinder.isUploading(account, file)) {
|
|
||||||
uploaderBinder.cancel(account, file);
|
uploaderBinder.cancel(account, file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,14 +21,12 @@
|
||||||
package com.owncloud.android.files.services;
|
package com.owncloud.android.files.services;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.AbstractList;
|
import java.util.AbstractList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
|
|
||||||
import com.owncloud.android.MainApp;
|
|
||||||
import com.owncloud.android.R;
|
import com.owncloud.android.R;
|
||||||
import com.owncloud.android.authentication.AccountUtils;
|
import com.owncloud.android.authentication.AccountUtils;
|
||||||
import com.owncloud.android.authentication.AuthenticatorActivity;
|
import com.owncloud.android.authentication.AuthenticatorActivity;
|
||||||
|
@ -54,7 +52,6 @@ import com.owncloud.android.utils.ErrorMessageAdapter;
|
||||||
|
|
||||||
import android.accounts.Account;
|
import android.accounts.Account;
|
||||||
import android.accounts.AccountManager;
|
import android.accounts.AccountManager;
|
||||||
import android.accounts.AccountsException;
|
|
||||||
import android.accounts.OnAccountsUpdateListener;
|
import android.accounts.OnAccountsUpdateListener;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
|
@ -153,7 +150,7 @@ public class FileDownloader extends Service
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Entry point to add one or several files to the queue of downloads.
|
* Entry point to add one or several files to the queue of downloads.
|
||||||
* <p/>
|
*
|
||||||
* New downloads are added calling to startService(), resulting in a call to this method.
|
* New downloads are added calling to startService(), resulting in a call to this method.
|
||||||
* This ensures the service will keep on working although the caller activity goes away.
|
* This ensures the service will keep on working although the caller activity goes away.
|
||||||
*/
|
*/
|
||||||
|
@ -169,12 +166,6 @@ public class FileDownloader extends Service
|
||||||
} else {
|
} else {
|
||||||
final Account account = intent.getParcelableExtra(EXTRA_ACCOUNT);
|
final Account account = intent.getParcelableExtra(EXTRA_ACCOUNT);
|
||||||
final OCFile file = intent.getParcelableExtra(EXTRA_FILE);
|
final OCFile file = intent.getParcelableExtra(EXTRA_FILE);
|
||||||
|
|
||||||
/*Log_OC.v(
|
|
||||||
"NOW " + TAG + ", thread " + Thread.currentThread().getName(),
|
|
||||||
"Received request to download file"
|
|
||||||
);*/
|
|
||||||
|
|
||||||
AbstractList<String> requestedDownloads = new Vector<String>();
|
AbstractList<String> requestedDownloads = new Vector<String>();
|
||||||
try {
|
try {
|
||||||
DownloadFileOperation newDownload = new DownloadFileOperation(account, file);
|
DownloadFileOperation newDownload = new DownloadFileOperation(account, file);
|
||||||
|
@ -185,20 +176,6 @@ public class FileDownloader extends Service
|
||||||
);
|
);
|
||||||
String downloadKey = putResult.first;
|
String downloadKey = putResult.first;
|
||||||
requestedDownloads.add(downloadKey);
|
requestedDownloads.add(downloadKey);
|
||||||
/*Log_OC.v(
|
|
||||||
"NOW " + TAG + ", thread " + Thread.currentThread().getName(),
|
|
||||||
"Download on " + file.getRemotePath() + " added to queue"
|
|
||||||
);*/
|
|
||||||
|
|
||||||
// Store file on db with state 'downloading'
|
|
||||||
/*
|
|
||||||
TODO - check if helps with UI responsiveness,
|
|
||||||
letting only folders use FileDownloaderBinder to check
|
|
||||||
FileDataStorageManager storageManager =
|
|
||||||
new FileDataStorageManager(account, getContentResolver());
|
|
||||||
file.setDownloading(true);
|
|
||||||
storageManager.saveFile(file);
|
|
||||||
*/
|
|
||||||
|
|
||||||
sendBroadcastNewDownload(newDownload, putResult.second);
|
sendBroadcastNewDownload(newDownload, putResult.second);
|
||||||
|
|
||||||
|
@ -275,34 +252,23 @@ public class FileDownloader extends Service
|
||||||
* @param file A file in the queue of pending downloads
|
* @param file A file in the queue of pending downloads
|
||||||
*/
|
*/
|
||||||
public void cancel(Account account, OCFile file) {
|
public void cancel(Account account, OCFile file) {
|
||||||
/*Log_OC.v(
|
Pair<DownloadFileOperation, String> removeResult = mPendingDownloads.remove(account, file.getRemotePath());
|
||||||
"NOW " + TAG + ", thread " + Thread.currentThread().getName(),
|
|
||||||
"Received request to cancel download of " + file.getRemotePath()
|
|
||||||
);
|
|
||||||
Log_OC.v( "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
|
|
||||||
"Removing download of " + file.getRemotePath());*/
|
|
||||||
Pair<DownloadFileOperation, String> removeResult =
|
|
||||||
mPendingDownloads.remove(account, file.getRemotePath());
|
|
||||||
DownloadFileOperation download = removeResult.first;
|
DownloadFileOperation download = removeResult.first;
|
||||||
if (download != null) {
|
if (download != null) {
|
||||||
/*Log_OC.v( "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
|
|
||||||
"Canceling returned download of " + file.getRemotePath());*/
|
|
||||||
download.cancel();
|
download.cancel();
|
||||||
} else {
|
} else {
|
||||||
if (mCurrentDownload != null && mCurrentAccount != null &&
|
if (mCurrentDownload != null && mCurrentAccount != null &&
|
||||||
mCurrentDownload.getRemotePath().startsWith(file.getRemotePath()) &&
|
mCurrentDownload.getRemotePath().startsWith(file.getRemotePath()) &&
|
||||||
account.name.equals(mCurrentAccount.name)) {
|
account.name.equals(mCurrentAccount.name)) {
|
||||||
/*Log_OC.v( "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
|
|
||||||
"Canceling current sync as descendant: " + mCurrentDownload.getRemotePath());*/
|
|
||||||
mCurrentDownload.cancel();
|
mCurrentDownload.cancel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancels a pending or current upload for an account
|
* Cancels all the downloads for an account
|
||||||
*
|
*
|
||||||
* @param account Owncloud accountName where the remote file will be stored.
|
* @param account ownCloud account.
|
||||||
*/
|
*/
|
||||||
public void cancel(Account account) {
|
public void cancel(Account account) {
|
||||||
Log_OC.d(TAG, "Account= " + account.name);
|
Log_OC.d(TAG, "Account= " + account.name);
|
||||||
|
@ -325,7 +291,7 @@ public class FileDownloader extends Service
|
||||||
/**
|
/**
|
||||||
* Returns True when the file described by 'file' in the ownCloud account 'account'
|
* Returns True when the file described by 'file' in the ownCloud account 'account'
|
||||||
* is downloading or waiting to download.
|
* is downloading or waiting to download.
|
||||||
* <p/>
|
*
|
||||||
* If 'file' is a directory, returns 'true' if any of its descendant files is downloading or
|
* If 'file' is a directory, returns 'true' if any of its descendant files is downloading or
|
||||||
* waiting to download.
|
* waiting to download.
|
||||||
*
|
*
|
||||||
|
@ -349,7 +315,6 @@ public class FileDownloader extends Service
|
||||||
OnDatatransferProgressListener listener, Account account, OCFile file
|
OnDatatransferProgressListener listener, Account account, OCFile file
|
||||||
) {
|
) {
|
||||||
if (account == null || file == null || listener == null) return;
|
if (account == null || file == null || listener == null) return;
|
||||||
//String targetKey = buildKey(account, file.getRemotePath());
|
|
||||||
mBoundListeners.put(file.getFileId(), listener);
|
mBoundListeners.put(file.getFileId(), listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -357,15 +322,14 @@ public class FileDownloader extends Service
|
||||||
/**
|
/**
|
||||||
* Removes a listener interested in the progress of the download for a concrete file.
|
* Removes a listener interested in the progress of the download for a concrete file.
|
||||||
*
|
*
|
||||||
* @param listener Object to notify about progress of transfer.
|
* @param listener Object to notify about progress of transfer.
|
||||||
* @param account ownCloud account holding the file of interest.
|
* @param account ownCloud account holding the file of interest.
|
||||||
* @param file {@link OCFile} of interest for listener.
|
* @param file {@link OCFile} of interest for listener.
|
||||||
*/
|
*/
|
||||||
public void removeDatatransferProgressListener(
|
public void removeDatatransferProgressListener(
|
||||||
OnDatatransferProgressListener listener, Account account, OCFile file
|
OnDatatransferProgressListener listener, Account account, OCFile file
|
||||||
) {
|
) {
|
||||||
if (account == null || file == null || listener == null) return;
|
if (account == null || file == null || listener == null) return;
|
||||||
//String targetKey = buildKey(account, file.getRemotePath());
|
|
||||||
Long fileId = file.getFileId();
|
Long fileId = file.getFileId();
|
||||||
if (mBoundListeners.get(fileId) == listener) {
|
if (mBoundListeners.get(fileId) == listener) {
|
||||||
mBoundListeners.remove(fileId);
|
mBoundListeners.remove(fileId);
|
||||||
|
@ -375,8 +339,6 @@ public class FileDownloader extends Service
|
||||||
@Override
|
@Override
|
||||||
public void onTransferProgress(long progressRate, long totalTransferredSoFar,
|
public void onTransferProgress(long progressRate, long totalTransferredSoFar,
|
||||||
long totalToTransfer, String fileName) {
|
long totalToTransfer, String fileName) {
|
||||||
//String key = buildKey(mCurrentDownload.getAccount(),
|
|
||||||
// mCurrentDownload.getFile().getRemotePath());
|
|
||||||
OnDatatransferProgressListener boundListener =
|
OnDatatransferProgressListener boundListener =
|
||||||
mBoundListeners.get(mCurrentDownload.getFile().getFileId());
|
mBoundListeners.get(mCurrentDownload.getFile().getFileId());
|
||||||
if (boundListener != null) {
|
if (boundListener != null) {
|
||||||
|
@ -385,23 +347,12 @@ public class FileDownloader extends Service
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Review downloads and cancel it if its account doesn't exist
|
|
||||||
*/
|
|
||||||
public void checkAccountOfCurrentDownload() {
|
|
||||||
if (mCurrentDownload != null &&
|
|
||||||
!AccountUtils.exists(mCurrentDownload.getAccount(), getApplicationContext())) {
|
|
||||||
mCurrentDownload.cancel();
|
|
||||||
}
|
|
||||||
// The rest of downloads are cancelled when they try to start
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Download worker. Performs the pending downloads in the order they were requested.
|
* Download worker. Performs the pending downloads in the order they were requested.
|
||||||
* <p/>
|
|
||||||
* Created with the Looper of a new thread, started in {@link FileUploader#onCreate()}.
|
* Created with the Looper of a new thread, started in {@link FileUploader#onCreate()}.
|
||||||
*/
|
*/
|
||||||
private static class ServiceHandler extends Handler {
|
private static class ServiceHandler extends Handler {
|
||||||
|
@ -440,14 +391,13 @@ public class FileDownloader extends Service
|
||||||
*/
|
*/
|
||||||
private void downloadFile(String downloadKey) {
|
private void downloadFile(String downloadKey) {
|
||||||
|
|
||||||
/*Log_OC.v( "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
|
|
||||||
"Getting download of " + downloadKey);*/
|
|
||||||
mCurrentDownload = mPendingDownloads.get(downloadKey);
|
mCurrentDownload = mPendingDownloads.get(downloadKey);
|
||||||
|
|
||||||
if (mCurrentDownload != null) {
|
if (mCurrentDownload != null) {
|
||||||
// Detect if the account exists
|
// Detect if the account exists
|
||||||
if (AccountUtils.exists(mCurrentDownload.getAccount(), getApplicationContext())) {
|
if (AccountUtils.exists(mCurrentDownload.getAccount(), getApplicationContext())) {
|
||||||
Log_OC.d(TAG, "Account " + mCurrentDownload.getAccount().name + " exists");
|
Log_OC.d(TAG, "Account " + mCurrentDownload.getAccount().name + " exists");
|
||||||
|
|
||||||
notifyDownloadStart(mCurrentDownload);
|
notifyDownloadStart(mCurrentDownload);
|
||||||
|
|
||||||
RemoteOperationResult downloadResult = null;
|
RemoteOperationResult downloadResult = null;
|
||||||
|
@ -470,26 +420,16 @@ public class FileDownloader extends Service
|
||||||
|
|
||||||
|
|
||||||
/// perform the download
|
/// perform the download
|
||||||
/*Log_OC.v( "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
|
|
||||||
"Executing download of " + mCurrentDownload.getRemotePath());*/
|
|
||||||
downloadResult = mCurrentDownload.execute(mDownloadClient);
|
downloadResult = mCurrentDownload.execute(mDownloadClient);
|
||||||
if (downloadResult.isSuccess()) {
|
if (downloadResult.isSuccess()) {
|
||||||
saveDownloadedFile();
|
saveDownloadedFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (AccountsException e) {
|
} catch (Exception e) {
|
||||||
Log_OC.e(TAG, "Error while trying to get authorization for "
|
Log_OC.e(TAG, "Error downloading", e);
|
||||||
+ mCurrentAccount.name, e);
|
|
||||||
downloadResult = new RemoteOperationResult(e);
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log_OC.e(TAG, "Error while trying to get authorization for "
|
|
||||||
+ mCurrentAccount.name, e);
|
|
||||||
downloadResult = new RemoteOperationResult(e);
|
downloadResult = new RemoteOperationResult(e);
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
/*Log_OC.v( "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
|
|
||||||
"Removing payload " + mCurrentDownload.getRemotePath());*/
|
|
||||||
|
|
||||||
Pair<DownloadFileOperation, String> removeResult =
|
Pair<DownloadFileOperation, String> removeResult =
|
||||||
mPendingDownloads.removePayload(mCurrentAccount,
|
mPendingDownloads.removePayload(mCurrentAccount,
|
||||||
mCurrentDownload.getRemotePath());
|
mCurrentDownload.getRemotePath());
|
||||||
|
@ -497,9 +437,9 @@ public class FileDownloader extends Service
|
||||||
/// notify result
|
/// notify result
|
||||||
notifyDownloadResult(mCurrentDownload, downloadResult);
|
notifyDownloadResult(mCurrentDownload, downloadResult);
|
||||||
|
|
||||||
sendBroadcastDownloadFinished(mCurrentDownload, downloadResult,
|
sendBroadcastDownloadFinished(mCurrentDownload, downloadResult, removeResult.second);
|
||||||
removeResult.second);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Cancel the transfer
|
// Cancel the transfer
|
||||||
Log_OC.d(TAG, "Account " + mCurrentDownload.getAccount().toString() +
|
Log_OC.d(TAG, "Account " + mCurrentDownload.getAccount().toString() +
|
||||||
|
@ -513,6 +453,8 @@ public class FileDownloader extends Service
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the OC File after a successful download.
|
* Updates the OC File after a successful download.
|
||||||
|
*
|
||||||
|
* TODO move to DownloadFileOperation
|
||||||
*/
|
*/
|
||||||
private void saveDownloadedFile() {
|
private void saveDownloadedFile() {
|
||||||
OCFile file = mStorageManager.getFileById(mCurrentDownload.getFile().getFileId());
|
OCFile file = mStorageManager.getFileById(mCurrentDownload.getFile().getFileId());
|
||||||
|
@ -522,25 +464,16 @@ public class FileDownloader extends Service
|
||||||
file.setNeedsUpdateThumbnail(true);
|
file.setNeedsUpdateThumbnail(true);
|
||||||
file.setModificationTimestamp(mCurrentDownload.getModificationTimestamp());
|
file.setModificationTimestamp(mCurrentDownload.getModificationTimestamp());
|
||||||
file.setModificationTimestampAtLastSyncForData(mCurrentDownload.getModificationTimestamp());
|
file.setModificationTimestampAtLastSyncForData(mCurrentDownload.getModificationTimestamp());
|
||||||
// file.setEtag(mCurrentDownload.getEtag()); // TODO Etag, where available
|
file.setEtag(mCurrentDownload.getEtag());
|
||||||
file.setMimetype(mCurrentDownload.getMimeType());
|
file.setMimetype(mCurrentDownload.getMimeType());
|
||||||
file.setStoragePath(mCurrentDownload.getSavePath());
|
file.setStoragePath(mCurrentDownload.getSavePath());
|
||||||
file.setFileLength((new File(mCurrentDownload.getSavePath()).length()));
|
file.setFileLength((new File(mCurrentDownload.getSavePath()).length()));
|
||||||
file.setRemoteId(mCurrentDownload.getFile().getRemoteId());
|
file.setRemoteId(mCurrentDownload.getFile().getRemoteId());
|
||||||
mStorageManager.saveFile(file);
|
mStorageManager.saveFile(file);
|
||||||
mStorageManager.triggerMediaScan(file.getStoragePath());
|
mStorageManager.triggerMediaScan(file.getStoragePath());
|
||||||
|
mStorageManager.saveConflict(file, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the OC File after a unsuccessful download
|
|
||||||
*/
|
|
||||||
private void updateUnsuccessfulDownloadedFile() {
|
|
||||||
OCFile file = mStorageManager.getFileById(mCurrentDownload.getFile().getFileId());
|
|
||||||
file.setDownloading(false);
|
|
||||||
mStorageManager.saveFile(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a status notification to show the download progress
|
* Creates a status notification to show the download progress
|
||||||
*
|
*
|
||||||
|
@ -683,6 +616,7 @@ public class FileDownloader extends Service
|
||||||
DownloadFileOperation download,
|
DownloadFileOperation download,
|
||||||
RemoteOperationResult downloadResult,
|
RemoteOperationResult downloadResult,
|
||||||
String unlinkedFromRemotePath) {
|
String unlinkedFromRemotePath) {
|
||||||
|
|
||||||
Intent end = new Intent(getDownloadFinishMessage());
|
Intent end = new Intent(getDownloadFinishMessage());
|
||||||
end.putExtra(EXTRA_DOWNLOAD_RESULT, downloadResult.isSuccess());
|
end.putExtra(EXTRA_DOWNLOAD_RESULT, downloadResult.isSuccess());
|
||||||
end.putExtra(ACCOUNT_NAME, download.getAccount().name);
|
end.putExtra(ACCOUNT_NAME, download.getAccount().name);
|
||||||
|
|
|
@ -21,18 +21,14 @@
|
||||||
package com.owncloud.android.files.services;
|
package com.owncloud.android.files.services;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.AbstractList;
|
import java.util.AbstractList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.concurrent.ConcurrentMap;
|
|
||||||
|
|
||||||
import android.accounts.Account;
|
import android.accounts.Account;
|
||||||
import android.accounts.AccountManager;
|
import android.accounts.AccountManager;
|
||||||
import android.accounts.AccountsException;
|
|
||||||
import android.accounts.OnAccountsUpdateListener;
|
import android.accounts.OnAccountsUpdateListener;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
|
@ -46,6 +42,7 @@ import android.os.Looper;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
import android.support.v4.app.NotificationCompat;
|
import android.support.v4.app.NotificationCompat;
|
||||||
|
import android.util.Pair;
|
||||||
import android.webkit.MimeTypeMap;
|
import android.webkit.MimeTypeMap;
|
||||||
|
|
||||||
import com.owncloud.android.R;
|
import com.owncloud.android.R;
|
||||||
|
@ -86,6 +83,7 @@ public class FileUploader extends Service
|
||||||
public static final String EXTRA_REMOTE_PATH = "REMOTE_PATH";
|
public static final String EXTRA_REMOTE_PATH = "REMOTE_PATH";
|
||||||
public static final String EXTRA_OLD_REMOTE_PATH = "OLD_REMOTE_PATH";
|
public static final String EXTRA_OLD_REMOTE_PATH = "OLD_REMOTE_PATH";
|
||||||
public static final String EXTRA_OLD_FILE_PATH = "OLD_FILE_PATH";
|
public static final String EXTRA_OLD_FILE_PATH = "OLD_FILE_PATH";
|
||||||
|
public static final String EXTRA_LINKED_TO_PATH = "LINKED_TO";
|
||||||
public static final String ACCOUNT_NAME = "ACCOUNT_NAME";
|
public static final String ACCOUNT_NAME = "ACCOUNT_NAME";
|
||||||
|
|
||||||
public static final String KEY_FILE = "FILE";
|
public static final String KEY_FILE = "FILE";
|
||||||
|
@ -113,11 +111,10 @@ public class FileUploader extends Service
|
||||||
private ServiceHandler mServiceHandler;
|
private ServiceHandler mServiceHandler;
|
||||||
private IBinder mBinder;
|
private IBinder mBinder;
|
||||||
private OwnCloudClient mUploadClient = null;
|
private OwnCloudClient mUploadClient = null;
|
||||||
private Account mLastAccount = null;
|
private Account mCurrentAccount = null;
|
||||||
private FileDataStorageManager mStorageManager;
|
private FileDataStorageManager mStorageManager;
|
||||||
|
|
||||||
private ConcurrentMap<String, UploadFileOperation> mPendingUploads =
|
private IndexedForest<UploadFileOperation> mPendingUploads = new IndexedForest<UploadFileOperation>();
|
||||||
new ConcurrentHashMap<String, UploadFileOperation>();
|
|
||||||
private UploadFileOperation mCurrentUpload = null;
|
private UploadFileOperation mCurrentUpload = null;
|
||||||
|
|
||||||
private NotificationManager mNotificationManager;
|
private NotificationManager mNotificationManager;
|
||||||
|
@ -132,20 +129,6 @@ public class FileUploader extends Service
|
||||||
return FileUploader.class.getName() + UPLOAD_FINISH_MESSAGE;
|
return FileUploader.class.getName() + UPLOAD_FINISH_MESSAGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Builds a key for mPendingUploads from the account and file to upload
|
|
||||||
*
|
|
||||||
* @param account Account where the file to upload is stored
|
|
||||||
* @param file File to upload
|
|
||||||
*/
|
|
||||||
private String buildRemoteName(Account account, OCFile file) {
|
|
||||||
return account.name + file.getRemotePath();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String buildRemoteName(Account account, String remotePath) {
|
|
||||||
return account.name + remotePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if an ownCloud server version should support chunked uploads.
|
* Checks if an ownCloud server version should support chunked uploads.
|
||||||
*
|
*
|
||||||
|
@ -153,6 +136,8 @@ public class FileUploader extends Service
|
||||||
* server.
|
* server.
|
||||||
* @return 'True' if the ownCloud server with version supports chunked
|
* @return 'True' if the ownCloud server with version supports chunked
|
||||||
* uploads.
|
* uploads.
|
||||||
|
*
|
||||||
|
* TODO - move to OCClient
|
||||||
*/
|
*/
|
||||||
private static boolean chunkedUploadIsSupported(OwnCloudVersion version) {
|
private static boolean chunkedUploadIsSupported(OwnCloudVersion version) {
|
||||||
return (version != null && version.compareTo(OwnCloudVersion.owncloud_v4_5) >= 0);
|
return (version != null && version.compareTo(OwnCloudVersion.owncloud_v4_5) >= 0);
|
||||||
|
@ -282,7 +267,7 @@ public class FileUploader extends Service
|
||||||
files = new OCFile[localPaths.length];
|
files = new OCFile[localPaths.length];
|
||||||
for (int i = 0; i < localPaths.length; i++) {
|
for (int i = 0; i < localPaths.length; i++) {
|
||||||
files[i] = obtainNewOCFileToUpload(remotePaths[i], localPaths[i],
|
files[i] = obtainNewOCFileToUpload(remotePaths[i], localPaths[i],
|
||||||
((mimeTypes != null) ? mimeTypes[i] : null), storageManager);
|
((mimeTypes != null) ? mimeTypes[i] : null));
|
||||||
if (files[i] == null) {
|
if (files[i] == null) {
|
||||||
// TODO @andomaex add failure Notification
|
// TODO @andomaex add failure Notification
|
||||||
return Service.START_NOT_STICKY;
|
return Service.START_NOT_STICKY;
|
||||||
|
@ -298,18 +283,23 @@ public class FileUploader extends Service
|
||||||
UploadFileOperation newUpload = null;
|
UploadFileOperation newUpload = null;
|
||||||
try {
|
try {
|
||||||
for (int i = 0; i < files.length; i++) {
|
for (int i = 0; i < files.length; i++) {
|
||||||
uploadKey = buildRemoteName(account, files[i].getRemotePath());
|
newUpload = new UploadFileOperation(
|
||||||
newUpload = new UploadFileOperation(account, files[i], chunked, isInstant,
|
account,
|
||||||
|
files[i],
|
||||||
|
chunked,
|
||||||
|
isInstant,
|
||||||
forceOverwrite, localAction,
|
forceOverwrite, localAction,
|
||||||
getApplicationContext());
|
getApplicationContext()
|
||||||
|
);
|
||||||
if (isInstant) {
|
if (isInstant) {
|
||||||
newUpload.setRemoteFolderToBeCreated();
|
newUpload.setRemoteFolderToBeCreated();
|
||||||
}
|
}
|
||||||
// Grants that the file only upload once time
|
|
||||||
mPendingUploads.putIfAbsent(uploadKey, newUpload);
|
|
||||||
|
|
||||||
newUpload.addDatatransferProgressListener(this);
|
newUpload.addDatatransferProgressListener(this);
|
||||||
newUpload.addDatatransferProgressListener((FileUploaderBinder)mBinder);
|
newUpload.addDatatransferProgressListener((FileUploaderBinder) mBinder);
|
||||||
|
Pair<String, String> putResult = mPendingUploads.putIfAbsent(
|
||||||
|
account, files[i].getRemotePath(), newUpload
|
||||||
|
);
|
||||||
|
uploadKey = putResult.first;
|
||||||
requestedUploads.add(uploadKey);
|
requestedUploads.add(uploadKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,7 +323,6 @@ public class FileUploader extends Service
|
||||||
msg.obj = requestedUploads;
|
msg.obj = requestedUploads;
|
||||||
mServiceHandler.sendMessage(msg);
|
mServiceHandler.sendMessage(msg);
|
||||||
}
|
}
|
||||||
Log_OC.i(TAG, "mPendingUploads size:" + mPendingUploads.size());
|
|
||||||
return Service.START_NOT_STICKY;
|
return Service.START_NOT_STICKY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -386,23 +375,27 @@ public class FileUploader extends Service
|
||||||
/**
|
/**
|
||||||
* Cancels a pending or current upload of a remote file.
|
* Cancels a pending or current upload of a remote file.
|
||||||
*
|
*
|
||||||
* @param account Owncloud account where the remote file will be stored.
|
* @param account ownCloud account where the remote file will be stored.
|
||||||
* @param file A file in the queue of pending uploads
|
* @param file A file in the queue of pending uploads
|
||||||
*/
|
*/
|
||||||
public void cancel(Account account, OCFile file) {
|
public void cancel(Account account, OCFile file) {
|
||||||
UploadFileOperation upload;
|
Pair<UploadFileOperation, String> removeResult = mPendingUploads.remove(account, file.getRemotePath());
|
||||||
synchronized (mPendingUploads) {
|
UploadFileOperation upload = removeResult.first;
|
||||||
upload = mPendingUploads.remove(buildRemoteName(account, file));
|
|
||||||
}
|
|
||||||
if (upload != null) {
|
if (upload != null) {
|
||||||
upload.cancel();
|
upload.cancel();
|
||||||
|
} else {
|
||||||
|
if (mCurrentUpload != null && mCurrentAccount != null &&
|
||||||
|
mCurrentUpload.getRemotePath().startsWith(file.getRemotePath()) &&
|
||||||
|
account.name.equals(mCurrentAccount.name)) {
|
||||||
|
mCurrentUpload.cancel();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancels a pending or current upload for an account
|
* Cancels all the uploads for an account
|
||||||
*
|
*
|
||||||
* @param account Owncloud accountName where the remote file will be stored.
|
* @param account ownCloud account.
|
||||||
*/
|
*/
|
||||||
public void cancel(Account account) {
|
public void cancel(Account account) {
|
||||||
Log_OC.d(TAG, "Account= " + account.name);
|
Log_OC.d(TAG, "Account= " + account.name);
|
||||||
|
@ -414,13 +407,14 @@ public class FileUploader extends Service
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Cancel pending uploads
|
// Cancel pending uploads
|
||||||
cancelUploadForAccount(account.name);
|
cancelUploadsForAccount(account);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearListeners() {
|
public void clearListeners() {
|
||||||
mBoundListeners.clear();
|
mBoundListeners.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns True when the file described by 'file' is being uploaded to
|
* Returns True when the file described by 'file' is being uploaded to
|
||||||
* the ownCloud account 'account' or waiting for it
|
* the ownCloud account 'account' or waiting for it
|
||||||
|
@ -432,22 +426,8 @@ public class FileUploader extends Service
|
||||||
* @param file A file that could be in the queue of pending uploads
|
* @param file A file that could be in the queue of pending uploads
|
||||||
*/
|
*/
|
||||||
public boolean isUploading(Account account, OCFile file) {
|
public boolean isUploading(Account account, OCFile file) {
|
||||||
if (account == null || file == null)
|
if (account == null || file == null) return false;
|
||||||
return false;
|
return (mPendingUploads.contains(account, file.getRemotePath()));
|
||||||
String targetKey = buildRemoteName(account, file);
|
|
||||||
synchronized (mPendingUploads) {
|
|
||||||
if (file.isFolder()) {
|
|
||||||
// this can be slow if there are many uploads :(
|
|
||||||
Iterator<String> it = mPendingUploads.keySet().iterator();
|
|
||||||
boolean found = false;
|
|
||||||
while (it.hasNext() && !found) {
|
|
||||||
found = it.next().startsWith(targetKey);
|
|
||||||
}
|
|
||||||
return found;
|
|
||||||
} else {
|
|
||||||
return (mPendingUploads.containsKey(targetKey));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -496,15 +476,19 @@ public class FileUploader extends Service
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Review uploads and cancel it if its account doesn't exist
|
* Builds a key for the map of listeners.
|
||||||
|
*
|
||||||
|
* TODO remove and replace key with file.getFileId() after changing current policy (upload file, then
|
||||||
|
* add to local database) to better policy (add to local database, then upload)
|
||||||
|
*
|
||||||
|
* @param account ownCloud account where the file to upload belongs.
|
||||||
|
* @param file File to upload
|
||||||
|
* @return Key
|
||||||
*/
|
*/
|
||||||
public void checkAccountOfCurrentUpload() {
|
private String buildRemoteName(Account account, OCFile file) {
|
||||||
if (mCurrentUpload != null &&
|
return account.name + file.getRemotePath();
|
||||||
!AccountUtils.exists(mCurrentUpload.getAccount(), getApplicationContext())) {
|
|
||||||
mCurrentUpload.cancel();
|
|
||||||
}
|
|
||||||
// The rest of uploads are cancelled when they try to start
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -544,17 +528,13 @@ public class FileUploader extends Service
|
||||||
/**
|
/**
|
||||||
* Core upload method: sends the file(s) to upload
|
* Core upload method: sends the file(s) to upload
|
||||||
*
|
*
|
||||||
* @param uploadKey Key to access the upload to perform, contained in
|
* @param uploadKey Key to access the upload to perform, contained in mPendingUploads
|
||||||
* mPendingUploads
|
|
||||||
*/
|
*/
|
||||||
public void uploadFile(String uploadKey) {
|
public void uploadFile(String uploadKey) {
|
||||||
|
|
||||||
synchronized (mPendingUploads) {
|
mCurrentUpload = mPendingUploads.get(uploadKey);
|
||||||
mCurrentUpload = mPendingUploads.get(uploadKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mCurrentUpload != null) {
|
if (mCurrentUpload != null) {
|
||||||
|
|
||||||
// Detect if the account exists
|
// Detect if the account exists
|
||||||
if (AccountUtils.exists(mCurrentUpload.getAccount(), getApplicationContext())) {
|
if (AccountUtils.exists(mCurrentUpload.getAccount(), getApplicationContext())) {
|
||||||
Log_OC.d(TAG, "Account " + mCurrentUpload.getAccount().name + " exists");
|
Log_OC.d(TAG, "Account " + mCurrentUpload.getAccount().name + " exists");
|
||||||
|
@ -564,16 +544,20 @@ public class FileUploader extends Service
|
||||||
RemoteOperationResult uploadResult = null, grantResult;
|
RemoteOperationResult uploadResult = null, grantResult;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
/// prepare client object to send requests to the ownCloud server
|
/// prepare client object to send the request to the ownCloud server
|
||||||
if (mUploadClient == null ||
|
if (mCurrentAccount == null || !mCurrentAccount.equals(mCurrentUpload.getAccount())) {
|
||||||
!mLastAccount.equals(mCurrentUpload.getAccount())) {
|
mCurrentAccount = mCurrentUpload.getAccount();
|
||||||
mLastAccount = mCurrentUpload.getAccount();
|
mStorageManager = new FileDataStorageManager(
|
||||||
mStorageManager =
|
mCurrentAccount,
|
||||||
new FileDataStorageManager(mLastAccount, getContentResolver());
|
getContentResolver()
|
||||||
OwnCloudAccount ocAccount = new OwnCloudAccount(mLastAccount, this);
|
);
|
||||||
mUploadClient = OwnCloudClientManagerFactory.getDefaultSingleton().
|
} // else, reuse storage manager from previous operation
|
||||||
getClientFor(ocAccount, this);
|
|
||||||
}
|
// always get client from client manager, to get fresh credentials in case of update
|
||||||
|
OwnCloudAccount ocAccount = new OwnCloudAccount(mCurrentAccount, this);
|
||||||
|
mUploadClient = OwnCloudClientManagerFactory.getDefaultSingleton().
|
||||||
|
getClientFor(ocAccount, this);
|
||||||
|
|
||||||
|
|
||||||
/// check the existence of the parent folder for the file to upload
|
/// check the existence of the parent folder for the file to upload
|
||||||
String remoteParentPath = new File(mCurrentUpload.getRemotePath()).getParent();
|
String remoteParentPath = new File(mCurrentUpload.getRemotePath()).getParent();
|
||||||
|
@ -588,43 +572,44 @@ public class FileUploader extends Service
|
||||||
uploadResult = mCurrentUpload.execute(mUploadClient);
|
uploadResult = mCurrentUpload.execute(mUploadClient);
|
||||||
if (uploadResult.isSuccess()) {
|
if (uploadResult.isSuccess()) {
|
||||||
saveUploadedFile();
|
saveUploadedFile();
|
||||||
|
|
||||||
|
} else if (uploadResult.getCode() == ResultCode.SYNC_CONFLICT) {
|
||||||
|
mStorageManager.saveConflict(mCurrentUpload.getFile(),
|
||||||
|
mCurrentUpload.getFile().getEtagInConflict());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
uploadResult = grantResult;
|
uploadResult = grantResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (AccountsException e) {
|
} catch (Exception e) {
|
||||||
Log_OC.e(TAG, "Error while trying to get autorization for " +
|
Log_OC.e(TAG, "Error uploading", e);
|
||||||
mLastAccount.name, e);
|
|
||||||
uploadResult = new RemoteOperationResult(e);
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log_OC.e(TAG, "Error while trying to get autorization for " +
|
|
||||||
mLastAccount.name, e);
|
|
||||||
uploadResult = new RemoteOperationResult(e);
|
uploadResult = new RemoteOperationResult(e);
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
synchronized (mPendingUploads) {
|
Pair<UploadFileOperation, String> removeResult;
|
||||||
mPendingUploads.remove(uploadKey);
|
if (mCurrentUpload.wasRenamed()) {
|
||||||
Log_OC.i(TAG, "Remove CurrentUploadItem from pending upload Item Map.");
|
removeResult = mPendingUploads.removePayload(
|
||||||
|
mCurrentAccount,
|
||||||
|
mCurrentUpload.getOldFile().getRemotePath()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
removeResult = mPendingUploads.removePayload(
|
||||||
|
mCurrentAccount,
|
||||||
|
mCurrentUpload.getRemotePath()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (uploadResult != null && uploadResult.isException()) {
|
|
||||||
// enforce the creation of a new client object for next uploads;
|
|
||||||
// this grant that a new socket will be created in the future if
|
|
||||||
// the current exception is due to an abrupt lose of network connection
|
|
||||||
mUploadClient = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// notify result
|
/// notify result
|
||||||
notifyUploadResult(uploadResult, mCurrentUpload);
|
notifyUploadResult(mCurrentUpload, uploadResult);
|
||||||
sendFinalBroadcast(mCurrentUpload, uploadResult);
|
|
||||||
|
sendBroadcastUploadFinished(mCurrentUpload, uploadResult, removeResult.second);
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Cancel the transfer
|
// Cancel the transfer
|
||||||
Log_OC.d(TAG, "Account " + mCurrentUpload.getAccount().toString() +
|
Log_OC.d(TAG, "Account " + mCurrentUpload.getAccount().toString() +
|
||||||
" doesn't exist");
|
" doesn't exist");
|
||||||
cancelUploadForAccount(mCurrentUpload.getAccount().name);
|
cancelUploadsForAccount(mCurrentUpload.getAccount());
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -691,7 +676,7 @@ public class FileUploader extends Service
|
||||||
* synchronized with the server, specially the modification time and Etag
|
* synchronized with the server, specially the modification time and Etag
|
||||||
* (where available)
|
* (where available)
|
||||||
*
|
*
|
||||||
* TODO refactor this ugly thing
|
* TODO move into UploadFileOperation
|
||||||
*/
|
*/
|
||||||
private void saveUploadedFile() {
|
private void saveUploadedFile() {
|
||||||
OCFile file = mCurrentUpload.getFile();
|
OCFile file = mCurrentUpload.getFile();
|
||||||
|
@ -709,6 +694,8 @@ public class FileUploader extends Service
|
||||||
if (result.isSuccess()) {
|
if (result.isSuccess()) {
|
||||||
updateOCFile(file, (RemoteFile) result.getData().get(0));
|
updateOCFile(file, (RemoteFile) result.getData().get(0));
|
||||||
file.setLastSyncDateForProperties(syncDate);
|
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
|
// / maybe this would be better as part of UploadFileOperation... or
|
||||||
|
@ -718,6 +705,7 @@ public class FileUploader extends Service
|
||||||
if (oldFile.fileExists()) {
|
if (oldFile.fileExists()) {
|
||||||
oldFile.setStoragePath(null);
|
oldFile.setStoragePath(null);
|
||||||
mStorageManager.saveFile(oldFile);
|
mStorageManager.saveFile(oldFile);
|
||||||
|
mStorageManager.saveConflict(oldFile, null);
|
||||||
|
|
||||||
} // else: it was just an automatic renaming due to a name
|
} // else: it was just an automatic renaming due to a name
|
||||||
// coincidence; nothing else is needed, the storagePath is right
|
// coincidence; nothing else is needed, the storagePath is right
|
||||||
|
@ -725,7 +713,10 @@ public class FileUploader extends Service
|
||||||
}
|
}
|
||||||
file.setNeedsUpdateThumbnail(true);
|
file.setNeedsUpdateThumbnail(true);
|
||||||
mStorageManager.saveFile(file);
|
mStorageManager.saveFile(file);
|
||||||
|
mStorageManager.saveConflict(file, null);
|
||||||
|
|
||||||
mStorageManager.triggerMediaScan(file.getStoragePath());
|
mStorageManager.triggerMediaScan(file.getStoragePath());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateOCFile(OCFile file, RemoteFile remoteFile) {
|
private void updateOCFile(OCFile file, RemoteFile remoteFile) {
|
||||||
|
@ -734,12 +725,11 @@ public class FileUploader extends Service
|
||||||
file.setMimetype(remoteFile.getMimeType());
|
file.setMimetype(remoteFile.getMimeType());
|
||||||
file.setModificationTimestamp(remoteFile.getModifiedTimestamp());
|
file.setModificationTimestamp(remoteFile.getModifiedTimestamp());
|
||||||
file.setModificationTimestampAtLastSyncForData(remoteFile.getModifiedTimestamp());
|
file.setModificationTimestampAtLastSyncForData(remoteFile.getModifiedTimestamp());
|
||||||
// file.setEtag(remoteFile.getEtag()); // TODO Etag, where available
|
file.setEtag(remoteFile.getEtag());
|
||||||
file.setRemoteId(remoteFile.getRemoteId());
|
file.setRemoteId(remoteFile.getRemoteId());
|
||||||
}
|
}
|
||||||
|
|
||||||
private OCFile obtainNewOCFileToUpload(String remotePath, String localPath, String mimeType,
|
private OCFile obtainNewOCFileToUpload(String remotePath, String localPath, String mimeType) {
|
||||||
FileDataStorageManager storageManager) {
|
|
||||||
|
|
||||||
// MIME type
|
// MIME type
|
||||||
if (mimeType == null || mimeType.length() <= 0) {
|
if (mimeType == null || mimeType.length() <= 0) {
|
||||||
|
@ -830,11 +820,11 @@ public class FileUploader extends Service
|
||||||
/**
|
/**
|
||||||
* Updates the status notification with the result of an upload operation.
|
* Updates the status notification with the result of an upload operation.
|
||||||
*
|
*
|
||||||
* @param uploadResult Result of the upload operation.
|
* @param uploadResult Result of the upload operation.
|
||||||
* @param upload Finished upload operation
|
* @param upload Finished upload operation
|
||||||
*/
|
*/
|
||||||
private void notifyUploadResult(
|
private void notifyUploadResult(UploadFileOperation upload,
|
||||||
RemoteOperationResult uploadResult, UploadFileOperation upload) {
|
RemoteOperationResult uploadResult) {
|
||||||
Log_OC.d(TAG, "NotifyUploadResult with resultCode: " + uploadResult.getCode());
|
Log_OC.d(TAG, "NotifyUploadResult with resultCode: " + uploadResult.getCode());
|
||||||
// / cancelled operation or success -> silent removal of progress notification
|
// / cancelled operation or success -> silent removal of progress notification
|
||||||
mNotificationManager.cancel(R.string.uploader_upload_in_progress_ticker);
|
mNotificationManager.cancel(R.string.uploader_upload_in_progress_ticker);
|
||||||
|
@ -941,10 +931,15 @@ public class FileUploader extends Service
|
||||||
* Sends a broadcast in order to the interested activities can update their
|
* Sends a broadcast in order to the interested activities can update their
|
||||||
* view
|
* view
|
||||||
*
|
*
|
||||||
* @param upload Finished upload operation
|
* @param upload Finished upload operation
|
||||||
* @param uploadResult Result of the upload operation
|
* @param uploadResult Result of the upload operation
|
||||||
|
* @param unlinkedFromRemotePath Path in the uploads tree where the upload was unlinked from
|
||||||
*/
|
*/
|
||||||
private void sendFinalBroadcast(UploadFileOperation upload, RemoteOperationResult uploadResult) {
|
private void sendBroadcastUploadFinished(
|
||||||
|
UploadFileOperation upload,
|
||||||
|
RemoteOperationResult uploadResult,
|
||||||
|
String unlinkedFromRemotePath) {
|
||||||
|
|
||||||
Intent end = new Intent(getUploadFinishMessage());
|
Intent end = new Intent(getUploadFinishMessage());
|
||||||
end.putExtra(EXTRA_REMOTE_PATH, upload.getRemotePath()); // real remote
|
end.putExtra(EXTRA_REMOTE_PATH, upload.getRemotePath()); // real remote
|
||||||
// path, after
|
// path, after
|
||||||
|
@ -957,6 +952,10 @@ public class FileUploader extends Service
|
||||||
end.putExtra(EXTRA_OLD_FILE_PATH, upload.getOriginalStoragePath());
|
end.putExtra(EXTRA_OLD_FILE_PATH, upload.getOriginalStoragePath());
|
||||||
end.putExtra(ACCOUNT_NAME, upload.getAccount().name);
|
end.putExtra(ACCOUNT_NAME, upload.getAccount().name);
|
||||||
end.putExtra(EXTRA_UPLOAD_RESULT, uploadResult.isSuccess());
|
end.putExtra(EXTRA_UPLOAD_RESULT, uploadResult.isSuccess());
|
||||||
|
if (unlinkedFromRemotePath != null) {
|
||||||
|
end.putExtra(EXTRA_LINKED_TO_PATH, unlinkedFromRemotePath);
|
||||||
|
}
|
||||||
|
|
||||||
sendStickyBroadcast(end);
|
sendStickyBroadcast(end);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -966,6 +965,8 @@ public class FileUploader extends Service
|
||||||
* @param localPath Full path to a file in the local file system.
|
* @param localPath Full path to a file in the local file system.
|
||||||
* @param mimeType MIME type of the file.
|
* @param mimeType MIME type of the file.
|
||||||
* @return true if is needed to add the pdf file extension to the file
|
* @return true if is needed to add the pdf file extension to the file
|
||||||
|
*
|
||||||
|
* TODO - move to OCFile or Utils class
|
||||||
*/
|
*/
|
||||||
private boolean isPdfFileFromContentProviderWithoutExtension(String localPath,
|
private boolean isPdfFileFromContentProviderWithoutExtension(String localPath,
|
||||||
String mimeType) {
|
String mimeType) {
|
||||||
|
@ -976,20 +977,11 @@ public class FileUploader extends Service
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove uploads of an account
|
* Remove uploads of an account
|
||||||
* @param accountName Name of an OC account
|
*
|
||||||
|
* @param account Downloads account to remove
|
||||||
*/
|
*/
|
||||||
private void cancelUploadForAccount(String accountName){
|
private void cancelUploadsForAccount(Account account){
|
||||||
// this can be slow if there are many uploads :(
|
// Cancel pending uploads
|
||||||
Iterator<String> it = mPendingUploads.keySet().iterator();
|
mPendingUploads.remove(account);
|
||||||
Log_OC.d(TAG, "Number of pending updloads= " + mPendingUploads.size());
|
|
||||||
while (it.hasNext()) {
|
|
||||||
String key = it.next();
|
|
||||||
Log_OC.d(TAG, "mPendingUploads CANCELLED " + key);
|
|
||||||
if (key.startsWith(accountName)) {
|
|
||||||
synchronized (mPendingUploads) {
|
|
||||||
mPendingUploads.remove(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,6 @@ import java.util.Iterator;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import com.owncloud.android.MainApp;
|
|
||||||
import com.owncloud.android.datamodel.OCFile;
|
import com.owncloud.android.datamodel.OCFile;
|
||||||
import com.owncloud.android.lib.common.network.OnDatatransferProgressListener;
|
import com.owncloud.android.lib.common.network.OnDatatransferProgressListener;
|
||||||
import com.owncloud.android.lib.common.OwnCloudClient;
|
import com.owncloud.android.lib.common.OwnCloudClient;
|
||||||
|
@ -52,6 +51,7 @@ public class DownloadFileOperation extends RemoteOperation {
|
||||||
private OCFile mFile;
|
private OCFile mFile;
|
||||||
private Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<OnDatatransferProgressListener>();
|
private Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<OnDatatransferProgressListener>();
|
||||||
private long mModificationTimestamp = 0;
|
private long mModificationTimestamp = 0;
|
||||||
|
private String mEtag = "";
|
||||||
private final AtomicBoolean mCancellationRequested = new AtomicBoolean(false);
|
private final AtomicBoolean mCancellationRequested = new AtomicBoolean(false);
|
||||||
|
|
||||||
private DownloadRemoteFileOperation mDownloadOperation;
|
private DownloadRemoteFileOperation mDownloadOperation;
|
||||||
|
@ -127,11 +127,15 @@ public class DownloadFileOperation extends RemoteOperation {
|
||||||
mFile.getModificationTimestamp();
|
mFile.getModificationTimestamp();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getEtag() {
|
||||||
|
return mEtag;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected RemoteOperationResult run(OwnCloudClient client) {
|
protected RemoteOperationResult run(OwnCloudClient client) {
|
||||||
RemoteOperationResult result = null;
|
RemoteOperationResult result;
|
||||||
File newFile = null;
|
File newFile;
|
||||||
boolean moved = true;
|
boolean moved;
|
||||||
|
|
||||||
/// download will be performed to a temporal file, then moved to the final location
|
/// download will be performed to a temporal file, then moved to the final location
|
||||||
File tmpFile = new File(getTmpPath());
|
File tmpFile = new File(getTmpPath());
|
||||||
|
@ -154,6 +158,7 @@ public class DownloadFileOperation extends RemoteOperation {
|
||||||
|
|
||||||
if (result.isSuccess()) {
|
if (result.isSuccess()) {
|
||||||
mModificationTimestamp = mDownloadOperation.getModificationTimestamp();
|
mModificationTimestamp = mDownloadOperation.getModificationTimestamp();
|
||||||
|
mEtag = mDownloadOperation.getEtag();
|
||||||
newFile = new File(getSavePath());
|
newFile = new File(getSavePath());
|
||||||
newFile.getParentFile().mkdirs();
|
newFile.getParentFile().mkdirs();
|
||||||
moved = tmpFile.renameTo(newFile);
|
moved = tmpFile.renameTo(newFile);
|
||||||
|
|
|
@ -20,26 +20,17 @@
|
||||||
|
|
||||||
package com.owncloud.android.operations;
|
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.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
|
|
||||||
import org.apache.http.HttpStatus;
|
|
||||||
import android.accounts.Account;
|
import android.accounts.Account;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.util.Log;
|
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.FileDataStorageManager;
|
||||||
import com.owncloud.android.datamodel.OCFile;
|
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.operations.RemoteOperationResult.ResultCode;
|
||||||
import com.owncloud.android.lib.common.utils.Log_OC;
|
import com.owncloud.android.lib.common.utils.Log_OC;
|
||||||
import com.owncloud.android.lib.resources.shares.GetRemoteSharesForFileOperation;
|
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.ReadRemoteFileOperation;
|
||||||
import com.owncloud.android.lib.resources.files.ReadRemoteFolderOperation;
|
import com.owncloud.android.lib.resources.files.ReadRemoteFolderOperation;
|
||||||
import com.owncloud.android.lib.resources.files.RemoteFile;
|
import com.owncloud.android.lib.resources.files.RemoteFile;
|
||||||
|
@ -120,6 +110,9 @@ public class RefreshFolderOperation extends RemoteOperation {
|
||||||
/** 'True' means that Etag will be ignored */
|
/** 'True' means that Etag will be ignored */
|
||||||
private boolean mIgnoreETag;
|
private boolean mIgnoreETag;
|
||||||
|
|
||||||
|
private List<SynchronizeFileOperation> mFilesToSyncContents;
|
||||||
|
// this will be used for every file when 'folder synchronization' replaces 'folder download'
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance of {@link RefreshFolderOperation}.
|
* Creates a new instance of {@link RefreshFolderOperation}.
|
||||||
|
@ -154,6 +147,7 @@ public class RefreshFolderOperation extends RemoteOperation {
|
||||||
mForgottenLocalFiles = new HashMap<String, String>();
|
mForgottenLocalFiles = new HashMap<String, String>();
|
||||||
mRemoteFolderChanged = false;
|
mRemoteFolderChanged = false;
|
||||||
mIgnoreETag = ignoreETag;
|
mIgnoreETag = ignoreETag;
|
||||||
|
mFilesToSyncContents = new Vector<SynchronizeFileOperation>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -191,7 +185,7 @@ public class RefreshFolderOperation extends RemoteOperation {
|
||||||
mConflictsFound = 0;
|
mConflictsFound = 0;
|
||||||
mForgottenLocalFiles.clear();
|
mForgottenLocalFiles.clear();
|
||||||
|
|
||||||
if (FileUtils.PATH_SEPARATOR.equals(mLocalFolder.getRemotePath()) && !mSyncFullAccount) {
|
if (OCFile.ROOT_PATH.equals(mLocalFolder.getRemotePath()) && !mSyncFullAccount) {
|
||||||
updateOCVersion(client);
|
updateOCVersion(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,9 +195,14 @@ public class RefreshFolderOperation extends RemoteOperation {
|
||||||
if (mRemoteFolderChanged) {
|
if (mRemoteFolderChanged) {
|
||||||
result = fetchAndSyncRemoteFolder(client);
|
result = fetchAndSyncRemoteFolder(client);
|
||||||
} else {
|
} else {
|
||||||
// TODO Enable when "On Device" is recovered ?
|
fetchFavoritesToSyncFromLocalData();
|
||||||
mChildren = mStorageManager.getFolderContent(mLocalFolder/*, false*/);
|
mChildren = mStorageManager.getFolderContent(mLocalFolder/*, false*/);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (result.isSuccess()) {
|
||||||
|
// request for the synchronization of KEPT-IN-SYNC file contents
|
||||||
|
startContentSynchronizations(mFilesToSyncContents, client);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mSyncFullAccount) {
|
if (!mSyncFullAccount) {
|
||||||
|
@ -239,9 +238,8 @@ public class RefreshFolderOperation extends RemoteOperation {
|
||||||
private RemoteOperationResult checkForChanges(OwnCloudClient client) {
|
private RemoteOperationResult checkForChanges(OwnCloudClient client) {
|
||||||
mRemoteFolderChanged = true;
|
mRemoteFolderChanged = true;
|
||||||
RemoteOperationResult result = null;
|
RemoteOperationResult result = null;
|
||||||
String remotePath = null;
|
String remotePath = mLocalFolder.getRemotePath();
|
||||||
|
|
||||||
remotePath = mLocalFolder.getRemotePath();
|
|
||||||
Log_OC.d(TAG, "Checking changes in " + mAccount.name + remotePath);
|
Log_OC.d(TAG, "Checking changes in " + mAccount.name + remotePath);
|
||||||
|
|
||||||
// remote request
|
// remote request
|
||||||
|
@ -337,7 +335,7 @@ public class RefreshFolderOperation extends RemoteOperation {
|
||||||
mLocalFolder = mStorageManager.getFileByPath(mLocalFolder.getRemotePath());
|
mLocalFolder = mStorageManager.getFileByPath(mLocalFolder.getRemotePath());
|
||||||
|
|
||||||
// parse data from remote folder
|
// 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.setParentId(mLocalFolder.getParentId());
|
||||||
remoteFolder.setFileId(mLocalFolder.getFileId());
|
remoteFolder.setFileId(mLocalFolder.getFileId());
|
||||||
|
|
||||||
|
@ -345,7 +343,7 @@ public class RefreshFolderOperation extends RemoteOperation {
|
||||||
+ " changed - starting update of local data ");
|
+ " changed - starting update of local data ");
|
||||||
|
|
||||||
List<OCFile> updatedFiles = new Vector<OCFile>(folderAndFiles.size() - 1);
|
List<OCFile> updatedFiles = new Vector<OCFile>(folderAndFiles.size() - 1);
|
||||||
List<SynchronizeFileOperation> filesToSyncContents = new Vector<SynchronizeFileOperation>();
|
mFilesToSyncContents.clear();
|
||||||
|
|
||||||
// get current data about local contents of the folder to synchronize
|
// get current data about local contents of the folder to synchronize
|
||||||
// TODO Enable when "On Device" is recovered ?
|
// TODO Enable when "On Device" is recovered ?
|
||||||
|
@ -356,54 +354,55 @@ public class RefreshFolderOperation extends RemoteOperation {
|
||||||
}
|
}
|
||||||
|
|
||||||
// loop to update every child
|
// loop to update every child
|
||||||
OCFile remoteFile = null, localFile = null;
|
OCFile remoteFile = null, localFile = null, updatedFile = null;
|
||||||
|
RemoteFile r;
|
||||||
for (int i=1; i<folderAndFiles.size(); i++) {
|
for (int i=1; i<folderAndFiles.size(); i++) {
|
||||||
/// new OCFile instance with the data from the server
|
/// new OCFile instance with the data from the server
|
||||||
remoteFile = fillOCFile((RemoteFile)folderAndFiles.get(i));
|
r = (RemoteFile) folderAndFiles.get(i);
|
||||||
remoteFile.setParentId(mLocalFolder.getFileId());
|
remoteFile = FileStorageUtils.fillOCFile(r);
|
||||||
|
|
||||||
|
/// new OCFile instance to merge fresh data from server with local state
|
||||||
|
updatedFile = FileStorageUtils.fillOCFile(r);
|
||||||
|
updatedFile.setParentId(mLocalFolder.getFileId());
|
||||||
|
|
||||||
/// retrieve local data for the read file
|
/// retrieve local data for the read file
|
||||||
// localFile = mStorageManager.getFileByPath(remoteFile.getRemotePath());
|
// localFile = mStorageManager.getFileByPath(remoteFile.getRemotePath());
|
||||||
localFile = localFilesMap.remove(remoteFile.getRemotePath());
|
localFile = localFilesMap.remove(remoteFile.getRemotePath());
|
||||||
|
|
||||||
/// add to the remoteFile (the new one) data about LOCAL STATE (not existing in server)
|
/// add to updatedFile data about LOCAL STATE (not existing in server)
|
||||||
remoteFile.setLastSyncDateForProperties(mCurrentSyncTime);
|
updatedFile.setLastSyncDateForProperties(mCurrentSyncTime);
|
||||||
if (localFile != null) {
|
if (localFile != null) {
|
||||||
// some properties of local state are kept unmodified
|
updatedFile.setFileId(localFile.getFileId());
|
||||||
remoteFile.setFileId(localFile.getFileId());
|
updatedFile.setFavorite(localFile.isFavorite());
|
||||||
remoteFile.setFavorite(localFile.isFavorite());
|
updatedFile.setLastSyncDateForData(localFile.getLastSyncDateForData());
|
||||||
remoteFile.setLastSyncDateForData(localFile.getLastSyncDateForData());
|
updatedFile.setModificationTimestampAtLastSyncForData(
|
||||||
remoteFile.setModificationTimestampAtLastSyncForData(
|
|
||||||
localFile.getModificationTimestampAtLastSyncForData()
|
localFile.getModificationTimestampAtLastSyncForData()
|
||||||
);
|
);
|
||||||
remoteFile.setStoragePath(localFile.getStoragePath());
|
updatedFile.setStoragePath(localFile.getStoragePath());
|
||||||
// eTag will not be updated unless contents are synchronized
|
// eTag will not be updated unless file CONTENTS are synchronized
|
||||||
// (Synchronize[File|Folder]Operation with remoteFile as parameter)
|
updatedFile.setEtag(localFile.getEtag());
|
||||||
remoteFile.setEtag(localFile.getEtag());
|
if (updatedFile.isFolder()) {
|
||||||
if (remoteFile.isFolder()) {
|
updatedFile.setFileLength(localFile.getFileLength());
|
||||||
remoteFile.setFileLength(localFile.getFileLength());
|
|
||||||
// TODO move operations about size of folders to FileContentProvider
|
// TODO move operations about size of folders to FileContentProvider
|
||||||
} else if (mRemoteFolderChanged && remoteFile.isImage() &&
|
} else if (mRemoteFolderChanged && remoteFile.isImage() &&
|
||||||
remoteFile.getModificationTimestamp() !=
|
remoteFile.getModificationTimestamp() !=
|
||||||
localFile.getModificationTimestamp()) {
|
localFile.getModificationTimestamp()) {
|
||||||
remoteFile.setNeedsUpdateThumbnail(true);
|
updatedFile.setNeedsUpdateThumbnail(true);
|
||||||
Log.d(TAG, "Image " + remoteFile.getFileName() + " updated on the server");
|
Log.d(TAG, "Image " + remoteFile.getFileName() + " updated on the server");
|
||||||
}
|
}
|
||||||
remoteFile.setPublicLink(localFile.getPublicLink());
|
updatedFile.setPublicLink(localFile.getPublicLink());
|
||||||
remoteFile.setShareByLink(localFile.isShareByLink());
|
updatedFile.setShareByLink(localFile.isShareByLink());
|
||||||
|
updatedFile.setEtagInConflict(localFile.getEtagInConflict());
|
||||||
} else {
|
} else {
|
||||||
// remote eTag will not be updated unless contents are synchronized
|
// remote eTag will not be updated unless file CONTENTS are synchronized
|
||||||
// (Synchronize[File|Folder]Operation with remoteFile as parameter)
|
updatedFile.setEtag("");
|
||||||
remoteFile.setEtag("");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// check and fix, if needed, local storage path
|
/// check and fix, if needed, local storage path
|
||||||
checkAndFixForeignStoragePath(remoteFile); // policy - local files are COPIED
|
FileStorageUtils.searchForLocalFileInDefaultPath(updatedFile, mAccount);
|
||||||
// into the ownCloud local folder;
|
|
||||||
searchForLocalFileInDefaultPath(remoteFile); // legacy
|
|
||||||
|
|
||||||
/// prepare content synchronization for kept-in-sync files
|
/// prepare content synchronization for kept-in-sync files
|
||||||
if (remoteFile.isFavorite()) {
|
if (updatedFile.isFavorite()) {
|
||||||
SynchronizeFileOperation operation = new SynchronizeFileOperation( localFile,
|
SynchronizeFileOperation operation = new SynchronizeFileOperation( localFile,
|
||||||
remoteFile,
|
remoteFile,
|
||||||
mAccount,
|
mAccount,
|
||||||
|
@ -411,18 +410,15 @@ public class RefreshFolderOperation extends RemoteOperation {
|
||||||
mContext
|
mContext
|
||||||
);
|
);
|
||||||
|
|
||||||
filesToSyncContents.add(operation);
|
mFilesToSyncContents.add(operation);
|
||||||
}
|
}
|
||||||
|
|
||||||
updatedFiles.add(remoteFile);
|
updatedFiles.add(updatedFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
// save updated contents in local database
|
// save updated contents in local database
|
||||||
mStorageManager.saveFolder(remoteFolder, updatedFiles, localFilesMap.values());
|
mStorageManager.saveFolder(remoteFolder, updatedFiles, localFilesMap.values());
|
||||||
|
|
||||||
// request for the synchronization of file contents AFTER saving current remote properties
|
|
||||||
startContentSynchronizations(filesToSyncContents, client);
|
|
||||||
|
|
||||||
mChildren = updatedFiles;
|
mChildren = updatedFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -460,97 +456,6 @@ public class RefreshFolderOperation extends RemoteOperation {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean isMultiStatus(int status) {
|
|
||||||
return (status == HttpStatus.SC_MULTI_STATUS);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
private OCFile fillOCFile(RemoteFile remote) {
|
|
||||||
OCFile file = new OCFile(remote.getRemotePath());
|
|
||||||
file.setCreationTimestamp(remote.getCreationTimestamp());
|
|
||||||
file.setFileLength(remote.getLength());
|
|
||||||
file.setMimetype(remote.getMimeType());
|
|
||||||
file.setModificationTimestamp(remote.getModifiedTimestamp());
|
|
||||||
file.setEtag(remote.getEtag());
|
|
||||||
file.setPermissions(remote.getPermissions());
|
|
||||||
file.setRemoteId(remote.getRemoteId());
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks the storage path of the OCFile received as parameter.
|
|
||||||
* If it's out of the local ownCloud folder, tries to copy the file inside it.
|
|
||||||
*
|
|
||||||
* If the copy fails, the link to the local file is nullified. The account of forgotten
|
|
||||||
* files is kept in {@link #mForgottenLocalFiles}
|
|
||||||
*)
|
|
||||||
* @param file File to check and fix.
|
|
||||||
*/
|
|
||||||
private void checkAndFixForeignStoragePath(OCFile file) {
|
|
||||||
String storagePath = file.getStoragePath();
|
|
||||||
String expectedPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, file);
|
|
||||||
if (storagePath != null && !storagePath.equals(expectedPath)) {
|
|
||||||
/// fix storagePaths out of the local ownCloud folder
|
|
||||||
File originalFile = new File(storagePath);
|
|
||||||
if (FileStorageUtils.getUsableSpace(mAccount.name) < originalFile.length()) {
|
|
||||||
mForgottenLocalFiles.put(file.getRemotePath(), storagePath);
|
|
||||||
file.setStoragePath(null);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
InputStream in = null;
|
|
||||||
OutputStream out = null;
|
|
||||||
try {
|
|
||||||
File expectedFile = new File(expectedPath);
|
|
||||||
File expectedParent = expectedFile.getParentFile();
|
|
||||||
expectedParent.mkdirs();
|
|
||||||
if (!expectedParent.isDirectory()) {
|
|
||||||
throw new IOException(
|
|
||||||
"Unexpected error: parent directory could not be created"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
expectedFile.createNewFile();
|
|
||||||
if (!expectedFile.isFile()) {
|
|
||||||
throw new IOException("Unexpected error: target file could not be created");
|
|
||||||
}
|
|
||||||
in = new FileInputStream(originalFile);
|
|
||||||
out = new FileOutputStream(expectedFile);
|
|
||||||
byte[] buf = new byte[1024];
|
|
||||||
int len;
|
|
||||||
while ((len = in.read(buf)) > 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) {
|
private RemoteOperationResult refreshSharesForFolder(OwnCloudClient client) {
|
||||||
RemoteOperationResult result = null;
|
RemoteOperationResult result = null;
|
||||||
|
|
||||||
|
@ -572,24 +477,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
|
* Sends a message to any application component interested in the progress
|
||||||
* of the synchronization.
|
* of the synchronization.
|
||||||
|
@ -614,8 +501,20 @@ public class RefreshFolderOperation extends RemoteOperation {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean getRemoteFolderChanged() {
|
private void fetchFavoritesToSyncFromLocalData() {
|
||||||
return mRemoteFolderChanged;
|
List<OCFile> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,6 @@
|
||||||
|
|
||||||
package com.owncloud.android.operations;
|
package com.owncloud.android.operations;
|
||||||
|
|
||||||
import com.owncloud.android.MainApp;
|
|
||||||
import com.owncloud.android.datamodel.OCFile;
|
import com.owncloud.android.datamodel.OCFile;
|
||||||
import com.owncloud.android.files.services.FileDownloader;
|
import com.owncloud.android.files.services.FileDownloader;
|
||||||
import com.owncloud.android.files.services.FileUploader;
|
import com.owncloud.android.files.services.FileUploader;
|
||||||
|
@ -208,15 +207,13 @@ public class SynchronizeFileOperation extends SyncOperation {
|
||||||
|
|
||||||
/// check changes in server and local file
|
/// check changes in server and local file
|
||||||
boolean serverChanged = false;
|
boolean serverChanged = false;
|
||||||
/* time for eTag is coming, but not yet
|
if (mLocalFile.getEtag() == null || mLocalFile.getEtag().length() == 0) {
|
||||||
if (mServerFile.getEtag() != null) {
|
// file uploaded (null) or downloaded ("") before upgrade to version 1.8.0; check the old condition
|
||||||
serverChanged = (!mServerFile.getEtag().equals(mLocalFile.getEtag()));
|
serverChanged = mServerFile.getModificationTimestamp() !=
|
||||||
} else { */
|
mLocalFile.getModificationTimestampAtLastSyncForData();
|
||||||
serverChanged = (
|
} else {
|
||||||
mServerFile.getModificationTimestamp() !=
|
serverChanged = (!mServerFile.getEtag().equals(mLocalFile.getEtag()));
|
||||||
mLocalFile.getModificationTimestampAtLastSyncForData()
|
}
|
||||||
);
|
|
||||||
//}
|
|
||||||
boolean localChanged = (
|
boolean localChanged = (
|
||||||
mLocalFile.getLocalModificationTimestamp() > mLocalFile.getLastSyncDateForData()
|
mLocalFile.getLocalModificationTimestamp() > mLocalFile.getLastSyncDateForData()
|
||||||
);
|
);
|
||||||
|
@ -225,6 +222,7 @@ public class SynchronizeFileOperation extends SyncOperation {
|
||||||
//if (!mLocalFile.getEtag().isEmpty() && localChanged && serverChanged) {
|
//if (!mLocalFile.getEtag().isEmpty() && localChanged && serverChanged) {
|
||||||
if (localChanged && serverChanged) {
|
if (localChanged && serverChanged) {
|
||||||
result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT);
|
result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT);
|
||||||
|
getStorageManager().saveConflict(mLocalFile, mServerFile.getEtag());
|
||||||
|
|
||||||
} else if (localChanged) {
|
} else if (localChanged) {
|
||||||
if (mSyncFileContents && mAllowUploads) {
|
if (mSyncFileContents && mAllowUploads) {
|
||||||
|
@ -254,6 +252,7 @@ public class SynchronizeFileOperation extends SyncOperation {
|
||||||
mServerFile.setLastSyncDateForData(mLocalFile.getLastSyncDateForData());
|
mServerFile.setLastSyncDateForData(mLocalFile.getLastSyncDateForData());
|
||||||
mServerFile.setStoragePath(mLocalFile.getStoragePath());
|
mServerFile.setStoragePath(mLocalFile.getStoragePath());
|
||||||
mServerFile.setParentId(mLocalFile.getParentId());
|
mServerFile.setParentId(mLocalFile.getParentId());
|
||||||
|
mServerFile.setEtag(mLocalFile.getEtag());
|
||||||
getStorageManager().saveFile(mServerFile);
|
getStorageManager().saveFile(mServerFile);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -264,6 +263,10 @@ public class SynchronizeFileOperation extends SyncOperation {
|
||||||
result = new RemoteOperationResult(ResultCode.OK);
|
result = new RemoteOperationResult(ResultCode.OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// safe blanket: sync'ing a not in-conflict file will clean wrong conflict markers in ancestors
|
||||||
|
if (result.getCode() != ResultCode.SYNC_CONFLICT) {
|
||||||
|
getStorageManager().saveConflict(mLocalFile, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,7 +60,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
* Fetches the list and properties of the files contained in the given folder, including their
|
* Fetches the list and properties of the files contained in the given folder, including their
|
||||||
* properties, and updates the local database with them.
|
* properties, and updates the local database with them.
|
||||||
*
|
*
|
||||||
* Does NOT enter in the child folders to synchronize their contents also.
|
* Does NOT enter in the child folders to synchronize their contents also, BUT requests for a new operation instance
|
||||||
|
* doing so.
|
||||||
*/
|
*/
|
||||||
public class SynchronizeFolderOperation extends SyncOperation {
|
public class SynchronizeFolderOperation extends SyncOperation {
|
||||||
|
|
||||||
|
@ -96,10 +97,7 @@ public class SynchronizeFolderOperation extends SyncOperation {
|
||||||
private List<OCFile> mFilesForDirectDownload;
|
private List<OCFile> mFilesForDirectDownload;
|
||||||
// to avoid extra PROPFINDs when there was no change in the folder
|
// to avoid extra PROPFINDs when there was no change in the folder
|
||||||
|
|
||||||
private List<SyncOperation> mFilesToSyncContentsWithoutUpload;
|
private List<SyncOperation> mFilesToSyncContents;
|
||||||
// this will go out when 'folder synchronization' replaces 'folder download'; step by step
|
|
||||||
|
|
||||||
private List<SyncOperation> mFavouriteFilesToSyncContents;
|
|
||||||
// this will be used for every file when 'folder synchronization' replaces 'folder download'
|
// this will be used for every file when 'folder synchronization' replaces 'folder download'
|
||||||
|
|
||||||
private final AtomicBoolean mCancellationRequested;
|
private final AtomicBoolean mCancellationRequested;
|
||||||
|
@ -120,8 +118,7 @@ public class SynchronizeFolderOperation extends SyncOperation {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mRemoteFolderChanged = false;
|
mRemoteFolderChanged = false;
|
||||||
mFilesForDirectDownload = new Vector<OCFile>();
|
mFilesForDirectDownload = new Vector<OCFile>();
|
||||||
mFilesToSyncContentsWithoutUpload = new Vector<SyncOperation>();
|
mFilesToSyncContents = new Vector<SyncOperation>();
|
||||||
mFavouriteFilesToSyncContents = new Vector<SyncOperation>();
|
|
||||||
mCancellationRequested = new AtomicBoolean(false);
|
mCancellationRequested = new AtomicBoolean(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,7 +278,7 @@ public class SynchronizeFolderOperation extends SyncOperation {
|
||||||
FileDataStorageManager storageManager = getStorageManager();
|
FileDataStorageManager storageManager = getStorageManager();
|
||||||
|
|
||||||
// parse data from remote folder
|
// 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.setParentId(mLocalFolder.getParentId());
|
||||||
remoteFolder.setFileId(mLocalFolder.getFileId());
|
remoteFolder.setFileId(mLocalFolder.getFileId());
|
||||||
|
|
||||||
|
@ -290,8 +287,7 @@ public class SynchronizeFolderOperation extends SyncOperation {
|
||||||
|
|
||||||
List<OCFile> updatedFiles = new Vector<OCFile>(folderAndFiles.size() - 1);
|
List<OCFile> updatedFiles = new Vector<OCFile>(folderAndFiles.size() - 1);
|
||||||
mFilesForDirectDownload.clear();
|
mFilesForDirectDownload.clear();
|
||||||
mFilesToSyncContentsWithoutUpload.clear();
|
mFilesToSyncContents.clear();
|
||||||
mFavouriteFilesToSyncContents.clear();
|
|
||||||
|
|
||||||
if (mCancellationRequested.get()) {
|
if (mCancellationRequested.get()) {
|
||||||
throw new OperationCancelledException();
|
throw new OperationCancelledException();
|
||||||
|
@ -306,85 +302,77 @@ public class SynchronizeFolderOperation extends SyncOperation {
|
||||||
}
|
}
|
||||||
|
|
||||||
// loop to synchronize every child
|
// loop to synchronize every child
|
||||||
OCFile remoteFile = null, localFile = null;
|
OCFile remoteFile = null, localFile = null, updatedFile = null;
|
||||||
|
RemoteFile r;
|
||||||
for (int i=1; i<folderAndFiles.size(); i++) {
|
for (int i=1; i<folderAndFiles.size(); i++) {
|
||||||
/// new OCFile instance with the data from the server
|
/// new OCFile instance with the data from the server
|
||||||
remoteFile = fillOCFile((RemoteFile)folderAndFiles.get(i));
|
r = (RemoteFile) folderAndFiles.get(i);
|
||||||
remoteFile.setParentId(mLocalFolder.getFileId());
|
remoteFile = FileStorageUtils.fillOCFile(r);
|
||||||
|
|
||||||
|
/// new OCFile instance to merge fresh data from server with local state
|
||||||
|
updatedFile = FileStorageUtils.fillOCFile(r);
|
||||||
|
updatedFile.setParentId(mLocalFolder.getFileId());
|
||||||
|
|
||||||
/// retrieve local data for the read file
|
/// retrieve local data for the read file
|
||||||
// localFile = mStorageManager.getFileByPath(remoteFile.getRemotePath());
|
// localFile = mStorageManager.getFileByPath(remoteFile.getRemotePath());
|
||||||
localFile = localFilesMap.remove(remoteFile.getRemotePath());
|
localFile = localFilesMap.remove(remoteFile.getRemotePath());
|
||||||
|
|
||||||
/// add to the remoteFile (the new one) data about LOCAL STATE (not existing in server)
|
/// add to updatedFile data about LOCAL STATE (not existing in server)
|
||||||
remoteFile.setLastSyncDateForProperties(mCurrentSyncTime);
|
updatedFile.setLastSyncDateForProperties(mCurrentSyncTime);
|
||||||
if (localFile != null) {
|
if (localFile != null) {
|
||||||
// some properties of local state are kept unmodified
|
updatedFile.setFileId(localFile.getFileId());
|
||||||
remoteFile.setFileId(localFile.getFileId());
|
updatedFile.setFavorite(localFile.isFavorite());
|
||||||
remoteFile.setFavorite(localFile.isFavorite());
|
updatedFile.setLastSyncDateForData(localFile.getLastSyncDateForData());
|
||||||
remoteFile.setLastSyncDateForData(localFile.getLastSyncDateForData());
|
updatedFile.setModificationTimestampAtLastSyncForData(
|
||||||
remoteFile.setModificationTimestampAtLastSyncForData(
|
|
||||||
localFile.getModificationTimestampAtLastSyncForData()
|
localFile.getModificationTimestampAtLastSyncForData()
|
||||||
);
|
);
|
||||||
remoteFile.setStoragePath(localFile.getStoragePath());
|
updatedFile.setStoragePath(localFile.getStoragePath());
|
||||||
// eTag will not be updated unless contents are synchronized
|
// eTag will not be updated unless file CONTENTS are synchronized
|
||||||
// (Synchronize[File|Folder]Operation with remoteFile as parameter)
|
updatedFile.setEtag(localFile.getEtag());
|
||||||
remoteFile.setEtag(localFile.getEtag());
|
if (updatedFile.isFolder()) {
|
||||||
if (remoteFile.isFolder()) {
|
updatedFile.setFileLength(localFile.getFileLength());
|
||||||
remoteFile.setFileLength(localFile.getFileLength());
|
|
||||||
// TODO move operations about size of folders to FileContentProvider
|
// TODO move operations about size of folders to FileContentProvider
|
||||||
} else if (mRemoteFolderChanged && remoteFile.isImage() &&
|
} else if (mRemoteFolderChanged && remoteFile.isImage() &&
|
||||||
remoteFile.getModificationTimestamp() !=
|
remoteFile.getModificationTimestamp() !=
|
||||||
localFile.getModificationTimestamp()) {
|
localFile.getModificationTimestamp()) {
|
||||||
remoteFile.setNeedsUpdateThumbnail(true);
|
updatedFile.setNeedsUpdateThumbnail(true);
|
||||||
Log.d(TAG, "Image " + remoteFile.getFileName() + " updated on the server");
|
Log.d(TAG, "Image " + remoteFile.getFileName() + " updated on the server");
|
||||||
}
|
}
|
||||||
remoteFile.setPublicLink(localFile.getPublicLink());
|
updatedFile.setPublicLink(localFile.getPublicLink());
|
||||||
remoteFile.setShareByLink(localFile.isShareByLink());
|
updatedFile.setShareByLink(localFile.isShareByLink());
|
||||||
|
updatedFile.setEtagInConflict(localFile.getEtagInConflict());
|
||||||
} else {
|
} else {
|
||||||
// remote eTag will not be updated unless contents are synchronized
|
// remote eTag will not be updated unless file CONTENTS are synchronized
|
||||||
// (Synchronize[File|Folder]Operation with remoteFile as parameter)
|
updatedFile.setEtag("");
|
||||||
remoteFile.setEtag("");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// check and fix, if needed, local storage path
|
/// check and fix, if needed, local storage path
|
||||||
searchForLocalFileInDefaultPath(remoteFile);
|
searchForLocalFileInDefaultPath(updatedFile);
|
||||||
|
|
||||||
/// classify file to sync/download contents later
|
/// classify file to sync/download contents later
|
||||||
if (remoteFile.isFolder()) {
|
if (remoteFile.isFolder()) {
|
||||||
/// to download children files recursively
|
/// to download children files recursively
|
||||||
synchronized(mCancellationRequested) {
|
synchronized (mCancellationRequested) {
|
||||||
if (mCancellationRequested.get()) {
|
if (mCancellationRequested.get()) {
|
||||||
throw new OperationCancelledException();
|
throw new OperationCancelledException();
|
||||||
}
|
}
|
||||||
startSyncFolderOperation(remoteFile.getRemotePath());
|
startSyncFolderOperation(remoteFile.getRemotePath());
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (remoteFile.isFavorite()) {
|
|
||||||
/// prepare content synchronization for kept-in-sync files
|
|
||||||
SynchronizeFileOperation operation = new SynchronizeFileOperation(
|
|
||||||
localFile,
|
|
||||||
remoteFile,
|
|
||||||
mAccount,
|
|
||||||
true,
|
|
||||||
mContext
|
|
||||||
);
|
|
||||||
mFavouriteFilesToSyncContents.add(operation);
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
/// prepare limited synchronization for regular files
|
/// prepare content synchronization for files (any file, not just favorites)
|
||||||
SynchronizeFileOperation operation = new SynchronizeFileOperation(
|
SynchronizeFileOperation operation = new SynchronizeFileOperation(
|
||||||
localFile,
|
localFile,
|
||||||
remoteFile,
|
remoteFile,
|
||||||
mAccount,
|
mAccount,
|
||||||
true,
|
true,
|
||||||
false,
|
|
||||||
mContext
|
mContext
|
||||||
);
|
);
|
||||||
mFilesToSyncContentsWithoutUpload.add(operation);
|
mFilesToSyncContents.add(operation);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updatedFiles.add(remoteFile);
|
updatedFiles.add(updatedFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
// save updated contents in local database
|
// save updated contents in local database
|
||||||
|
@ -408,10 +396,23 @@ public class SynchronizeFolderOperation extends SyncOperation {
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
/// prepare limited synchronization for regular files
|
/// synchronization for regular files
|
||||||
if (!child.isDown()) {
|
if (!child.isDown()) {
|
||||||
mFilesForDirectDownload.add(child);
|
mFilesForDirectDownload.add(child);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
/// this should result in direct upload of files that were locally modified
|
||||||
|
SynchronizeFileOperation operation = new SynchronizeFileOperation(
|
||||||
|
child,
|
||||||
|
(child.getEtagInConflict() != null ? child : null),
|
||||||
|
mAccount,
|
||||||
|
true,
|
||||||
|
mContext
|
||||||
|
);
|
||||||
|
mFilesToSyncContents.add(operation);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -419,8 +420,7 @@ public class SynchronizeFolderOperation extends SyncOperation {
|
||||||
|
|
||||||
private void syncContents(OwnCloudClient client) throws OperationCancelledException {
|
private void syncContents(OwnCloudClient client) throws OperationCancelledException {
|
||||||
startDirectDownloads();
|
startDirectDownloads();
|
||||||
startContentSynchronizations(mFilesToSyncContentsWithoutUpload, client);
|
startContentSynchronizations(mFilesToSyncContents, client);
|
||||||
startContentSynchronizations(mFavouriteFilesToSyncContents, client);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -478,26 +478,6 @@ public class SynchronizeFolderOperation extends SyncOperation {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates and populates a new {@link com.owncloud.android.datamodel.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.
|
|
||||||
*/
|
|
||||||
private OCFile fillOCFile(RemoteFile remote) {
|
|
||||||
OCFile file = new OCFile(remote.getRemotePath());
|
|
||||||
file.setCreationTimestamp(remote.getCreationTimestamp());
|
|
||||||
file.setFileLength(remote.getLength());
|
|
||||||
file.setMimetype(remote.getMimeType());
|
|
||||||
file.setModificationTimestamp(remote.getModifiedTimestamp());
|
|
||||||
file.setEtag(remote.getEtag());
|
|
||||||
file.setPermissions(remote.getPermissions());
|
|
||||||
file.setRemoteId(remote.getRemoteId());
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scans the default location for saving local copies of files searching for
|
* Scans the default location for saving local copies of files searching for
|
||||||
* a 'lost' file with the same full name as the {@link com.owncloud.android.datamodel.OCFile}
|
* a 'lost' file with the same full name as the {@link com.owncloud.android.datamodel.OCFile}
|
||||||
|
|
|
@ -31,7 +31,7 @@ import java.util.Iterator;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import org.apache.commons.httpclient.methods.PutMethod;
|
import org.apache.commons.httpclient.HttpStatus;
|
||||||
import org.apache.commons.httpclient.methods.RequestEntity;
|
import org.apache.commons.httpclient.methods.RequestEntity;
|
||||||
|
|
||||||
import android.accounts.Account;
|
import android.accounts.Account;
|
||||||
|
@ -75,7 +75,6 @@ public class UploadFileOperation extends RemoteOperation {
|
||||||
private boolean mWasRenamed = false;
|
private boolean mWasRenamed = false;
|
||||||
private String mOriginalFileName = null;
|
private String mOriginalFileName = null;
|
||||||
private String mOriginalStoragePath = null;
|
private String mOriginalStoragePath = null;
|
||||||
PutMethod mPutMethod = null;
|
|
||||||
private Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<OnDatatransferProgressListener>();
|
private Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<OnDatatransferProgressListener>();
|
||||||
private AtomicBoolean mCancellationRequested = new AtomicBoolean(false);
|
private AtomicBoolean mCancellationRequested = new AtomicBoolean(false);
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
|
@ -312,61 +311,61 @@ public class UploadFileOperation extends RemoteOperation {
|
||||||
(new File(mFile.getStoragePath())).length() >
|
(new File(mFile.getStoragePath())).length() >
|
||||||
ChunkedUploadRemoteFileOperation.CHUNK_SIZE ) {
|
ChunkedUploadRemoteFileOperation.CHUNK_SIZE ) {
|
||||||
mUploadOperation = new ChunkedUploadRemoteFileOperation(mFile.getStoragePath(),
|
mUploadOperation = new ChunkedUploadRemoteFileOperation(mFile.getStoragePath(),
|
||||||
mFile.getRemotePath(), mFile.getMimetype());
|
mFile.getRemotePath(), mFile.getMimetype(), mFile.getEtagInConflict());
|
||||||
} else {
|
} else {
|
||||||
mUploadOperation = new UploadRemoteFileOperation(mFile.getStoragePath(),
|
mUploadOperation = new UploadRemoteFileOperation(mFile.getStoragePath(),
|
||||||
mFile.getRemotePath(), mFile.getMimetype());
|
mFile.getRemotePath(), mFile.getMimetype(), mFile.getEtagInConflict());
|
||||||
}
|
}
|
||||||
Iterator <OnDatatransferProgressListener> listener = mDataTransferListeners.iterator();
|
Iterator <OnDatatransferProgressListener> listener = mDataTransferListeners.iterator();
|
||||||
while (listener.hasNext()) {
|
while (listener.hasNext()) {
|
||||||
mUploadOperation.addDatatransferProgressListener(listener.next());
|
mUploadOperation.addDatatransferProgressListener(listener.next());
|
||||||
}
|
}
|
||||||
if (!mCancellationRequested.get()) {
|
if (mCancellationRequested.get()) {
|
||||||
result = mUploadOperation.execute(client);
|
throw new OperationCancelledException();
|
||||||
|
}
|
||||||
|
|
||||||
/// move local temporal file or original file to its corresponding
|
result = mUploadOperation.execute(client);
|
||||||
// location in the ownCloud local folder
|
|
||||||
if (result.isSuccess()) {
|
|
||||||
if (mLocalBehaviour == FileUploader.LOCAL_BEHAVIOUR_FORGET) {
|
|
||||||
mFile.setStoragePath(null);
|
|
||||||
|
|
||||||
} else {
|
/// move local temporal file or original file to its corresponding
|
||||||
mFile.setStoragePath(expectedPath);
|
// location in the ownCloud local folder
|
||||||
File fileToMove = null;
|
if (result.isSuccess()) {
|
||||||
if (temporalFile != null) { // FileUploader.LOCAL_BEHAVIOUR_COPY
|
if (mLocalBehaviour == FileUploader.LOCAL_BEHAVIOUR_FORGET) {
|
||||||
// ; see where temporalFile was
|
mFile.setStoragePath(null);
|
||||||
// set
|
|
||||||
fileToMove = temporalFile;
|
} else {
|
||||||
} else { // FileUploader.LOCAL_BEHAVIOUR_MOVE
|
mFile.setStoragePath(expectedPath);
|
||||||
fileToMove = originalFile;
|
File fileToMove = null;
|
||||||
}
|
if (temporalFile != null) { // FileUploader.LOCAL_BEHAVIOUR_COPY
|
||||||
if (!expectedFile.equals(fileToMove)) {
|
// ; see where temporalFile was
|
||||||
File expectedFolder = expectedFile.getParentFile();
|
// set
|
||||||
expectedFolder.mkdirs();
|
fileToMove = temporalFile;
|
||||||
if (!expectedFolder.isDirectory() || !fileToMove.renameTo(expectedFile)) {
|
} else { // FileUploader.LOCAL_BEHAVIOUR_MOVE
|
||||||
mFile.setStoragePath(null); // forget the local file
|
fileToMove = originalFile;
|
||||||
// by now, treat this as a success; the file was
|
}
|
||||||
// uploaded; the user won't like that the local file
|
if (!expectedFile.equals(fileToMove)) {
|
||||||
// is not linked, but this should be a very rare
|
File expectedFolder = expectedFile.getParentFile();
|
||||||
// fail;
|
expectedFolder.mkdirs();
|
||||||
// the best option could be show a warning message
|
if (!expectedFolder.isDirectory() || !fileToMove.renameTo(expectedFile)) {
|
||||||
// (but not a fail)
|
mFile.setStoragePath(null); // forget the local file
|
||||||
// result = new
|
// by now, treat this as a success; the file was
|
||||||
// RemoteOperationResult(ResultCode.LOCAL_STORAGE_NOT_MOVED);
|
// uploaded; the user won't like that the local file
|
||||||
// return result;
|
// is not linked, but this should be a very rare
|
||||||
}
|
// fail;
|
||||||
|
// the best option could be show a warning message
|
||||||
|
// (but not a fail)
|
||||||
|
// result = new
|
||||||
|
// RemoteOperationResult(ResultCode.LOCAL_STORAGE_NOT_MOVED);
|
||||||
|
// return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else if (result.getHttpCode() == HttpStatus.SC_PRECONDITION_FAILED ) {
|
||||||
|
result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// TODO something cleaner with cancellations
|
result = new RemoteOperationResult(e);
|
||||||
if (mCancellationRequested.get()) {
|
|
||||||
result = new RemoteOperationResult(new OperationCancelledException());
|
|
||||||
} else {
|
|
||||||
result = new RemoteOperationResult(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
if (temporalFile != null && !originalFile.equals(temporalFile)) {
|
if (temporalFile != null && !originalFile.equals(temporalFile)) {
|
||||||
|
@ -406,7 +405,7 @@ public class UploadFileOperation extends RemoteOperation {
|
||||||
newFile.setModificationTimestamp(mFile.getModificationTimestamp());
|
newFile.setModificationTimestamp(mFile.getModificationTimestamp());
|
||||||
newFile.setModificationTimestampAtLastSyncForData(
|
newFile.setModificationTimestampAtLastSyncForData(
|
||||||
mFile.getModificationTimestampAtLastSyncForData());
|
mFile.getModificationTimestampAtLastSyncForData());
|
||||||
// newFile.setEtag(mFile.getEtag())
|
newFile.setEtag(mFile.getEtag());
|
||||||
newFile.setFavorite(mFile.isFavorite());
|
newFile.setFavorite(mFile.isFavorite());
|
||||||
newFile.setLastSyncDateForProperties(mFile.getLastSyncDateForProperties());
|
newFile.setLastSyncDateForProperties(mFile.getLastSyncDateForProperties());
|
||||||
newFile.setLastSyncDateForData(mFile.getLastSyncDateForData());
|
newFile.setLastSyncDateForData(mFile.getLastSyncDateForData());
|
||||||
|
|
|
@ -23,7 +23,6 @@
|
||||||
package com.owncloud.android.providers;
|
package com.owncloud.android.providers;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.security.Provider;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
@ -107,6 +106,8 @@ public class FileContentProvider extends ContentProvider {
|
||||||
ProviderTableMeta.FILE_UPDATE_THUMBNAIL);
|
ProviderTableMeta.FILE_UPDATE_THUMBNAIL);
|
||||||
mFileProjectionMap.put(ProviderTableMeta.FILE_IS_DOWNLOADING,
|
mFileProjectionMap.put(ProviderTableMeta.FILE_IS_DOWNLOADING,
|
||||||
ProviderTableMeta.FILE_IS_DOWNLOADING);
|
ProviderTableMeta.FILE_IS_DOWNLOADING);
|
||||||
|
mFileProjectionMap.put(ProviderTableMeta.FILE_ETAG_IN_CONFLICT,
|
||||||
|
ProviderTableMeta.FILE_ETAG_IN_CONFLICT);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final int SINGLE_FILE = 1;
|
private static final int SINGLE_FILE = 1;
|
||||||
|
@ -527,59 +528,6 @@ public class FileContentProvider extends ContentProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
private int updateFolderSize(SQLiteDatabase db, String folderId) {
|
|
||||||
int count = 0;
|
|
||||||
String [] whereArgs = new String[] { folderId };
|
|
||||||
|
|
||||||
// read current size saved for the folder
|
|
||||||
long folderSize = 0;
|
|
||||||
long folderParentId = -1;
|
|
||||||
Uri selectFolderUri = Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_FILE, folderId);
|
|
||||||
String[] folderProjection = new String[] { ProviderTableMeta.FILE_CONTENT_LENGTH, ProviderTableMeta.FILE_PARENT};
|
|
||||||
String folderWhere = ProviderTableMeta._ID + "=?";
|
|
||||||
Cursor folderCursor = query(db, selectFolderUri, folderProjection, folderWhere, whereArgs, null);
|
|
||||||
if (folderCursor != null && folderCursor.moveToFirst()) {
|
|
||||||
folderSize = folderCursor.getLong(folderCursor.getColumnIndex(ProviderTableMeta.FILE_CONTENT_LENGTH));;
|
|
||||||
folderParentId = folderCursor.getLong(folderCursor.getColumnIndex(ProviderTableMeta.FILE_PARENT));;
|
|
||||||
}
|
|
||||||
folderCursor.close();
|
|
||||||
|
|
||||||
// read and sum sizes of children
|
|
||||||
long childrenSize = 0;
|
|
||||||
Uri selectChildrenUri = Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_DIR, folderId);
|
|
||||||
String[] childrenProjection = new String[] { ProviderTableMeta.FILE_CONTENT_LENGTH, ProviderTableMeta.FILE_PARENT};
|
|
||||||
String childrenWhere = ProviderTableMeta.FILE_PARENT + "=?";
|
|
||||||
Cursor childrenCursor = query(db, selectChildrenUri, childrenProjection, childrenWhere, whereArgs, null);
|
|
||||||
if (childrenCursor != null && childrenCursor.moveToFirst()) {
|
|
||||||
while (!childrenCursor.isAfterLast()) {
|
|
||||||
childrenSize += childrenCursor.getLong(childrenCursor.getColumnIndex(ProviderTableMeta.FILE_CONTENT_LENGTH));
|
|
||||||
childrenCursor.moveToNext();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
childrenCursor.close();
|
|
||||||
|
|
||||||
// update if needed
|
|
||||||
if (folderSize != childrenSize) {
|
|
||||||
Log_OC.d("FileContentProvider", "Updating " + folderSize + " to " + childrenSize);
|
|
||||||
ContentValues cv = new ContentValues();
|
|
||||||
cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, childrenSize);
|
|
||||||
count = db.update(ProviderTableMeta.FILE_TABLE_NAME, cv, folderWhere, whereArgs);
|
|
||||||
|
|
||||||
// propagate update until root
|
|
||||||
if (folderParentId > FileDataStorageManager.ROOT_PARENT_ID) {
|
|
||||||
Log_OC.d("FileContentProvider", "Propagating update to " + folderParentId);
|
|
||||||
updateFolderSize(db, String.valueOf(folderParentId));
|
|
||||||
} else {
|
|
||||||
Log_OC.d("FileContentProvider", "NOT propagating to " + folderParentId);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Log_OC.d("FileContentProvider", "NOT updating, sizes are " + folderSize + " and " + childrenSize);
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ContentProviderResult[] applyBatch (ArrayList<ContentProviderOperation> operations)
|
public ContentProviderResult[] applyBatch (ArrayList<ContentProviderOperation> operations)
|
||||||
throws OperationApplicationException {
|
throws OperationApplicationException {
|
||||||
|
@ -636,7 +584,8 @@ public class FileContentProvider extends ContentProvider {
|
||||||
+ ProviderTableMeta.FILE_PERMISSIONS + " TEXT null,"
|
+ ProviderTableMeta.FILE_PERMISSIONS + " TEXT null,"
|
||||||
+ ProviderTableMeta.FILE_REMOTE_ID + " TEXT null,"
|
+ ProviderTableMeta.FILE_REMOTE_ID + " TEXT null,"
|
||||||
+ ProviderTableMeta.FILE_UPDATE_THUMBNAIL + " INTEGER," //boolean
|
+ ProviderTableMeta.FILE_UPDATE_THUMBNAIL + " INTEGER," //boolean
|
||||||
+ ProviderTableMeta.FILE_IS_DOWNLOADING + " INTEGER);" //boolean
|
+ ProviderTableMeta.FILE_IS_DOWNLOADING + " INTEGER," //boolean
|
||||||
|
+ ProviderTableMeta.FILE_ETAG_IN_CONFLICT + " TEXT);"
|
||||||
);
|
);
|
||||||
|
|
||||||
// Create table ocshares
|
// Create table ocshares
|
||||||
|
@ -836,6 +785,24 @@ public class FileContentProvider extends ContentProvider {
|
||||||
if (!upgraded)
|
if (!upgraded)
|
||||||
Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion +
|
Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion +
|
||||||
", newVersion == " + newVersion);
|
", newVersion == " + newVersion);
|
||||||
|
|
||||||
|
if (oldVersion < 11 && newVersion >= 11) {
|
||||||
|
Log_OC.i("SQL", "Entering in the #11 ADD in onUpgrade");
|
||||||
|
db.beginTransaction();
|
||||||
|
try {
|
||||||
|
db .execSQL("ALTER TABLE " + ProviderTableMeta.FILE_TABLE_NAME +
|
||||||
|
" ADD COLUMN " + ProviderTableMeta.FILE_ETAG_IN_CONFLICT + " TEXT " +
|
||||||
|
" DEFAULT NULL");
|
||||||
|
upgraded = true;
|
||||||
|
db.setTransactionSuccessful();
|
||||||
|
} finally {
|
||||||
|
db.endTransaction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!upgraded)
|
||||||
|
Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion +
|
||||||
|
", newVersion == " + newVersion);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -100,7 +100,7 @@ public class OperationsService extends Service {
|
||||||
public static final String ACTION_REMOVE = "REMOVE";
|
public static final String ACTION_REMOVE = "REMOVE";
|
||||||
public static final String ACTION_CREATE_FOLDER = "CREATE_FOLDER";
|
public static final String ACTION_CREATE_FOLDER = "CREATE_FOLDER";
|
||||||
public static final String ACTION_SYNC_FILE = "SYNC_FILE";
|
public static final String ACTION_SYNC_FILE = "SYNC_FILE";
|
||||||
public static final String ACTION_SYNC_FOLDER = "SYNC_FOLDER";//for the moment, just to download
|
public static final String ACTION_SYNC_FOLDER = "SYNC_FOLDER";
|
||||||
public static final String ACTION_MOVE_FILE = "MOVE_FILE";
|
public static final String ACTION_MOVE_FILE = "MOVE_FILE";
|
||||||
public static final String ACTION_COPY_FILE = "COPY_FILE";
|
public static final String ACTION_COPY_FILE = "COPY_FILE";
|
||||||
|
|
||||||
|
@ -234,7 +234,6 @@ public class OperationsService extends Service {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public IBinder onBind(Intent intent) {
|
public IBinder onBind(Intent intent) {
|
||||||
//Log_OC.wtf(TAG, "onBind" );
|
|
||||||
return mOperationsBinder;
|
return mOperationsBinder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -615,7 +614,7 @@ public class OperationsService extends Service {
|
||||||
);
|
);
|
||||||
|
|
||||||
} else if (action.equals(ACTION_SYNC_FOLDER)) {
|
} else if (action.equals(ACTION_SYNC_FOLDER)) {
|
||||||
// Sync file
|
// Sync folder (all its descendant files are sync'ed)
|
||||||
String remotePath = operationIntent.getStringExtra(EXTRA_REMOTE_PATH);
|
String remotePath = operationIntent.getStringExtra(EXTRA_REMOTE_PATH);
|
||||||
operation = new SynchronizeFolderOperation(
|
operation = new SynchronizeFolderOperation(
|
||||||
this, // TODO remove this dependency from construction time
|
this, // TODO remove this dependency from construction time
|
||||||
|
@ -726,7 +725,6 @@ public class OperationsService extends Service {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (count == 0) {
|
if (count == 0) {
|
||||||
//mOperationResults.put(operation.hashCode(), result);
|
|
||||||
Pair<RemoteOperation, RemoteOperationResult> undispatched =
|
Pair<RemoteOperation, RemoteOperationResult> undispatched =
|
||||||
new Pair<RemoteOperation, RemoteOperationResult>(operation, result);
|
new Pair<RemoteOperation, RemoteOperationResult>(operation, result);
|
||||||
mUndispatchedFinishedOperations.put(((Runnable) operation).hashCode(), undispatched);
|
mUndispatchedFinishedOperations.put(((Runnable) operation).hashCode(), undispatched);
|
||||||
|
|
|
@ -30,7 +30,6 @@ import java.util.Map;
|
||||||
|
|
||||||
import org.apache.jackrabbit.webdav.DavException;
|
import org.apache.jackrabbit.webdav.DavException;
|
||||||
|
|
||||||
import com.owncloud.android.MainApp;
|
|
||||||
import com.owncloud.android.R;
|
import com.owncloud.android.R;
|
||||||
import com.owncloud.android.authentication.AuthenticatorActivity;
|
import com.owncloud.android.authentication.AuthenticatorActivity;
|
||||||
import com.owncloud.android.datamodel.FileDataStorageManager;
|
import com.owncloud.android.datamodel.FileDataStorageManager;
|
||||||
|
@ -59,7 +58,7 @@ import android.support.v4.app.NotificationCompat;
|
||||||
* Implementation of {@link AbstractThreadedSyncAdapter} responsible for synchronizing
|
* Implementation of {@link AbstractThreadedSyncAdapter} responsible for synchronizing
|
||||||
* ownCloud files.
|
* ownCloud files.
|
||||||
*
|
*
|
||||||
* Performs a full synchronization of the account recieved in {@link #onPerformSync(Account, Bundle,
|
* Performs a full synchronization of the account received in {@link #onPerformSync(Account, Bundle,
|
||||||
* String, ContentProviderClient, SyncResult)}.
|
* String, ContentProviderClient, SyncResult)}.
|
||||||
*/
|
*/
|
||||||
public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
|
public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
|
||||||
|
@ -77,8 +76,6 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
|
||||||
".EVENT_FULL_SYNC_END";
|
".EVENT_FULL_SYNC_END";
|
||||||
public static final String EVENT_FULL_SYNC_FOLDER_CONTENTS_SYNCED =
|
public static final String EVENT_FULL_SYNC_FOLDER_CONTENTS_SYNCED =
|
||||||
FileSyncAdapter.class.getName() + ".EVENT_FULL_SYNC_FOLDER_CONTENTS_SYNCED";
|
FileSyncAdapter.class.getName() + ".EVENT_FULL_SYNC_FOLDER_CONTENTS_SYNCED";
|
||||||
//public static final String EVENT_FULL_SYNC_FOLDER_SIZE_SYNCED =
|
|
||||||
// FileSyncAdapter.class.getName() + ".EVENT_FULL_SYNC_FOLDER_SIZE_SYNCED";
|
|
||||||
|
|
||||||
public static final String EXTRA_ACCOUNT_NAME = FileSyncAdapter.class.getName() +
|
public static final String EXTRA_ACCOUNT_NAME = FileSyncAdapter.class.getName() +
|
||||||
".EXTRA_ACCOUNT_NAME";
|
".EXTRA_ACCOUNT_NAME";
|
||||||
|
@ -268,16 +265,6 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
|
||||||
if (mFailedResultsCounter > MAX_FAILED_RESULTS || isFinisher(mLastFailedResult))
|
if (mFailedResultsCounter > MAX_FAILED_RESULTS || isFinisher(mLastFailedResult))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/*
|
|
||||||
OCFile folder,
|
|
||||||
long currentSyncTime,
|
|
||||||
boolean updateFolderProperties,
|
|
||||||
boolean syncFullAccount,
|
|
||||||
DataStorageManager dataStorageManager,
|
|
||||||
Account account,
|
|
||||||
Context context ) {
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
// folder synchronization
|
// folder synchronization
|
||||||
RefreshFolderOperation synchFolderOp = new RefreshFolderOperation( folder,
|
RefreshFolderOperation synchFolderOp = new RefreshFolderOperation( folder,
|
||||||
mCurrentSyncTime,
|
mCurrentSyncTime,
|
||||||
|
@ -308,7 +295,7 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
|
||||||
// synchronize children folders
|
// synchronize children folders
|
||||||
List<OCFile> children = synchFolderOp.getChildren();
|
List<OCFile> children = synchFolderOp.getChildren();
|
||||||
// beware of the 'hidden' recursion here!
|
// beware of the 'hidden' recursion here!
|
||||||
fetchChildren(folder, children, synchFolderOp.getRemoteFolderChanged());
|
syncChildren(children);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -352,24 +339,18 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
|
||||||
/**
|
/**
|
||||||
* Triggers the synchronization of any folder contained in the list of received files.
|
* 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.
|
* @param files Files to recursively synchronize.
|
||||||
*/
|
*/
|
||||||
private void fetchChildren(OCFile parent, List<OCFile> files, boolean parentEtagChanged) {
|
private void syncChildren(List<OCFile> files) {
|
||||||
int i;
|
int i;
|
||||||
OCFile newFile = null;
|
OCFile newFile;
|
||||||
//String etag = null;
|
|
||||||
//boolean syncDown = false;
|
|
||||||
for (i=0; i < files.size() && !mCancellation; i++) {
|
for (i=0; i < files.size() && !mCancellation; i++) {
|
||||||
newFile = files.get(i);
|
newFile = files.get(i);
|
||||||
if (newFile.isFolder()) {
|
if (newFile.isFolder()) {
|
||||||
/*
|
synchronizeFolder(newFile);
|
||||||
etag = newFile.getEtag();
|
|
||||||
syncDown = (parentEtagChanged || etag == null || etag.length() == 0);
|
|
||||||
if(syncDown) { */
|
|
||||||
synchronizeFolder(newFile);
|
|
||||||
//sendLocalBroadcast(EVENT_FULL_SYNC_FOLDER_SIZE_SYNCED, parent.getRemotePath(),
|
|
||||||
// null);
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1048,7 +1048,11 @@ public class FileDisplayActivity extends HookActivity
|
||||||
(uploadedRemotePath.startsWith(currentDir.getRemotePath()));
|
(uploadedRemotePath.startsWith(currentDir.getRemotePath()));
|
||||||
|
|
||||||
if (sameAccount && isDescendant) {
|
if (sameAccount && isDescendant) {
|
||||||
refreshListOfFilesFragment();
|
String linkedToRemotePath =
|
||||||
|
intent.getStringExtra(FileDownloader.EXTRA_LINKED_TO_PATH);
|
||||||
|
if (linkedToRemotePath == null || isAscendant(linkedToRemotePath)) {
|
||||||
|
refreshListOfFilesFragment();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean uploadWasFine = intent.getBooleanExtra(FileUploader.EXTRA_UPLOAD_RESULT,
|
boolean uploadWasFine = intent.getBooleanExtra(FileUploader.EXTRA_UPLOAD_RESULT,
|
||||||
|
@ -1101,6 +1105,16 @@ public class FileDisplayActivity extends HookActivity
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO refactor this receiver, and maybe DownloadFinishReceiver; this method is duplicated :S
|
||||||
|
private boolean isAscendant(String linkedToRemotePath) {
|
||||||
|
OCFile currentDir = getCurrentDir();
|
||||||
|
return (
|
||||||
|
currentDir != null &&
|
||||||
|
currentDir.getRemotePath().startsWith(linkedToRemotePath)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1112,11 +1126,10 @@ public class FileDisplayActivity extends HookActivity
|
||||||
*/
|
*/
|
||||||
private class DownloadFinishReceiver extends BroadcastReceiver {
|
private class DownloadFinishReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
//int refreshCounter = 0;
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
try {
|
try {
|
||||||
boolean sameAccount = isSameAccount(context, intent);
|
boolean sameAccount = isSameAccount(intent);
|
||||||
String downloadedRemotePath =
|
String downloadedRemotePath =
|
||||||
intent.getStringExtra(FileDownloader.EXTRA_REMOTE_PATH);
|
intent.getStringExtra(FileDownloader.EXTRA_REMOTE_PATH);
|
||||||
boolean isDescendant = isDescendant(downloadedRemotePath);
|
boolean isDescendant = isDescendant(downloadedRemotePath);
|
||||||
|
@ -1125,7 +1138,6 @@ public class FileDisplayActivity extends HookActivity
|
||||||
String linkedToRemotePath =
|
String linkedToRemotePath =
|
||||||
intent.getStringExtra(FileDownloader.EXTRA_LINKED_TO_PATH);
|
intent.getStringExtra(FileDownloader.EXTRA_LINKED_TO_PATH);
|
||||||
if (linkedToRemotePath == null || isAscendant(linkedToRemotePath)) {
|
if (linkedToRemotePath == null || isAscendant(linkedToRemotePath)) {
|
||||||
//Log_OC.v(TAG, "refresh #" + ++refreshCounter);
|
|
||||||
refreshListOfFilesFragment();
|
refreshListOfFilesFragment();
|
||||||
}
|
}
|
||||||
refreshSecondFragment(
|
refreshSecondFragment(
|
||||||
|
@ -1167,7 +1179,7 @@ public class FileDisplayActivity extends HookActivity
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isSameAccount(Context context, Intent intent) {
|
private boolean isSameAccount(Intent intent) {
|
||||||
String accountName = intent.getStringExtra(FileDownloader.ACCOUNT_NAME);
|
String accountName = intent.getStringExtra(FileDownloader.ACCOUNT_NAME);
|
||||||
return (accountName != null && getAccount() != null &&
|
return (accountName != null && getAccount() != null &&
|
||||||
accountName.equals(getAccount().name));
|
accountName.equals(getAccount().name));
|
||||||
|
|
|
@ -249,24 +249,47 @@ public class FileListListAdapter extends BaseAdapter implements ListAdapter {
|
||||||
mTransferServiceGetter.getFileDownloaderBinder();
|
mTransferServiceGetter.getFileDownloaderBinder();
|
||||||
FileUploaderBinder uploaderBinder =
|
FileUploaderBinder uploaderBinder =
|
||||||
mTransferServiceGetter.getFileUploaderBinder();
|
mTransferServiceGetter.getFileUploaderBinder();
|
||||||
boolean downloading = (downloaderBinder != null &&
|
|
||||||
downloaderBinder.isDownloading(mAccount, file));
|
|
||||||
OperationsServiceBinder opsBinder =
|
OperationsServiceBinder opsBinder =
|
||||||
mTransferServiceGetter.getOperationsServiceBinder();
|
mTransferServiceGetter.getOperationsServiceBinder();
|
||||||
downloading |= (opsBinder != null &&
|
|
||||||
opsBinder.isSynchronizing(mAccount, file.getRemotePath()));
|
localStateView.setVisibility(View.INVISIBLE); // default first
|
||||||
if (downloading) {
|
|
||||||
localStateView.setImageResource(R.drawable.downloading_file_indicator);
|
if ( //synchronizing
|
||||||
|
opsBinder != null &&
|
||||||
|
opsBinder.isSynchronizing(mAccount, file.getRemotePath())
|
||||||
|
) {
|
||||||
|
localStateView.setImageResource(R.drawable.synchronizing_file_indicator);
|
||||||
localStateView.setVisibility(View.VISIBLE);
|
localStateView.setVisibility(View.VISIBLE);
|
||||||
} else if (uploaderBinder != null &&
|
|
||||||
uploaderBinder.isUploading(mAccount, file)) {
|
} else if ( // downloading
|
||||||
localStateView.setImageResource(R.drawable.uploading_file_indicator);
|
downloaderBinder != null &&
|
||||||
|
downloaderBinder.isDownloading(mAccount, file)
|
||||||
|
) {
|
||||||
|
localStateView.setImageResource(
|
||||||
|
file.isFolder() ?
|
||||||
|
R.drawable.synchronizing_file_indicator :
|
||||||
|
R.drawable.downloading_file_indicator
|
||||||
|
);
|
||||||
localStateView.setVisibility(View.VISIBLE);
|
localStateView.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
} else if ( //uploading
|
||||||
|
uploaderBinder != null &&
|
||||||
|
uploaderBinder.isUploading(mAccount, file)
|
||||||
|
) {
|
||||||
|
localStateView.setImageResource(
|
||||||
|
file.isFolder() ?
|
||||||
|
R.drawable.synchronizing_file_indicator :
|
||||||
|
R.drawable.uploading_file_indicator
|
||||||
|
);
|
||||||
|
localStateView.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
} else if (file.getEtagInConflict() != null) { // conflict
|
||||||
|
localStateView.setImageResource(R.drawable.conflict_file_indicator);
|
||||||
|
localStateView.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
} else if (file.isDown()) {
|
} else if (file.isDown()) {
|
||||||
localStateView.setImageResource(R.drawable.local_file_indicator);
|
localStateView.setImageResource(R.drawable.local_file_indicator);
|
||||||
localStateView.setVisibility(View.VISIBLE);
|
localStateView.setVisibility(View.VISIBLE);
|
||||||
} else {
|
|
||||||
localStateView.setVisibility(View.INVISIBLE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// share with me icon
|
// share with me icon
|
||||||
|
@ -345,46 +368,6 @@ public class FileListListAdapter extends BaseAdapter implements ListAdapter {
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Local Folder size in human readable format
|
|
||||||
*
|
|
||||||
* @param path
|
|
||||||
* String
|
|
||||||
* @return Size in human readable format
|
|
||||||
*/
|
|
||||||
private String getFolderSizeHuman(String path) {
|
|
||||||
|
|
||||||
File dir = new File(path);
|
|
||||||
|
|
||||||
if (dir.exists()) {
|
|
||||||
long bytes = FileStorageUtils.getFolderSize(dir);
|
|
||||||
return DisplayUtils.bytesToHumanReadable(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
return "0 B";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Local Folder size
|
|
||||||
* @param dir File
|
|
||||||
* @return Size in bytes
|
|
||||||
*/
|
|
||||||
private long getFolderSize(File dir) {
|
|
||||||
if (dir.exists()) {
|
|
||||||
long result = 0;
|
|
||||||
File[] fileList = dir.listFiles();
|
|
||||||
for(int i = 0; i < fileList.length; i++) {
|
|
||||||
if(fileList[i].isDirectory()) {
|
|
||||||
result += getFolderSize(fileList[i]);
|
|
||||||
} else {
|
|
||||||
result += fileList[i].length();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getViewTypeCount() {
|
public int getViewTypeCount() {
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
@ -25,7 +25,6 @@ package com.owncloud.android.ui.dialog;
|
||||||
*
|
*
|
||||||
* Triggers the removal according to the user response.
|
* Triggers the removal according to the user response.
|
||||||
*/
|
*/
|
||||||
import java.util.Vector;
|
|
||||||
|
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
@ -106,34 +105,6 @@ implements ConfirmationDialogFragmentListener {
|
||||||
public void onCancel(String callerTag) {
|
public void onCancel(String callerTag) {
|
||||||
ComponentsGetter cg = (ComponentsGetter)getActivity();
|
ComponentsGetter cg = (ComponentsGetter)getActivity();
|
||||||
cg.getFileOperationsHelper().removeFile(mTargetFile, true);
|
cg.getFileOperationsHelper().removeFile(mTargetFile, true);
|
||||||
|
|
||||||
FileDataStorageManager storageManager = cg.getStorageManager();
|
|
||||||
|
|
||||||
boolean containsFavorite = false;
|
|
||||||
if (mTargetFile.isFolder()) {
|
|
||||||
// TODO Enable when "On Device" is recovered ?
|
|
||||||
Vector<OCFile> 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
|
@Override
|
||||||
|
|
|
@ -248,9 +248,8 @@ public class FileDetailFragment extends FileFragment implements OnClickListener
|
||||||
dialog.show(getFragmentManager(), FTAG_RENAME_FILE);
|
dialog.show(getFragmentManager(), FTAG_RENAME_FILE);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case R.id.action_cancel_download:
|
case R.id.action_cancel_sync: {
|
||||||
case R.id.action_cancel_upload: {
|
((FileDisplayActivity)mContainerActivity).cancelTransference(getFile());
|
||||||
((FileDisplayActivity) mContainerActivity).cancelTransference(getFile());
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case R.id.action_download_file:
|
case R.id.action_download_file:
|
||||||
|
@ -300,7 +299,6 @@ public class FileDetailFragment extends FileFragment implements OnClickListener
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the fragment was created with an empty layout. An empty fragment can't show file details, must be replaced.
|
* Check if the fragment was created with an empty layout. An empty fragment can't show file details, must be replaced.
|
||||||
*
|
*
|
||||||
|
|
|
@ -368,9 +368,8 @@ public class OCFileListFragment extends ExtendedListFragment implements FileActi
|
||||||
mContainerActivity.getFileOperationsHelper().syncFile(mTargetFile);
|
mContainerActivity.getFileOperationsHelper().syncFile(mTargetFile);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case R.id.action_cancel_download:
|
case R.id.action_cancel_sync: {
|
||||||
case R.id.action_cancel_upload: {
|
((FileDisplayActivity)mContainerActivity).cancelTransference(mTargetFile);
|
||||||
((FileDisplayActivity) mContainerActivity).cancelTransference(mTargetFile);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case R.id.action_see_details: {
|
case R.id.action_see_details: {
|
||||||
|
|
|
@ -238,7 +238,7 @@ public class ErrorMessageAdapter {
|
||||||
|
|
||||||
} else { // Generic error
|
} else { // Generic error
|
||||||
// Show a Message, operation finished without success
|
// Show a Message, operation finished without success
|
||||||
message = String.format(res.getString(R.string.download_folder_failed_content),
|
message = String.format(res.getString(R.string.sync_folder_failed_content),
|
||||||
folderPathName);
|
folderPathName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ import com.owncloud.android.R;
|
||||||
import com.owncloud.android.datamodel.OCFile;
|
import com.owncloud.android.datamodel.OCFile;
|
||||||
import com.owncloud.android.lib.resources.files.RemoteFile;
|
import com.owncloud.android.lib.resources.files.RemoteFile;
|
||||||
|
|
||||||
|
import android.accounts.Account;
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
@ -53,8 +54,6 @@ public class FileStorageUtils {
|
||||||
public static Boolean mSortAscending = true;
|
public static Boolean mSortAscending = true;
|
||||||
|
|
||||||
|
|
||||||
//private static final String LOG_TAG = "FileStorageUtils";
|
|
||||||
|
|
||||||
public static final String getSavePath(String accountName) {
|
public static final String getSavePath(String accountName) {
|
||||||
File sdCard = Environment.getExternalStorageDirectory();
|
File sdCard = Environment.getExternalStorageDirectory();
|
||||||
return sdCard.getAbsolutePath() + "/" + MainApp.getDataFolder() + "/" + Uri.encode(accountName, "@");
|
return sdCard.getAbsolutePath() + "/" + MainApp.getDataFolder() + "/" + Uri.encode(accountName, "@");
|
||||||
|
@ -120,7 +119,7 @@ public class FileStorageUtils {
|
||||||
* Creates and populates a new {@link OCFile} object with the data read from the server.
|
* 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).
|
* @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) {
|
public static OCFile fillOCFile(RemoteFile remote) {
|
||||||
OCFile file = new OCFile(remote.getRemotePath());
|
OCFile file = new OCFile(remote.getRemotePath());
|
||||||
|
@ -303,4 +302,32 @@ public class FileStorageUtils {
|
||||||
return (result != null) ? result : "";
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue