<string name="filedetails_created">Created:</string>
<string name="filedetails_modified">Modified:</string>
<string name="filedetails_download">Download</string>
- <string name="filedetails_redownload">Refresh</string>
+ <string name="filedetails_sync_file">Refresh</string>
+ <string name="filedetails_redownload">Redownload</string>
<string name="filedetails_open">Open</string>
+ <string name="filedetails_renamed_in_upload_msg">File was renamed to %1$s during upload</string>
<string name="common_yes">Yes</string>
<string name="common_no">No</string>
<string name="common_ok">OK</string>
- <string name="common_cancel">Cancel</string>
+ <string name="common_cancel_download">Cancel download</string>
+ <string name="common_cancel_upload">Cancel upload</string>
+ <string name="common_cancel">Cancel</string>
<string name="common_save_exit">Save & Exit</string>
<string name="common_exit">Leave ownCloud</string>
<string name="common_error">Error</string>
<string name="uploader_upload_failed_content_multiple">Upload failed: %1$d/%2$d files were upload</string>
<string name="downloader_download_in_progress_ticker">Downloading …</string>
<string name="downloader_download_in_progress_content">%1$d%% Downloading %2$s</string>
-- <string name="downloader_download_succeeded_ticker">Download suceeded</string>
++ <string name="downloader_download_succeeded_ticker">Download succeeded</string>
<string name="downloader_download_succeeded_content">%1$s was successfully download</string>
<string name="downloader_download_failed_ticker">Download failed</string>
<string name="downloader_download_failed_content">Download of %1$s could not be completed</string>
<string name="sync_string_contacts">Contacts</string>
<string name="sync_fail_ticker">Synchronization failed</string>
<string name="sync_fail_content">Synchronization of %1$s could not be completed</string>
+ <string name="sync_conflicts_in_favourites_ticker">Conflicts found</string>
+ <string name="sync_conflicts_in_favourites_content">%1$d kept-in-sync files could not be sync\'ed</string>
+ <string name="sync_fail_in_favourites_ticker">Kept-in-sync files failed</string>
+ <string name="sync_fail_in_favourites_content">Contents of %1$d files could not be sync\'ed (%2$d conflicts)</string>
<string name="use_ssl">Use Secure Connection</string>
<string name="location_no_provider">ownCloud cannot track your device. Please check your location settings</string>
<string name="common_rename">Rename</string>
<string name="common_remove">Remove</string>
- <string name="confirmation_remove_alert">"Do you really want to remove %1$s ?"</string>
- <string name="confirmation_remove_local">Local only</string>
- <string name="confirmation_remove_remote">Remove from server</string>
- <string name="confirmation_remove_remote_and_local">Remote and local</string>
+ <string name="confirmation_remove_alert">"Do you really want to remove %1$s ?"</string>
+ <string name="confirmation_remove_folder_alert">"Do you really want to remove %1$s and its contents ?"</string>
+ <string name="confirmation_remove_local">Local only</string>
+ <string name="confirmation_remove_folder_local">Local contents only</string>
+ <string name="confirmation_remove_remote">Remove from server</string>
+ <string name="confirmation_remove_remote_and_local">Remote and local</string>
<string name="remove_success_msg">"Successful removal"</string>
<string name="remove_fail_msg">"Removal could not be completed"</string>
+ <string name="rename_dialog_title">Enter a new name</string>
<string name="rename_local_fail_msg">"Local copy could not be renamed; try a differente new name"</string>
<string name="rename_server_fail_msg">"Rename could not be completed"</string>
+ <string name="sync_file_fail_msg">Remote file could not be checked</string>
+ <string name="sync_file_nothing_to_do_msg">File contents already synchronized</string>
+
<string name="create_dir_fail_msg">Directory could not be created</string>
<string name="wait_a_moment">Wait a moment</string>
import com.owncloud.android.db.ProviderMeta;
import com.owncloud.android.db.ProviderMeta.ProviderTableMeta;
- import com.owncloud.android.files.services.FileDownloader;
+ import com.owncloud.android.utils.FileStorageUtils;
import android.accounts.Account;
import android.content.ContentProviderClient;
if (!file.isDirectory())
cv.put(ProviderTableMeta.FILE_STORAGE_PATH, file.getStoragePath());
cv.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, mAccount.name);
- cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE, file.getLastSyncDate());
+ cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE, file.getLastSyncDateForProperties());
+ cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, file.getLastSyncDateForData());
cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.keepInSync() ? 1 : 0);
-- if (fileExists(file.getRemotePath())) {
-- OCFile oldFile = getFileByPath(file.getRemotePath());
- if (file.getStoragePath() == null && oldFile.getStoragePath() != null)
- file.setStoragePath(oldFile.getStoragePath());
- if (!file.isDirectory());
- cv.put(ProviderTableMeta.FILE_STORAGE_PATH, file.getStoragePath());
-- file.setFileId(oldFile.getFileId());
++ boolean sameRemotePath = fileExists(file.getRemotePath());
++ if (sameRemotePath ||
++ fileExists(file.getFileId()) ) { // for renamed files; no more delete and create
++
++ if (sameRemotePath) {
++ OCFile oldFile = getFileByPath(file.getRemotePath());
++ file.setFileId(oldFile.getFileId());
++ }
overriden = true;
if (getContentResolver() != null) {
+ e.getMessage());
}
}
- } else if (fileExists(file.getFileId())) { // for renamed files; no more delete and create
- OCFile oldFile = getFileById(file.getFileId());
- if (file.getStoragePath() == null && oldFile.getStoragePath() != null)
- file.setStoragePath(oldFile.getStoragePath());
- if (!file.isDirectory());
- cv.put(ProviderTableMeta.FILE_STORAGE_PATH, file.getStoragePath());
-
- overriden = true;
- if (getContentResolver() != null) {
- getContentResolver().update(ProviderTableMeta.CONTENT_URI, cv,
- ProviderTableMeta._ID + "=?",
- new String[] { String.valueOf(file.getFileId()) });
- } else {
- try {
- getContentProvider().update(ProviderTableMeta.CONTENT_URI,
- cv, ProviderTableMeta._ID + "=?",
- new String[] { String.valueOf(file.getFileId()) });
- } catch (RemoteException e) {
- Log.e(TAG,
- "Fail to insert insert file to database "
- + e.getMessage());
- }
- }
} else {
Uri result_uri = null;
if (getContentResolver() != null) {
if (!file.isDirectory())
cv.put(ProviderTableMeta.FILE_STORAGE_PATH, file.getStoragePath());
cv.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, mAccount.name);
- cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE, file.getLastSyncDate());
+ cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE, file.getLastSyncDateForProperties());
+ cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, file.getLastSyncDateForData());
cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.keepInSync() ? 1 : 0);
if (fileExists(file.getRemotePath())) {
OCFile oldFile = getFileByPath(file.getRemotePath());
- if (file.getStoragePath() == null && oldFile.getStoragePath() != null)
- file.setStoragePath(oldFile.getStoragePath());
- if (!file.isDirectory());
- cv.put(ProviderTableMeta.FILE_STORAGE_PATH, file.getStoragePath());
file.setFileId(oldFile.getFileId());
-
operations.add(ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI).
withValues(cv).
withSelection( ProviderTableMeta._ID + "=?",
new String[] { String.valueOf(file.getFileId()) })
.build());
+ } else if (fileExists(file.getFileId())) {
+ OCFile oldFile = getFileById(file.getFileId());
+ if (file.getStoragePath() == null && oldFile.getStoragePath() != null)
+ file.setStoragePath(oldFile.getStoragePath());
+ if (!file.isDirectory());
+ cv.put(ProviderTableMeta.FILE_STORAGE_PATH, file.getStoragePath());
+
+ operations.add(ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI).
+ withValues(cv).
+ withSelection( ProviderTableMeta._ID + "=?",
+ new String[] { String.valueOf(file.getFileId()) })
+ .build());
+
} else {
operations.add(ContentProviderOperation.newInsert(ProviderTableMeta.CONTENT_URI).withValues(cv).build());
}
file.setStoragePath(c.getString(c
.getColumnIndex(ProviderTableMeta.FILE_STORAGE_PATH)));
if (file.getStoragePath() == null) {
- // try to find existing file and bind it with current account
- File f = new File(FileDownloader.getSavePath(mAccount.name) + file.getRemotePath());
- if (f.exists())
+ // try to find existing file and bind it with current account; - with the current update of SynchronizeFolderOperation, this won't be necessary anymore after a full synchronization of the account
+ File f = new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file));
+ if (f.exists()) {
file.setStoragePath(f.getAbsolutePath());
+ file.setLastSyncDateForData(f.lastModified());
+ }
}
}
file.setFileLength(c.getLong(c
.getColumnIndex(ProviderTableMeta.FILE_CREATION)));
file.setModificationTimestamp(c.getLong(c
.getColumnIndex(ProviderTableMeta.FILE_MODIFIED)));
- file.setLastSyncDate(c.getLong(c
+ file.setLastSyncDateForProperties(c.getLong(c
.getColumnIndex(ProviderTableMeta.FILE_LAST_SYNC_DATE)));
+ file.setLastSyncDateForData(c.getLong(c.
+ getColumnIndex(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA)));
file.setKeepInSync(c.getInt(
c.getColumnIndex(ProviderTableMeta.FILE_KEEP_IN_SYNC)) == 1 ? true : false);
}
if (file.isDown() && removeLocalCopy) {
new File(file.getStoragePath()).delete();
}
- File f = new File(FileDownloader.getSavePath(mAccount.name) + file.getRemotePath());
+ if (file.isDirectory() && removeLocalCopy) {
- String defaultSavePath = FileDownloader.getSavePath(mAccount.name);
++ File f = new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file));
+ if (f.exists() && f.isDirectory() && (f.list() == null || f.list().length == 0)) {
+ f.delete();
+ }
+ }
+ }
+
+ @Override
+ public void removeDirectory(OCFile dir, boolean removeDBData, boolean removeLocalContent) {
+ // TODO consider possible failures
+ if (dir != null && dir.isDirectory() && dir.getFileId() != -1) {
+ Vector<OCFile> children = getDirectoryContent(dir);
+ if (children != null) {
+ OCFile child = null;
+ for (int i=0; i<children.size(); i++) {
+ child = children.get(i);
+ if (child.isDirectory()) {
+ removeDirectory(child, removeDBData, removeLocalContent);
+ } else {
+ if (removeDBData) {
+ removeFile(child, removeLocalContent);
+ } else if (removeLocalContent) {
+ if (child.isDown()) {
+ new File(child.getStoragePath()).delete();
+ }
+ }
+ }
+ }
+ if (removeDBData) {
+ removeFile(dir, true);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Updates database for a folder that was moved to a different location.
+ *
+ * TODO explore better (faster) implementations
+ * TODO throw exceptions up !
+ */
+ @Override
+ public void moveDirectory(OCFile dir, String newPath) {
+ // TODO check newPath
+
+ if (dir != null && dir.isDirectory() && dir.fileExists() && !dir.getFileName().equals(OCFile.PATH_SEPARATOR)) {
+ /// 1. get all the descendants of 'dir' in a single QUERY (including 'dir')
+ Cursor c = null;
+ if (getContentProvider() != null) {
+ try {
+ c = getContentProvider().query(ProviderTableMeta.CONTENT_URI,
+ null,
+ ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " + ProviderTableMeta.FILE_PATH + " LIKE ?",
+ new String[] { mAccount.name, dir.getRemotePath() + "%" }, null);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.getMessage());
+ }
+ } else {
+ c = getContentResolver().query(ProviderTableMeta.CONTENT_URI,
+ null,
+ ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " + ProviderTableMeta.FILE_PATH + " LIKE ?",
+ new String[] { mAccount.name, dir.getRemotePath() + "%" }, null);
+ }
+
+ /// 2. prepare a batch of update operations to change all the descendants
+ ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>(c.getCount());
+ int lengthOfOldPath = dir.getRemotePath().length();
++ String defaultSavePath = FileStorageUtils.getSavePath(mAccount.name);
+ int lengthOfOldStoragePath = defaultSavePath.length() + lengthOfOldPath;
+ if (c.moveToFirst()) {
+ do {
+ ContentValues cv = new ContentValues(); // don't take the constructor out of the loop and clear the object
+ OCFile child = createFileInstance(c);
+ cv.put(ProviderTableMeta.FILE_PATH, newPath + child.getRemotePath().substring(lengthOfOldPath));
+ if (child.getStoragePath() != null && child.getStoragePath().startsWith(defaultSavePath)) {
+ cv.put(ProviderTableMeta.FILE_STORAGE_PATH, defaultSavePath + newPath + child.getStoragePath().substring(lengthOfOldStoragePath));
+ }
+ operations.add(ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI).
+ withValues(cv).
+ withSelection( ProviderTableMeta._ID + "=?",
+ new String[] { String.valueOf(child.getFileId()) })
+ .build());
+ } while (c.moveToNext());
+ }
+ c.close();
+
+ /// 3. apply updates in batch
+ try {
+ if (getContentResolver() != null) {
+ getContentResolver().applyBatch(ProviderMeta.AUTHORITY_FILES, operations);
+
+ } else {
+ getContentProvider().applyBatch(operations);
+ }
+
+ } catch (OperationApplicationException e) {
+ Log.e(TAG, "Fail to update descendants of " + dir.getFileId() + " in database", e);
+
+ } catch (RemoteException e) {
+ Log.e(TAG, "Fail to update desendants of " + dir.getFileId() + " in database", e);
+ }
+
+ }
}
}
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.Log;
public class OCFile implements Parcelable, Comparable<OCFile> {
};
public static final String PATH_SEPARATOR = "/";
+
+ private static final String TAG = OCFile.class.getSimpleName();
private long mId;
private long mParentId;
private String mLocalPath;
private String mMimeType;
private boolean mNeedsUpdating;
- private long mLastSyncDate;
+ private long mLastSyncDateForProperties;
+ private long mLastSyncDateForData;
private boolean mKeepInSync;
+ private String mEtag;
+
/**
* Create new {@link OCFile} with given path.
*
mMimeType = source.readString();
mNeedsUpdating = source.readInt() == 0;
mKeepInSync = source.readInt() == 1;
- mLastSyncDate = source.readLong();
+ mLastSyncDateForProperties = source.readLong();
+ mLastSyncDateForData = source.readLong();
}
@Override
dest.writeString(mMimeType);
dest.writeInt(mNeedsUpdating ? 1 : 0);
dest.writeInt(mKeepInSync ? 1 : 0);
- dest.writeLong(mLastSyncDate);
+ dest.writeLong(mLastSyncDateForProperties);
+ dest.writeLong(mLastSyncDateForData);
}
/**
*/
public String getFileName() {
File f = new File(getRemotePath());
- return f.getName().length() == 0 ? "/" : f.getName();
+ return f.getName().length() == 0 ? PATH_SEPARATOR : f.getName();
+ }
+
+ /**
+ * Sets the name of the file
+ *
+ * Does nothing if the new name is null, empty or includes "/" ; or if the file is the root directory
+ */
+ public void setFileName(String name) {
+ Log.d(TAG, "OCFile name changin from " + mRemotePath);
+ if (name != null && name.length() > 0 && !name.contains(PATH_SEPARATOR) && !mRemotePath.equals(PATH_SEPARATOR)) {
+ String parent = (new File(getRemotePath())).getParent();
+ parent = (parent.endsWith(PATH_SEPARATOR)) ? parent : parent + PATH_SEPARATOR;
+ mRemotePath = parent + name;
+ if (isDirectory()) {
+ mRemotePath += PATH_SEPARATOR;
+ }
+ Log.d(TAG, "OCFile name changed to " + mRemotePath);
+ }
}
/**
mLength = 0;
mCreationTimestamp = 0;
mModifiedTimestamp = 0;
- mLastSyncDate = 0;
+ mLastSyncDateForProperties = 0;
+ mLastSyncDateForData = 0;
mKeepInSync = false;
mNeedsUpdating = false;
}
return mNeedsUpdating;
}
- public long getLastSyncDate() {
- return mLastSyncDate;
+ public long getLastSyncDateForProperties() {
+ return mLastSyncDateForProperties;
+ }
+
+ public void setLastSyncDateForProperties(long lastSyncDate) {
+ mLastSyncDateForProperties = lastSyncDate;
}
- public void setLastSyncDate(long lastSyncDate) {
- mLastSyncDate = lastSyncDate;
+ public long getLastSyncDateForData() {
+ return mLastSyncDateForData;
+ }
+
+ public void setLastSyncDateForData(long lastSyncDate) {
+ mLastSyncDateForData = lastSyncDate;
}
public void setKeepInSync(boolean keepInSync) {
@Override
public String toString() {
String asString = "[id=%s, name=%s, mime=%s, downloaded=%s, local=%s, remote=%s, parentId=%s, keepInSinc=%s]";
- asString = String.format(asString, new Long(mId), getFileName(), mMimeType, isDown(), mLocalPath, mRemotePath, new Long(mParentId), new Boolean(mKeepInSync));
+ asString = String.format(asString, Long.valueOf(mId), getFileName(), mMimeType, isDown(), mLocalPath, mRemotePath, Long.valueOf(mParentId), Boolean.valueOf(mKeepInSync));
return asString;
}
+ public String getEtag() {
+ return mEtag;
+ }
+
+ public long getLocalModificationTimestamp() {
+ if (mLocalPath != null && mLocalPath.length() > 0) {
+ File f = new File(mLocalPath);
+ return f.lastModified();
+ }
+ return 0;
+ }
+
}
import java.util.concurrent.ConcurrentHashMap;\r
import java.util.concurrent.ConcurrentMap;\r
\r
+ import com.owncloud.android.datamodel.FileDataStorageManager;\r
import com.owncloud.android.datamodel.OCFile;\r
- import com.owncloud.android.db.ProviderMeta.ProviderTableMeta;\r
import eu.alefzero.webdav.OnDatatransferProgressListener;\r
\r
import com.owncloud.android.network.OwnCloudClientUtils;\r
import android.app.NotificationManager;\r
import android.app.PendingIntent;\r
import android.app.Service;\r
- import android.content.ContentValues;\r
import android.content.Intent;\r
- import android.net.Uri;\r
import android.os.Binder;\r
- import android.os.Environment;\r
import android.os.Handler;\r
import android.os.HandlerThread;\r
import android.os.IBinder;\r
public static final String EXTRA_ACCOUNT = "ACCOUNT";\r
public static final String EXTRA_FILE = "FILE";\r
\r
+ public static final String DOWNLOAD_ADDED_MESSAGE = "DOWNLOAD_ADDED";\r
public static final String DOWNLOAD_FINISH_MESSAGE = "DOWNLOAD_FINISH";\r
public static final String EXTRA_DOWNLOAD_RESULT = "RESULT"; \r
public static final String EXTRA_FILE_PATH = "FILE_PATH";\r
private IBinder mBinder;\r
private WebdavClient mDownloadClient = null;\r
private Account mLastAccount = null;\r
+ private FileDataStorageManager mStorageManager;\r
\r
private ConcurrentMap<String, DownloadFileOperation> mPendingDownloads = new ConcurrentHashMap<String, DownloadFileOperation>();\r
private DownloadFileOperation mCurrentDownload = null;\r
private String buildRemoteName(Account account, OCFile file) {\r
return account.name + file.getRemotePath();\r
}\r
- \r
- public static final String getSavePath(String accountName) {\r
- File sdCard = Environment.getExternalStorageDirectory();\r
- return sdCard.getAbsolutePath() + "/owncloud/" + Uri.encode(accountName, "@"); \r
- // URL encoding is an 'easy fix' to overcome that NTFS and FAT32 don't allow ":" in file names, that can be in the accountName since 0.1.190B\r
- }\r
- \r
- public static final String getTemporalPath(String accountName) {\r
- File sdCard = Environment.getExternalStorageDirectory();\r
- return sdCard.getAbsolutePath() + "/owncloud/tmp/" + Uri.encode(accountName, "@");\r
- // URL encoding is an 'easy fix' to overcome that NTFS and FAT32 don't allow ":" in file names, that can be in the accountName since 0.1.190B\r
- }\r
\r
\r
/**\r
mPendingDownloads.putIfAbsent(downloadKey, newDownload);\r
newDownload.addDatatransferProgressListener(this);\r
requestedDownloads.add(downloadKey);\r
+ sendBroadcastNewDownload(newDownload);\r
\r
} catch (IllegalArgumentException e) {\r
Log.e(TAG, "Not enough information provided in intent: " + e.getMessage());\r
\r
\r
/**\r
- * Returns True when the file described by 'file' in the ownCloud account 'account' is downloading or waiting to download\r
+ * Returns True when the file described by 'file' in the ownCloud account 'account' is downloading or waiting to download.\r
+ * \r
+ * If 'file' is a directory, returns 'true' if some of its descendant files is downloading or waiting to download. \r
* \r
* @param account Owncloud account where the remote file is stored.\r
* @param file A file that could be in the queue of downloads.\r
*/\r
public boolean isDownloading(Account account, OCFile file) {\r
+ String targetKey = buildRemoteName(account, file);\r
synchronized (mPendingDownloads) {\r
- return (mPendingDownloads.containsKey(buildRemoteName(account, file)));\r
+ if (file.isDirectory()) {\r
+ // this can be slow if there are many downloads :(\r
+ Iterator<String> it = mPendingDownloads.keySet().iterator();\r
+ boolean found = false;\r
+ while (it.hasNext() && !found) {\r
+ found = it.next().startsWith(targetKey);\r
+ }\r
+ return found;\r
+ } else {\r
+ return (mPendingDownloads.containsKey(targetKey));\r
+ }\r
}\r
}\r
}\r
/// prepare client object to send the request to the ownCloud server\r
if (mDownloadClient == null || !mLastAccount.equals(mCurrentDownload.getAccount())) {\r
mLastAccount = mCurrentDownload.getAccount();\r
+ mStorageManager = new FileDataStorageManager(mLastAccount, getContentResolver());\r
mDownloadClient = OwnCloudClientUtils.createOwnCloudClient(mLastAccount, getApplicationContext());\r
}\r
\r
try {\r
downloadResult = mCurrentDownload.execute(mDownloadClient);\r
if (downloadResult.isSuccess()) {\r
- ContentValues cv = new ContentValues();\r
- cv.put(ProviderTableMeta.FILE_STORAGE_PATH, mCurrentDownload.getSavePath());\r
- getContentResolver().update(\r
- ProviderTableMeta.CONTENT_URI,\r
- cv,\r
- ProviderTableMeta.FILE_NAME + "=? AND "\r
- + ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?",\r
- new String[] {\r
- mCurrentDownload.getSavePath().substring(mCurrentDownload.getSavePath().lastIndexOf('/') + 1),\r
- mLastAccount.name });\r
+ saveDownloadedFile();\r
}\r
\r
} finally {\r
/// notify result\r
notifyDownloadResult(mCurrentDownload, downloadResult);\r
\r
- sendFinalBroadcast(mCurrentDownload, downloadResult);\r
+ sendBroadcastDownloadFinished(mCurrentDownload, downloadResult);\r
}\r
}\r
\r
- \r
+ \r
+ /**\r
+ * Updates the OC File after a successful download.\r
+ */\r
+ private void saveDownloadedFile() {\r
+ OCFile file = mCurrentDownload.getFile();\r
+ long syncDate = System.currentTimeMillis();\r
+ file.setLastSyncDateForProperties(syncDate);\r
+ file.setLastSyncDateForData(syncDate);\r
+ file.setModificationTimestamp(mCurrentDownload.getModificationTimestamp());\r
+ // file.setEtag(mCurrentDownload.getEtag()); // TODO Etag, where available\r
+ file.setMimetype(mCurrentDownload.getMimeType());\r
+ file.setStoragePath(mCurrentDownload.getSavePath());\r
+ file.setFileLength((new File(mCurrentDownload.getSavePath()).length()));\r
+ mStorageManager.saveFile(file);\r
+ }\r
+ \r
+ \r
/**\r
* Creates a status notification to show the download progress\r
* \r
\r
\r
/**\r
- * Sends a broadcast in order to the interested activities can update their view\r
+ * Sends a broadcast when a download finishes in order to the interested activities can update their view\r
* \r
* @param download Finished download operation\r
* @param downloadResult Result of the download operation\r
*/\r
- private void sendFinalBroadcast(DownloadFileOperation download, RemoteOperationResult downloadResult) {\r
+ private void sendBroadcastDownloadFinished(DownloadFileOperation download, RemoteOperationResult downloadResult) {\r
Intent end = new Intent(DOWNLOAD_FINISH_MESSAGE);\r
end.putExtra(EXTRA_DOWNLOAD_RESULT, downloadResult.isSuccess());\r
end.putExtra(ACCOUNT_NAME, download.getAccount().name);\r
end.putExtra(EXTRA_REMOTE_PATH, download.getRemotePath());\r
- if (downloadResult.isSuccess()) {\r
- end.putExtra(EXTRA_FILE_PATH, download.getSavePath());\r
- }\r
- sendBroadcast(end);\r
+ end.putExtra(EXTRA_FILE_PATH, download.getSavePath());\r
+ sendStickyBroadcast(end);\r
+ }\r
+ \r
+ \r
+ /**\r
+ * Sends a broadcast when a new download is added to the queue.\r
+ * \r
+ * @param download Added download operation\r
+ */\r
+ private void sendBroadcastNewDownload(DownloadFileOperation download) {\r
+ Intent added = new Intent(DOWNLOAD_ADDED_MESSAGE);\r
+ /*added.putExtra(ACCOUNT_NAME, download.getAccount().name);\r
+ added.putExtra(EXTRA_REMOTE_PATH, download.getRemotePath());*/\r
+ added.putExtra(EXTRA_FILE_PATH, download.getSavePath());\r
+ sendStickyBroadcast(added);\r
}\r
\r
}\r
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
+ import org.apache.http.HttpStatus;
+ import org.apache.jackrabbit.webdav.MultiStatus;
+ import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;
+
import com.owncloud.android.authenticator.AccountAuthenticator;
import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.operations.UploadFileOperation;
import com.owncloud.android.ui.activity.FileDetailActivity;
import com.owncloud.android.ui.fragment.FileDetailFragment;
+ import com.owncloud.android.utils.FileStorageUtils;
import com.owncloud.android.utils.OwnCloudVersion;
import eu.alefzero.webdav.OnDatatransferProgressListener;
+ import eu.alefzero.webdav.WebdavEntry;
+ import eu.alefzero.webdav.WebdavUtils;
import com.owncloud.android.network.OwnCloudClientUtils;
public class FileUploader extends Service implements OnDatatransferProgressListener {
public static final String UPLOAD_FINISH_MESSAGE = "UPLOAD_FINISH";
- public static final String EXTRA_PARENT_DIR_ID = "PARENT_DIR_ID";
public static final String EXTRA_UPLOAD_RESULT = "RESULT";
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_FILE_PATH = "FILE_PATH";
+ public static final String ACCOUNT_NAME = "ACCOUNT_NAME";
- public static final String KEY_LOCAL_FILE = "LOCAL_FILE"; // TODO remove this as a possible input argument ; use KEY_FILE everywhere
- public static final String KEY_REMOTE_FILE = "REMOTE_FILE"; // TODO remove this as a possible input argument ; use KEY_FILE everywhere
+ public static final String KEY_FILE = "FILE";
+ public static final String KEY_LOCAL_FILE = "LOCAL_FILE";
+ public static final String KEY_REMOTE_FILE = "REMOTE_FILE";
+ public static final String KEY_MIME_TYPE = "MIME_TYPE";
+
public static final String KEY_ACCOUNT = "ACCOUNT";
+
public static final String KEY_UPLOAD_TYPE = "UPLOAD_TYPE";
public static final String KEY_FORCE_OVERWRITE = "KEY_FORCE_OVERWRITE";
- public static final String ACCOUNT_NAME = "ACCOUNT_NAME";
- public static final String KEY_MIME_TYPE = "MIME_TYPE";
public static final String KEY_INSTANT_UPLOAD = "INSTANT_UPLOAD";
public static final int UPLOAD_SINGLE_FILE = 0;
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
- if (!intent.hasExtra(KEY_ACCOUNT) || !intent.hasExtra(KEY_UPLOAD_TYPE)) {
+ if (!intent.hasExtra(KEY_ACCOUNT) || !intent.hasExtra(KEY_UPLOAD_TYPE) || !(intent.hasExtra(KEY_LOCAL_FILE) || intent.hasExtra(KEY_FILE))) {
Log.e(TAG, "Not enough information provided in intent");
return Service.START_NOT_STICKY;
}
}
Account account = intent.getParcelableExtra(KEY_ACCOUNT);
- String[] localPaths, remotePaths, mimeTypes;
+ String[] localPaths = null, remotePaths = null, mimeTypes = null;
+ OCFile[] files = null;
if (uploadType == UPLOAD_SINGLE_FILE) {
- localPaths = new String[] { intent.getStringExtra(KEY_LOCAL_FILE) };
- remotePaths = new String[] { intent
- .getStringExtra(KEY_REMOTE_FILE) };
- mimeTypes = new String[] { intent.getStringExtra(KEY_MIME_TYPE) };
+
+ if (intent.hasExtra(KEY_FILE)) {
+ files = new OCFile[] {intent.getParcelableExtra(KEY_FILE) };
+
+ } else {
+ localPaths = new String[] { intent.getStringExtra(KEY_LOCAL_FILE) };
+ remotePaths = new String[] { intent.getStringExtra(KEY_REMOTE_FILE) };
+ mimeTypes = new String[] { intent.getStringExtra(KEY_MIME_TYPE) };
+ }
} else { // mUploadType == UPLOAD_MULTIPLE_FILES
- localPaths = intent.getStringArrayExtra(KEY_LOCAL_FILE);
- remotePaths = intent.getStringArrayExtra(KEY_REMOTE_FILE);
- mimeTypes = intent.getStringArrayExtra(KEY_MIME_TYPE);
+
+ if (intent.hasExtra(KEY_FILE)) {
+ files = (OCFile[]) intent.getParcelableArrayExtra(KEY_FILE); // TODO will this casting work fine?
+
+ } else {
+ localPaths = intent.getStringArrayExtra(KEY_LOCAL_FILE);
+ remotePaths = intent.getStringArrayExtra(KEY_REMOTE_FILE);
+ mimeTypes = intent.getStringArrayExtra(KEY_MIME_TYPE);
+ }
}
- if (localPaths == null) {
- Log.e(TAG, "Incorrect array for local paths provided in upload intent");
- return Service.START_NOT_STICKY;
+ FileDataStorageManager storageManager = new FileDataStorageManager(account, getContentResolver());
+
+ boolean forceOverwrite = intent.getBooleanExtra(KEY_FORCE_OVERWRITE, false);
+ boolean isInstant = intent.getBooleanExtra(KEY_INSTANT_UPLOAD, false);
+ boolean fixed = false;
+ if (isInstant) {
+ fixed = checkAndFixInstantUploadDirectory(storageManager); // MUST be done BEFORE calling obtainNewOCFileToUpload
}
- if (remotePaths == null) {
- Log.e(TAG, "Incorrect array for remote paths provided in upload intent");
+
+ if (intent.hasExtra(KEY_FILE) && files == null) {
+ Log.e(TAG, "Incorrect array for OCFiles provided in upload intent");
return Service.START_NOT_STICKY;
- }
- if (localPaths.length != remotePaths.length) {
- Log.e(TAG, "Different number of remote paths and local paths!");
- return Service.START_NOT_STICKY;
+ } else if (!intent.hasExtra(KEY_FILE)) {
+ if (localPaths == null) {
+ Log.e(TAG, "Incorrect array for local paths provided in upload intent");
+ return Service.START_NOT_STICKY;
+ }
+ if (remotePaths == null) {
+ Log.e(TAG, "Incorrect array for remote paths provided in upload intent");
+ return Service.START_NOT_STICKY;
+ }
+ if (localPaths.length != remotePaths.length) {
+ Log.e(TAG, "Different number of remote paths and local paths!");
+ return Service.START_NOT_STICKY;
+ }
+
+ files = new OCFile[localPaths.length];
+ for (int i=0; i < localPaths.length; i++) {
+ files[i] = obtainNewOCFileToUpload(remotePaths[i], localPaths[i], ((mimeTypes!=null)?mimeTypes[i]:(String)null), storageManager);
+ }
}
-
- boolean isInstant = intent.getBooleanExtra(KEY_INSTANT_UPLOAD, false);
- boolean forceOverwrite = intent.getBooleanExtra(KEY_FORCE_OVERWRITE, false);
-
+
OwnCloudVersion ocv = new OwnCloudVersion(AccountManager.get(this).getUserData(account, AccountAuthenticator.KEY_OC_VERSION));
boolean chunked = FileUploader.chunkedUploadIsSupported(ocv);
AbstractList<String> requestedUploads = new Vector<String>();
String uploadKey = null;
UploadFileOperation newUpload = null;
- OCFile file = null;
- FileDataStorageManager storageManager = new FileDataStorageManager(account, getContentResolver());
- boolean fixed = false;
- if (isInstant) {
- fixed = checkAndFixInstantUploadDirectory(storageManager);
- }
try {
- for (int i=0; i < localPaths.length; i++) {
- uploadKey = buildRemoteName(account, remotePaths[i]);
- file = obtainNewOCFileToUpload(remotePaths[i], localPaths[i], ((mimeTypes!=null)?mimeTypes[i]:(String)null), isInstant, forceOverwrite, storageManager);
+ for (int i=0; i < files.length; i++) {
+ uploadKey = buildRemoteName(account, files[i].getRemotePath());
if (chunked) {
- newUpload = new ChunkedUploadFileOperation(account, file, isInstant, forceOverwrite);
+ newUpload = new ChunkedUploadFileOperation(account, files[i], isInstant, forceOverwrite);
} else {
- newUpload = new UploadFileOperation(account, file, isInstant, forceOverwrite);
+ newUpload = new UploadFileOperation(account, files[i], isInstant, forceOverwrite);
}
if (fixed && i==0) {
newUpload.setRemoteFolderToBeCreated();
/**
* Returns True when the file described by 'file' is being uploaded to the ownCloud account 'account' or waiting for it
*
+ * If 'file' is a directory, returns 'true' if some of its descendant files is downloading or waiting to download.
+ *
* @param account Owncloud account where the remote file will be stored.
* @param file A file that could be in the queue of pending uploads
*/
public boolean isUploading(Account account, OCFile file) {
+ String targetKey = buildRemoteName(account, file);
synchronized (mPendingUploads) {
- return (mPendingUploads.containsKey(buildRemoteName(account, file)));
+ if (file.isDirectory()) {
+ // this can be slow if there are many downloads :(
+ 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));
+ }
}
}
}
try {
uploadResult = mCurrentUpload.execute(mUploadClient);
if (uploadResult.isSuccess()) {
- saveUploadedFile(mCurrentUpload.getFile(), mStorageManager);
+ saveUploadedFile();
}
} finally {
}
/**
- * Saves a new OC File after a successful upload.
+ * Saves a OC File after a successful upload.
+ *
+ * A PROPFIND is necessary to keep the props in the local database synchronized with the server,
+ * specially the modification time and Etag (where available)
*
- * @param file OCFile describing the uploaded file
- * @param storageManager Interface to the database where the new OCFile has to be stored.
- * @param parentDirId Id of the parent OCFile.
+ * TODO refactor this ugly thing
*/
- private void saveUploadedFile(OCFile file, FileDataStorageManager storageManager) {
- file.setModificationTimestamp(System.currentTimeMillis());
- storageManager.saveFile(file);
+ private void saveUploadedFile() {
+ OCFile file = mCurrentUpload.getFile();
+ long syncDate = System.currentTimeMillis();
+ file.setLastSyncDateForData(syncDate);
+
+ /// new PROPFIND to keep data consistent with server in theory, should return the same we already have
+ PropFindMethod propfind = null;
+ RemoteOperationResult result = null;
+ try {
+ propfind = new PropFindMethod(mUploadClient.getBaseUri() + WebdavUtils.encodePath(mCurrentUpload.getRemotePath()));
+ int status = mUploadClient.executeMethod(propfind);
+ boolean isMultiStatus = (status == HttpStatus.SC_MULTI_STATUS);
+ if (isMultiStatus) {
+ MultiStatus resp = propfind.getResponseBodyAsMultiStatus();
+ WebdavEntry we = new WebdavEntry(resp.getResponses()[0],
+ mUploadClient.getBaseUri().getPath());
+ updateOCFile(file, we);
+ file.setLastSyncDateForProperties(syncDate);
+
+ } else {
+ mUploadClient.exhaustResponse(propfind.getResponseBodyAsStream());
+ }
+
+ result = new RemoteOperationResult(isMultiStatus, status);
+ Log.i(TAG, "Update: synchronizing properties for uploaded " + mCurrentUpload.getRemotePath() + ": " + result.getLogMessage());
+
+ } catch (Exception e) {
+ result = new RemoteOperationResult(e);
+ Log.i(TAG, "Update: synchronizing properties for uploaded " + mCurrentUpload.getRemotePath() + ": " + result.getLogMessage(), e);
+
+ } finally {
+ if (propfind != null)
+ propfind.releaseConnection();
+ }
+
+
+ if (mCurrentUpload.wasRenamed()) {
+ OCFile oldFile = mCurrentUpload.getOldFile();
+ if (!oldFile.fileExists()) {
+ // just a name coincidence
+ file.setStoragePath(oldFile.getStoragePath());
+
+ } else {
+ // conflict resolved with 'Keep both' by the user
+ File localFile = new File(oldFile.getStoragePath());
+ File newLocalFile = new File(FileStorageUtils.getDefaultSavePathFor(mCurrentUpload.getAccount().name, file));
+ boolean renameSuccessed = localFile.renameTo(newLocalFile);
+ if (renameSuccessed) {
+ file.setStoragePath(newLocalFile.getAbsolutePath());
+
+ } else {
+ // poor solution
+ Log.d(TAG, "DAMN IT: local rename failed after uploading a file with a new name already existing both in the remote account and the local database (should be due to a conflict solved with 'keep both'");
+ file.setStoragePath(null);
+ // not so fine:
+ // - local file will be kept there as 'trash' until is download (and overwritten) again from the server;
+ // - user will see as 'not down' a file that was just upload
+ // BUT:
+ // - no loss of data happened
+ // - when the user downloads again the renamed and original file from the server, local file names and contents will be correctly synchronized with names and contents in server
+ }
+ oldFile.setStoragePath(null);
+ mStorageManager.saveFile(oldFile);
+ }
+ }
+
+ mStorageManager.saveFile(file);
+ }
+
+
+ private void updateOCFile(OCFile file, WebdavEntry we) {
+ file.setCreationTimestamp(we.createTimestamp());
+ file.setFileLength(we.contentLength());
+ file.setMimetype(we.contentType());
+ file.setModificationTimestamp(we.modifiedTimesamp());
+ // file.setEtag(mCurrentDownload.getEtag()); // TODO Etag, where available
}
}
- private OCFile obtainNewOCFileToUpload(String remotePath, String localPath, String mimeType, boolean isInstant, boolean forceOverwrite, FileDataStorageManager storageManager) {
+ private OCFile obtainNewOCFileToUpload(String remotePath, String localPath, String mimeType, FileDataStorageManager storageManager) {
OCFile newFile = new OCFile(remotePath);
newFile.setStoragePath(localPath);
- newFile.setLastSyncDate(0);
- newFile.setKeepInSync(forceOverwrite);
+ newFile.setLastSyncDateForProperties(0);
+ newFile.setLastSyncDateForData(0);
// size
if (localPath != null && localPath.length() > 0) {
File localFile = new File(localPath);
newFile.setFileLength(localFile.length());
+ newFile.setLastSyncDateForData(localFile.lastModified());
} // don't worry about not assigning size, the problems with localPath are checked when the UploadFileOperation instance is created
// MIME type
// parent dir
String parentPath = new File(remotePath).getParent();
- parentPath = parentPath.endsWith("/")?parentPath:parentPath+"/" ;
+ parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : parentPath + OCFile.PATH_SEPARATOR ;
OCFile parentDir = storageManager.getFileByPath(parentPath);
if (parentDir == null) {
throw new IllegalStateException("Can not upload a file to a non existing remote location: " + parentPath);
private void sendFinalBroadcast(UploadFileOperation upload, RemoteOperationResult uploadResult) {
Intent end = new Intent(UPLOAD_FINISH_MESSAGE);
end.putExtra(EXTRA_REMOTE_PATH, upload.getRemotePath()); // real remote path, after possible automatic renaming
+ if (upload.wasRenamed()) {
+ end.putExtra(EXTRA_OLD_REMOTE_PATH, upload.getOldFile().getRemotePath());
+ }
end.putExtra(EXTRA_FILE_PATH, upload.getStoragePath());
end.putExtra(ACCOUNT_NAME, upload.getAccount().name);
end.putExtra(EXTRA_UPLOAD_RESULT, uploadResult.isSuccess());
- sendBroadcast(end);
- end.putExtra(EXTRA_PARENT_DIR_ID, upload.getFile().getParentId());
+ sendStickyBroadcast(end);
}
import org.apache.jackrabbit.webdav.client.methods.DavMethodBase;
//import org.apache.jackrabbit.webdav.client.methods.MoveMethod;
+import android.accounts.Account;
import android.util.Log;
import com.owncloud.android.datamodel.DataStorageManager;
import com.owncloud.android.datamodel.OCFile;
- import com.owncloud.android.files.services.FileDownloader;
import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
+ import com.owncloud.android.utils.FileStorageUtils;
import eu.alefzero.webdav.WebdavClient;
import eu.alefzero.webdav.WebdavUtils;
private OCFile mFile;
+ private Account mAccount;
private String mNewName;
+ private String mNewRemotePath;
private DataStorageManager mStorageManager;
* Constructor
*
* @param file OCFile instance describing the remote file or folder to rename
+ * @param account OwnCloud account containing the remote file
* @param newName New name to set as the name of file.
* @param storageManager Reference to the local database corresponding to the account where the file is contained.
*/
- public RenameFileOperation(OCFile file, String newName, DataStorageManager storageManager) {
+ public RenameFileOperation(OCFile file, Account account, String newName, DataStorageManager storageManager) {
mFile = file;
+ mAccount = account;
mNewName = newName;
+ mNewRemotePath = null;
mStorageManager = storageManager;
}
RemoteOperationResult result = null;
LocalMoveMethod move = null;
- //MoveMethod move = null; // TODO find out why not use this
- String newRemotePath = null;
+ mNewRemotePath = null;
try {
if (mNewName.equals(mFile.getFileName())) {
return new RemoteOperationResult(ResultCode.OK);
}
- newRemotePath = (new File(mFile.getRemotePath())).getParent() + mNewName;
+ String parent = (new File(mFile.getRemotePath())).getParent();
+ parent = (parent.endsWith(OCFile.PATH_SEPARATOR)) ? parent : parent + OCFile.PATH_SEPARATOR;
+ mNewRemotePath = parent + mNewName;
+ if (mFile.isDirectory()) {
+ mNewRemotePath += OCFile.PATH_SEPARATOR;
+ }
// check if the new name is valid in the local file system
if (!isValidNewName()) {
return new RemoteOperationResult(ResultCode.INVALID_LOCAL_FILE_NAME);
}
- // check if a remote file with the new name already exists
- if (client.existsFile(newRemotePath)) {
+ // check if a file with the new name already exists
+ if (client.existsFile(mNewRemotePath) || // remote check could fail by network failure, or by indeterminate behavior of HEAD for folders ...
+ mStorageManager.getFileByPath(mNewRemotePath) != null) { // ... so local check is convenient
return new RemoteOperationResult(ResultCode.INVALID_OVERWRITE);
}
- /*move = new MoveMethod( client.getBaseUri() + WebdavUtils.encodePath(mFile.getRemotePath()),
- client.getBaseUri() + WebdavUtils.encodePath(newRemotePath),
- false);*/
move = new LocalMoveMethod( client.getBaseUri() + WebdavUtils.encodePath(mFile.getRemotePath()),
- client.getBaseUri() + WebdavUtils.encodePath(newRemotePath));
+ client.getBaseUri() + WebdavUtils.encodePath(mNewRemotePath));
int status = client.executeMethod(move, RENAME_READ_TIMEOUT, RENAME_CONNECTION_TIMEOUT);
if (move.succeeded()) {
- // create new OCFile instance for the renamed file
- OCFile newFile = obtainUpdatedFile();
- OCFile oldFile = mFile;
- mFile = newFile;
-
- // try to rename the local copy of the file
- if (oldFile.isDown()) {
- File f = new File(oldFile.getStoragePath());
- String newStoragePath = f.getParent() + mNewName;
- if (f.renameTo(new File(newStoragePath))) {
- mFile.setStoragePath(newStoragePath);
- }
- // else - NOTHING: the link to the local file is kept although the local name can't be updated
- // TODO - study conditions when this could be a problem
+ if (mFile.isDirectory()) {
+ saveLocalDirectory();
+
+ } else {
+ saveLocalFile();
+
}
-
- mStorageManager.removeFile(oldFile, false);
- mStorageManager.saveFile(mFile);
+
+ /*
+ *} else if (mFile.isDirectory() && (status == 207 || status >= 500)) {
+ * // TODO
+ * // if server fails in the rename of a folder, some children files could have been moved to a folder with the new name while some others
+ * // stayed in the old folder;
+ * //
+ * // easiest and heaviest solution is synchronizing the parent folder (or the full account);
+ * //
+ * // a better solution is synchronizing the folders with the old and new names;
+ *}
+ */
}
move.getResponseBodyAsString(); // exhaust response, although not interesting
result = new RemoteOperationResult(move.succeeded(), status);
- Log.i(TAG, "Rename " + mFile.getRemotePath() + " to " + newRemotePath + ": " + result.getLogMessage());
+ Log.i(TAG, "Rename " + mFile.getRemotePath() + " to " + mNewRemotePath + ": " + result.getLogMessage());
} catch (Exception e) {
result = new RemoteOperationResult(e);
- Log.e(TAG, "Rename " + mFile.getRemotePath() + " to " + ((newRemotePath==null) ? mNewName : newRemotePath) + ": " + result.getLogMessage(), e);
+ Log.e(TAG, "Rename " + mFile.getRemotePath() + " to " + ((mNewRemotePath==null) ? mNewName : mNewRemotePath) + ": " + result.getLogMessage(), e);
} finally {
if (move != null)
}
- String localPath = FileDownloader.getSavePath(mAccount.name) + mFile.getRemotePath();
+ private void saveLocalDirectory() {
+ mStorageManager.moveDirectory(mFile, mNewRemotePath);
- localDir.renameTo(new File(FileDownloader.getSavePath(mAccount.name) + mNewRemotePath));
++ String localPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, mFile);
+ File localDir = new File(localPath);
+ if (localDir.exists()) {
++ localDir.renameTo(new File(FileStorageUtils.getSavePath(mAccount.name) + mNewRemotePath));
+ // TODO - if renameTo fails, children files that are already down will result unlinked
+ }
+ }
+
+ private void saveLocalFile() {
+ mFile.setFileName(mNewName);
+
+ // try to rename the local copy of the file
+ if (mFile.isDown()) {
+ File f = new File(mFile.getStoragePath());
+ String parentStoragePath = f.getParent();
+ if (!parentStoragePath.endsWith(File.separator))
+ parentStoragePath += File.separator;
+ if (f.renameTo(new File(parentStoragePath + mNewName))) {
+ mFile.setStoragePath(parentStoragePath + mNewName);
+ }
+ // else - NOTHING: the link to the local file is kept although the local name can't be updated
+ // TODO - study conditions when this could be a problem
+ }
+
+ mStorageManager.saveFile(mFile);
+ }
+
/**
* Checks if the new name to set is valid in the file system
*
return false;
}
// create a test file
- String tmpFolder = FileDownloader.getTemporalPath("");
+ String tmpFolder = FileStorageUtils.getTemporalPath("");
File testFile = new File(tmpFolder + mNewName);
try {
testFile.createNewFile(); // return value is ignored; it could be 'false' because the file already existed, that doesn't invalidate the name
}
- /**
- * Creates a new OCFile for the new remote name of the renamed file.
- *
- * @return OCFile object with the same information than mFile, but the renamed remoteFile and the storagePath (empty)
- */
- private OCFile obtainUpdatedFile() {
- OCFile file = new OCFile(mStorageManager.getFileById(mFile.getParentId()).getRemotePath() + mNewName);
- file.setCreationTimestamp(mFile.getCreationTimestamp());
- file.setFileId(mFile.getFileId());
- file.setFileLength(mFile.getFileLength());
- file.setKeepInSync(mFile.keepInSync());
- file.setLastSyncDateForProperties(mFile.getLastSyncDateForProperties());
- file.setLastSyncDateForData(mFile.getLastSyncDateForData());
- file.setMimetype(mFile.getMimetype());
- file.setModificationTimestamp(mFile.getModificationTimestamp());
- file.setParentId(mFile.getParentId());
- return file;
- }
-
--
-- // move operation - TODO: find out why org.apache.jackrabbit.webdav.client.methods.MoveMethod is not used instead ¿?
++ // move operation
private class LocalMoveMethod extends DavMethodBase {
public LocalMoveMethod(String uri, String dest) {
import com.owncloud.android.operations.RemoteOperationResult.ResultCode;\r
import com.owncloud.android.operations.RemoveFileOperation;\r
import com.owncloud.android.operations.RenameFileOperation;\r
+ import com.owncloud.android.operations.SynchronizeFileOperation;\r
+ import com.owncloud.android.ui.activity.ConflictsResolveActivity;\r
import com.owncloud.android.ui.activity.FileDetailActivity;\r
import com.owncloud.android.ui.activity.FileDisplayActivity;\r
import com.owncloud.android.ui.activity.TransferServiceGetter;\r
private View mView;\r
private OCFile mFile;\r
private Account mAccount;\r
+ private FileDataStorageManager mStorageManager;\r
private ImageView mPreview;\r
\r
private DownloadFinishReceiver mDownloadFinishReceiver;\r
private Handler mHandler;\r
private RemoteOperation mLastRemoteOperation;\r
\r
- private static final String TAG = "FileDetailFragment";\r
+ private static final String TAG = FileDetailFragment.class.getSimpleName();\r
public static final String FTAG = "FileDetails"; \r
public static final String FTAG_CONFIRMATION = "REMOVE_CONFIRMATION_FRAGMENT";\r
\r
public FileDetailFragment() {\r
mFile = null;\r
mAccount = null;\r
+ mStorageManager = null;\r
mLayout = R.layout.file_details_empty;\r
}\r
\r
public FileDetailFragment(OCFile fileToDetail, Account ocAccount) {\r
mFile = fileToDetail;\r
mAccount = ocAccount;\r
+ mStorageManager = null; // we need a context to init this; the container activity is not available yet at this moment \r
mLayout = R.layout.file_details_empty;\r
\r
if(fileToDetail != null && ocAccount != null) {\r
mPreview = (ImageView)mView.findViewById(R.id.fdPreview);\r
}\r
\r
- updateFileDetails();\r
+ updateFileDetails(false);\r
return view;\r
}\r
\r
throw new ClassCastException(activity.toString() + " must implement " + FileDetailFragment.ContainerActivity.class.getSimpleName());\r
}\r
}\r
+ \r
+ \r
+ /**\r
+ * {@inheritDoc}\r
+ */\r
+ @Override\r
+ public void onActivityCreated(Bundle savedInstanceState) {\r
+ super.onActivityCreated(savedInstanceState);\r
+ if (mAccount != null) {\r
+ mStorageManager = new FileDataStorageManager(mAccount, getActivity().getApplicationContext().getContentResolver());;\r
+ }\r
+ }\r
\r
\r
@Override\r
public void onClick(View v) {\r
switch (v.getId()) {\r
case R.id.fdDownloadBtn: {\r
-- //if (FileDownloader.isDownloading(mAccount, mFile.getRemotePath())) {\r
FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();\r
FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder();\r
if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, mFile)) {\r
}\r
\r
} else {\r
- Intent i = new Intent(getActivity(), FileDownloader.class);\r
- i.putExtra(FileDownloader.EXTRA_ACCOUNT, mAccount);\r
- i.putExtra(FileDownloader.EXTRA_FILE, mFile);\r
- /*i.putExtra(FileDownloader.EXTRA_REMOTE_PATH, mFile.getRemotePath());\r
- i.putExtra(FileDownloader.EXTRA_FILE_PATH, mFile.getRemotePath());\r
- i.putExtra(FileDownloader.EXTRA_FILE_SIZE, mFile.getFileLength());*/\r
+ mLastRemoteOperation = new SynchronizeFileOperation(mFile, null, mStorageManager, mAccount, true, false, getActivity());\r
+ WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getSherlockActivity().getApplicationContext());\r
+ mLastRemoteOperation.execute(wc, this, mHandler);\r
\r
// update ui \r
- setButtonsForTransferring();\r
- \r
- getActivity().startService(i);\r
- mContainerActivity.onFileStateChanged(); // this is not working; it is performed before the fileDownloadService registers it as 'in progress'\r
+ boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity;\r
+ getActivity().showDialog((inDisplayActivity)? FileDisplayActivity.DIALOG_SHORT_WAIT : FileDetailActivity.DIALOG_SHORT_WAIT);\r
+ setButtonsForTransferring(); // disable button immediately, although the synchronization does not result in a file transference\r
+ \r
}\r
break;\r
}\r
case R.id.fdKeepInSync: {\r
CheckBox cb = (CheckBox) getView().findViewById(R.id.fdKeepInSync);\r
mFile.setKeepInSync(cb.isChecked());\r
- FileDataStorageManager fdsm = new FileDataStorageManager(mAccount, getActivity().getApplicationContext().getContentResolver());\r
- fdsm.saveFile(mFile);\r
- if (mFile.keepInSync()) {\r
- onClick(getView().findViewById(R.id.fdDownloadBtn));\r
- } else {\r
- mContainerActivity.onFileStateChanged(); // put inside 'else' to not call it twice (here, and in the virtual click on fdDownloadBtn)\r
- }\r
+ mStorageManager.saveFile(mFile);\r
\r
+ /// register the OCFile instance in the observer service to monitor local updates;\r
+ /// if necessary, the file is download \r
Intent intent = new Intent(getActivity().getApplicationContext(),\r
FileObserverService.class);\r
intent.putExtra(FileObserverService.KEY_FILE_CMD,\r
(cb.isChecked()?\r
FileObserverService.CMD_ADD_OBSERVED_FILE:\r
FileObserverService.CMD_DEL_OBSERVED_FILE));\r
- intent.putExtra(FileObserverService.KEY_CMD_ARG, mFile.getStoragePath());\r
+ intent.putExtra(FileObserverService.KEY_CMD_ARG_FILE, mFile);\r
+ intent.putExtra(FileObserverService.KEY_CMD_ARG_ACCOUNT, mAccount);\r
Log.e(TAG, "starting observer service");\r
getActivity().startService(intent);\r
\r
+ if (mFile.keepInSync()) {\r
+ onClick(getView().findViewById(R.id.fdDownloadBtn)); // force an immediate synchronization\r
+ }\r
break;\r
}\r
case R.id.fdRenameBtn: {\r
- EditNameDialog dialog = EditNameDialog.newInstance(mFile.getFileName());\r
- dialog.setOnDismissListener(this);\r
+ EditNameDialog dialog = EditNameDialog.newInstance(getString(R.string.rename_dialog_title), mFile.getFileName(), this);\r
dialog.show(getFragmentManager(), "nameeditdialog");\r
break;\r
} \r
try {\r
Intent i = new Intent(Intent.ACTION_VIEW);\r
mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(storagePath.substring(storagePath.lastIndexOf('.') + 1));\r
- if (mimeType != null && !mimeType.equals(mFile.getMimetype())) {\r
- i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), mimeType);\r
+ if (mimeType == null || !mimeType.equals(mFile.getMimetype())) {\r
+ if (mimeType != null) {\r
+ i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), mimeType);\r
+ } else {\r
+ // desperate try\r
+ i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), "*/*");\r
+ }\r
i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);\r
startActivity(i);\r
toastIt = false;\r
@Override\r
public void onConfirmation(String callerTag) {\r
if (callerTag.equals(FTAG_CONFIRMATION)) {\r
- FileDataStorageManager fdsm = new FileDataStorageManager(mAccount, getActivity().getContentResolver());\r
- if (fdsm.getFileById(mFile.getFileId()) != null) {\r
+ if (mStorageManager.getFileById(mFile.getFileId()) != null) {\r
mLastRemoteOperation = new RemoveFileOperation( mFile, \r
true, \r
- new FileDataStorageManager(mAccount, getActivity().getContentResolver()));\r
+ mStorageManager);\r
WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getSherlockActivity().getApplicationContext());\r
mLastRemoteOperation.execute(wc, this, mHandler);\r
\r
\r
@Override\r
public void onNeutral(String callerTag) {\r
- FileDataStorageManager fdsm = new FileDataStorageManager(mAccount, getActivity().getContentResolver());\r
File f = null;\r
if (mFile.isDown() && (f = new File(mFile.getStoragePath())).exists()) {\r
f.delete();\r
mFile.setStoragePath(null);\r
- fdsm.saveFile(mFile);\r
+ mStorageManager.saveFile(mFile);\r
updateFileDetails(mFile, mAccount);\r
}\r
}\r
*/\r
public void updateFileDetails(OCFile file, Account ocAccount) {\r
mFile = file;\r
+ if (ocAccount != null && ( \r
+ mStorageManager == null || \r
+ (mAccount != null && !mAccount.equals(ocAccount))\r
+ )) {\r
+ mStorageManager = new FileDataStorageManager(ocAccount, getActivity().getApplicationContext().getContentResolver());\r
+ }\r
mAccount = ocAccount;\r
- updateFileDetails();\r
+ updateFileDetails(false);\r
}\r
\r
\r
/**\r
* Updates the view with all relevant details about that file.\r
+ *\r
+ * TODO Remove parameter when the transferring state of files is kept in database. \r
+ * \r
+ * @param transferring Flag signaling if the file should be considered as downloading or uploading, \r
+ * although {@link FileDownloaderBinder#isDownloading(Account, OCFile)} and \r
+ * {@link FileUploaderBinder#isUploading(Account, OCFile)} return false.\r
+ * \r
*/\r
- public void updateFileDetails() {\r
+ public void updateFileDetails(boolean transferring) {\r
\r
if (mFile != null && mAccount != null && mLayout == R.layout.file_details_fragment) {\r
\r
//if (FileDownloader.isDownloading(mAccount, mFile.getRemotePath()) || FileUploader.isUploading(mAccount, mFile.getRemotePath())) {\r
FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();\r
FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder();\r
- if ((downloaderBinder != null && downloaderBinder.isDownloading(mAccount, mFile)) || (uploaderBinder != null && uploaderBinder.isUploading(mAccount, mFile))) {\r
+ if (transferring || (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, mFile)) || (uploaderBinder != null && uploaderBinder.isUploading(mAccount, mFile))) {\r
setButtonsForTransferring();\r
\r
} else if (mFile.isDown()) {\r
setButtonsForDown();\r
\r
} else {\r
+ // TODO load default preview image; when the local file is removed, the preview remains there\r
setButtonsForRemote();\r
}\r
}\r
+ getView().invalidate();\r
}\r
\r
\r
private void setButtonsForDown() {\r
if (!isEmpty()) {\r
Button downloadButton = (Button) getView().findViewById(R.id.fdDownloadBtn);\r
- downloadButton.setText(R.string.filedetails_redownload);\r
- //downloadButton.setEnabled(true);\r
+ downloadButton.setText(R.string.filedetails_sync_file);\r
- //downloadButton.setEnabled(true);\r
\r
((Button) getView().findViewById(R.id.fdOpenBtn)).setEnabled(true);\r
((Button) getView().findViewById(R.id.fdRenameBtn)).setEnabled(true);\r
String downloadedRemotePath = intent.getStringExtra(FileDownloader.EXTRA_REMOTE_PATH);\r
if (mFile.getRemotePath().equals(downloadedRemotePath)) {\r
if (downloadWasFine) {\r
- mFile.setStoragePath(intent.getStringExtra(FileDownloader.EXTRA_FILE_PATH)); // updates the local object without accessing the database again\r
+ mFile = mStorageManager.getFileByPath(downloadedRemotePath);\r
}\r
- updateFileDetails(); // it updates the buttons; must be called although !downloadWasFine\r
+ updateFileDetails(false); // it updates the buttons; must be called although !downloadWasFine\r
}\r
}\r
}\r
private class UploadFinishReceiver extends BroadcastReceiver {\r
@Override\r
public void onReceive(Context context, Intent intent) {\r
- Log.d(TAG, "Received broacast! : " + intent.getStringExtra(FileUploader.EXTRA_REMOTE_PATH));\r
String accountName = intent.getStringExtra(FileUploader.ACCOUNT_NAME);\r
\r
if (!isEmpty() && accountName.equals(mAccount.name)) {\r
boolean uploadWasFine = intent.getBooleanExtra(FileUploader.EXTRA_UPLOAD_RESULT, false);\r
String uploadRemotePath = intent.getStringExtra(FileUploader.EXTRA_REMOTE_PATH);\r
- if (mFile.getRemotePath().equals(uploadRemotePath)) {\r
+ boolean renamedInUpload = mFile.getRemotePath().equals(intent.getStringExtra(FileUploader.EXTRA_OLD_REMOTE_PATH));\r
+ if (mFile.getRemotePath().equals(uploadRemotePath) ||\r
+ renamedInUpload) {\r
if (uploadWasFine) {\r
- FileDataStorageManager fdsm = new FileDataStorageManager(mAccount, getActivity().getApplicationContext().getContentResolver());\r
- mFile = fdsm.getFileByPath(mFile.getRemotePath());\r
+ mFile = mStorageManager.getFileByPath(mFile.getRemotePath());\r
+ }\r
+ if (renamedInUpload) {\r
+ String newName = (new File(uploadRemotePath)).getName();\r
+ Toast msg = Toast.makeText(getActivity().getApplicationContext(), String.format(getString(R.string.filedetails_renamed_in_upload_msg), newName), Toast.LENGTH_LONG);\r
+ msg.show();\r
}\r
- updateFileDetails(); // it updates the buttons; must be called although !uploadWasFine; interrupted uploads still leave an incomplete file in the server\r
+ updateFileDetails(false); // it updates the buttons; must be called although !uploadWasFine; interrupted uploads still leave an incomplete file in the server\r
}\r
}\r
}\r
String newFilename = dialog.getNewFilename();\r
Log.d(TAG, "name edit dialog dismissed with new name " + newFilename);\r
mLastRemoteOperation = new RenameFileOperation( mFile, \r
+ mAccount, \r
newFilename, \r
new FileDataStorageManager(mAccount, getActivity().getContentResolver()));\r
WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getSherlockActivity().getApplicationContext());\r
\r
} else if (operation instanceof RenameFileOperation) {\r
onRenameFileOperationFinish((RenameFileOperation)operation, result);\r
+ \r
+ } else if (operation instanceof SynchronizeFileOperation) {\r
+ onSynchronizeFileOperationFinish((SynchronizeFileOperation)operation, result);\r
}\r
}\r
}\r
}\r
}\r
}\r
+ \r
+ private void onSynchronizeFileOperationFinish(SynchronizeFileOperation operation, RemoteOperationResult result) {\r
+ boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity;\r
+ getActivity().dismissDialog((inDisplayActivity)? FileDisplayActivity.DIALOG_SHORT_WAIT : FileDetailActivity.DIALOG_SHORT_WAIT);\r
\r
+ if (!result.isSuccess()) {\r
+ if (result.getCode() == ResultCode.SYNC_CONFLICT) {\r
+ Intent i = new Intent(getActivity(), ConflictsResolveActivity.class);\r
+ i.putExtra(ConflictsResolveActivity.EXTRA_FILE, mFile);\r
+ i.putExtra(ConflictsResolveActivity.EXTRA_ACCOUNT, mAccount);\r
+ startActivity(i);\r
+ \r
+ } else {\r
+ Toast msg = Toast.makeText(getActivity(), R.string.sync_file_fail_msg, Toast.LENGTH_LONG); \r
+ msg.show();\r
+ }\r
+ \r
+ if (mFile.isDown()) {\r
+ setButtonsForDown();\r
+ \r
+ } else {\r
+ setButtonsForRemote();\r
+ }\r
+ \r
+ } else {\r
+ if (operation.transferWasRequested()) {\r
+ mContainerActivity.onFileStateChanged(); // this is not working; FileDownloader won't do NOTHING at all until this method finishes, so \r
+ // checking the service to see if the file is downloading results in FALSE\r
+ } else {\r
+ Toast msg = Toast.makeText(getActivity(), R.string.sync_file_nothing_to_do_msg, Toast.LENGTH_LONG); \r
+ msg.show();\r
+ if (mFile.isDown()) {\r
+ setButtonsForDown();\r
+ \r
+ } else {\r
+ setButtonsForRemote();\r
+ }\r
+ }\r
+ }\r
+ }\r
\r
}\r