-Subproject commit 9598f2bb2ceed4a834cd5586a903f270ca4c0ccc
+Subproject commit 90939dc3925ffaaa0de269bbbe1b35e274968ea1
<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="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="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 + "=?",
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);
}
new File(file.getStoragePath()).delete();
}
if (file.isDirectory() && removeLocalCopy) {
- File f = new File(FileDownloader.getSavePath(mAccount.name) + file.getRemotePath());
+ File f = new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file));
if (f.exists() && f.isDirectory() && (f.list() == null || f.list().length == 0)) {
f.delete();
}
/// 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 = FileDownloader.getSavePath(mAccount.name);
+ String defaultSavePath = FileStorageUtils.getSavePath(mAccount.name);
int lengthOfOldStoragePath = defaultSavePath.length() + lengthOfOldPath;
if (c.moveToFirst()) {
do {
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);
}
/**
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) {
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;
+ }
+
}
public static final String AUTHORITY_FILES = "org.owncloud";\r
public static final String DB_FILE = "owncloud.db";\r
public static final String DB_NAME = "filelist";\r
- public static final int DB_VERSION = 2;\r
+ //public static final int DB_VERSION = 2;\r
+ public static final int DB_VERSION = 3;\r
\r
private ProviderMeta() {\r
}\r
public static final String FILE_STORAGE_PATH = "media_path";\r
public static final String FILE_PATH = "path";\r
public static final String FILE_ACCOUNT_OWNER = "file_owner";\r
- public static final String FILE_LAST_SYNC_DATE = "last_sync_date";\r
+ public static final String FILE_LAST_SYNC_DATE = "last_sync_date"; // _for_properties, but let's keep it as it is\r
+ public static final String FILE_LAST_SYNC_DATE_FOR_DATA = "last_sync_date_for_data";\r
public static final String FILE_KEEP_IN_SYNC = "keep_in_sync";\r
\r
public static final String DEFAULT_SORT_ORDER = FILE_NAME\r
package com.owncloud.android.files;
-import java.util.LinkedList;
-import java.util.List;
+import java.io.File;
-import com.owncloud.android.datamodel.DataStorageManager;
+import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile;
-import com.owncloud.android.files.OwnCloudFileObserver.FileObserverStatusListener.Status;
-import com.owncloud.android.files.services.FileUploader;
import com.owncloud.android.network.OwnCloudClientUtils;
import com.owncloud.android.operations.RemoteOperationResult;
import com.owncloud.android.operations.SynchronizeFileOperation;
+import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
+import com.owncloud.android.ui.activity.ConflictsResolveActivity;
import eu.alefzero.webdav.WebdavClient;
public static int CHANGES_ONLY = CLOSE_WRITE;
- private static String TAG = "OwnCloudFileObserver";
+ private static String TAG = OwnCloudFileObserver.class.getSimpleName();
+
private String mPath;
private int mMask;
- DataStorageManager mStorage;
- Account mOCAccount;
- OCFile mFile;
- static Context mContext;
- List<FileObserverStatusListener> mListeners;
-
- public OwnCloudFileObserver(String path) {
- this(path, ALL_EVENTS);
- }
+ private Account mOCAccount;
+ //private OCFile mFile;
+ private Context mContext;
+
- public OwnCloudFileObserver(String path, int mask) {
+ public OwnCloudFileObserver(String path, Account account, Context context, int mask) {
super(path, mask);
+ if (path == null)
+ throw new IllegalArgumentException("NULL path argument received");
+ /*if (file == null)
+ throw new IllegalArgumentException("NULL file argument received");*/
+ if (account == null)
+ throw new IllegalArgumentException("NULL account argument received");
+ if (context == null)
+ throw new IllegalArgumentException("NULL context argument received");
+ /*if (!path.equals(file.getStoragePath()) && !path.equals(FileStorageUtils.getDefaultSavePathFor(account.name, file)))
+ throw new IllegalArgumentException("File argument is not linked to the local file set in path argument"); */
mPath = path;
- mMask = mask;
- mListeners = new LinkedList<FileObserverStatusListener>();
- }
-
- public void setAccount(Account account) {
+ //mFile = file;
mOCAccount = account;
- }
-
- public void setStorageManager(DataStorageManager manager) {
- mStorage = manager;
- }
-
- public void setOCFile(OCFile file) {
- mFile = file;
- }
-
- public void setContext(Context context) {
- mContext = context;
- }
-
- public String getPath() {
- return mPath;
- }
-
- public String getRemotePath() {
- return mFile.getRemotePath();
- }
-
- public void addObserverStatusListener(FileObserverStatusListener listener) {
- mListeners.add(listener);
+ mContext = context;
+ mMask = mask;
}
@Override
public void onEvent(int event, String path) {
- Log.d(TAG, "Got file modified with event " + event + " and path " + path);
+ Log.d(TAG, "Got file modified with event " + event + " and path " + mPath + ((path != null) ? File.separator + path : ""));
if ((event & mMask) == 0) {
- Log.wtf(TAG, "Incorrect event " + event + " sent for file " + path +
+ Log.wtf(TAG, "Incorrect event " + event + " sent for file " + mPath + ((path != null) ? File.separator + path : "") +
" with registered for " + mMask + " and original path " +
mPath);
- for (FileObserverStatusListener l : mListeners)
- l.OnObservedFileStatusUpdate(mPath, getRemotePath(), mOCAccount, Status.INCORRECT_MASK);
return;
}
WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(mOCAccount, mContext);
- SynchronizeFileOperation sfo = new SynchronizeFileOperation(mFile.getRemotePath(), mStorage, mOCAccount, mContext);
+ FileDataStorageManager storageManager = new FileDataStorageManager(mOCAccount, mContext.getContentResolver());
+ OCFile file = storageManager.getFileByLocalPath(mPath); // a fresh object is needed; many things could have occurred to the file since it was registered to observe
+ // again, assuming that local files are linked to a remote file AT MOST, SOMETHING TO BE DONE;
+ SynchronizeFileOperation sfo = new SynchronizeFileOperation(file,
+ null,
+ storageManager,
+ mOCAccount,
+ true,
+ true,
+ mContext);
RemoteOperationResult result = sfo.execute(wc);
-
- if (result.getExtraData() == Boolean.TRUE) {
- // inform user about conflict and let him decide what to do
- for (FileObserverStatusListener l : mListeners)
- l.OnObservedFileStatusUpdate(mPath, getRemotePath(), mOCAccount, Status.CONFLICT);
- return;
- }
-
- for (FileObserverStatusListener l : mListeners)
- l.OnObservedFileStatusUpdate(mPath, getRemotePath(), mOCAccount, Status.SENDING_TO_UPLOADER);
-
- Intent i = new Intent(mContext, FileUploader.class);
- i.putExtra(FileUploader.KEY_ACCOUNT, mOCAccount);
- i.putExtra(FileUploader.KEY_REMOTE_FILE, mFile.getRemotePath());
- i.putExtra(FileUploader.KEY_LOCAL_FILE, mPath);
- i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
- i.putExtra(FileUploader.KEY_FORCE_OVERWRITE, true);
- mContext.startService(i);
- }
-
- public interface FileObserverStatusListener {
- public enum Status {
- SENDING_TO_UPLOADER,
- CONFLICT,
- INCORRECT_MASK
+ if (result.getCode() == ResultCode.SYNC_CONFLICT) {
+ // ISSUE 5: if the user is not running the app (this is a service!), this can be very intrusive; a notification should be preferred
+ Intent i = new Intent(mContext, ConflictsResolveActivity.class);
+ i.setFlags(i.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
+ i.putExtra(ConflictsResolveActivity.EXTRA_FILE, file);
+ i.putExtra(ConflictsResolveActivity.EXTRA_ACCOUNT, mOCAccount);
+ mContext.startActivity(i);
}
-
- public void OnObservedFileStatusUpdate(String localPath,
- String remotePath,
- Account account,
- FileObserverStatusListener.Status status);
+ // TODO save other errors in some point where the user can inspect them later;
+ // or maybe just toast them;
+ // or nothing, very strange fails
}
}
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
/// 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
package com.owncloud.android.files.services;
-import java.util.ArrayList;
-import java.util.List;
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
-import com.owncloud.android.AccountUtils;
import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.db.ProviderMeta.ProviderTableMeta;
import com.owncloud.android.files.OwnCloudFileObserver;
-import com.owncloud.android.files.OwnCloudFileObserver.FileObserverStatusListener;
-import com.owncloud.android.ui.activity.ConflictsResolveActivity;
+import com.owncloud.android.operations.SynchronizeFileOperation;
+import com.owncloud.android.utils.FileStorageUtils;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.os.IBinder;
import android.util.Log;
-public class FileObserverService extends Service implements FileObserverStatusListener {
-
- public final static String KEY_FILE_CMD = "KEY_FILE_CMD";
- public final static String KEY_CMD_ARG = "KEY_CMD_ARG";
+public class FileObserverService extends Service {
public final static int CMD_INIT_OBSERVED_LIST = 1;
public final static int CMD_ADD_OBSERVED_FILE = 2;
public final static int CMD_DEL_OBSERVED_FILE = 3;
- public final static int CMD_ADD_DOWNLOADING_FILE = 4;
- private static String TAG = "FileObserverService";
- private static List<OwnCloudFileObserver> mObservers;
- private static List<DownloadCompletedReceiver> mDownloadReceivers;
- private static Object mReceiverListLock = new Object();
+ public final static String KEY_FILE_CMD = "KEY_FILE_CMD";
+ public final static String KEY_CMD_ARG_FILE = "KEY_CMD_ARG_FILE";
+ public final static String KEY_CMD_ARG_ACCOUNT = "KEY_CMD_ARG_ACCOUNT";
+
+ private static String TAG = FileObserverService.class.getSimpleName();
+
+ private static Map<String, OwnCloudFileObserver> mObserversMap;
+ private static DownloadCompletedReceiverBis mDownloadReceiver;
private IBinder mBinder = new LocalBinder();
public class LocalBinder extends Binder {
}
@Override
+ public void onCreate() {
+ super.onCreate();
+ mDownloadReceiver = new DownloadCompletedReceiverBis();
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(FileDownloader.DOWNLOAD_ADDED_MESSAGE);
+ filter.addAction(FileDownloader.DOWNLOAD_FINISH_MESSAGE);
+ registerReceiver(mDownloadReceiver, filter);
+
+ mObserversMap = new HashMap<String, OwnCloudFileObserver>();
+ //initializeObservedList();
+ }
+
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ unregisterReceiver(mDownloadReceiver);
+ mObserversMap = null; // TODO study carefully the life cycle of Services to grant the best possible observance
+ Log.d(TAG, "Bye, bye");
+ }
+
+
+ @Override
public IBinder onBind(Intent intent) {
return mBinder;
}
initializeObservedList();
break;
case CMD_ADD_OBSERVED_FILE:
- addObservedFile(intent.getStringExtra(KEY_CMD_ARG));
+ addObservedFile( (OCFile)intent.getParcelableExtra(KEY_CMD_ARG_FILE),
+ (Account)intent.getParcelableExtra(KEY_CMD_ARG_ACCOUNT));
break;
case CMD_DEL_OBSERVED_FILE:
- removeObservedFile(intent.getStringExtra(KEY_CMD_ARG));
- break;
- case CMD_ADD_DOWNLOADING_FILE:
- addDownloadingFile(intent.getStringExtra(KEY_CMD_ARG));
+ removeObservedFile( (OCFile)intent.getParcelableExtra(KEY_CMD_ARG_FILE),
+ (Account)intent.getParcelableExtra(KEY_CMD_ARG_ACCOUNT));
break;
default:
Log.wtf(TAG, "Incorrect key given");
return Service.START_STICKY;
}
+
+ /**
+ * Read from the local database the list of files that must to be kept synchronized and
+ * starts file observers to monitor local changes on them
+ */
private void initializeObservedList() {
- if (mObservers != null) return; // nothing to do here
- mObservers = new ArrayList<OwnCloudFileObserver>();
- mDownloadReceivers = new ArrayList<DownloadCompletedReceiver>();
+ mObserversMap.clear();
Cursor c = getContentResolver().query(
ProviderTableMeta.CONTENT_URI,
null,
continue;
String path = c.getString(c.getColumnIndex(ProviderTableMeta.FILE_STORAGE_PATH));
+ if (path == null || path.length() <= 0)
+ continue;
OwnCloudFileObserver observer =
- new OwnCloudFileObserver(path, OwnCloudFileObserver.CHANGES_ONLY);
- observer.setContext(getApplicationContext());
- observer.setAccount(account);
- observer.setStorageManager(storage);
- observer.setOCFile(storage.getFileByPath(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_PATH))));
- observer.addObserverStatusListener(this);
- observer.startWatching();
- mObservers.add(observer);
- Log.d(TAG, "Started watching file " + path);
+ new OwnCloudFileObserver( path,
+ account,
+ getApplicationContext(),
+ OwnCloudFileObserver.CHANGES_ONLY);
+ mObserversMap.put(path, observer);
+ if (new File(path).exists()) {
+ observer.startWatching();
+ Log.d(TAG, "Started watching file " + path);
+ }
} while (c.moveToNext());
c.close();
}
- private void addObservedFile(String path) {
- if (path == null) return;
- if (mObservers == null) {
- // this is very rare case when service was killed by system
- // and observers list was deleted in that procedure
- initializeObservedList();
- }
- boolean duplicate = false;
- OwnCloudFileObserver observer = null;
- for (int i = 0; i < mObservers.size(); ++i) {
- observer = mObservers.get(i);
- if (observer.getPath().equals(path))
- duplicate = true;
- observer.setContext(getBaseContext());
- }
- if (duplicate) return;
- observer = new OwnCloudFileObserver(path, OwnCloudFileObserver.CHANGES_ONLY);
- observer.setContext(getBaseContext());
- Account account = AccountUtils.getCurrentOwnCloudAccount(getBaseContext());
- observer.setAccount(account);
- FileDataStorageManager storage =
- new FileDataStorageManager(account, getContentResolver());
- observer.setStorageManager(storage);
- observer.setOCFile(storage.getFileByLocalPath(path));
- observer.addObserverStatusListener(this);
-
- DownloadCompletedReceiver receiver = new DownloadCompletedReceiver(path, observer);
- registerReceiver(receiver, new IntentFilter(FileDownloader.DOWNLOAD_FINISH_MESSAGE));
-
- mObservers.add(observer);
- Log.d(TAG, "Observer added for path " + path);
- }
- private void removeObservedFile(String path) {
- if (path == null) return;
- if (mObservers == null) {
- initializeObservedList();
+ /**
+ * Registers the local copy of a remote file to be observed for local changes,
+ * an automatically updated in the ownCloud server.
+ *
+ * This method does NOT perform a {@link SynchronizeFileOperation} over the file.
+ *
+ * TODO We are ignoring that, currently, a local file can be linked to different files
+ * in ownCloud if it's uploaded several times. That's something pending to update: we
+ * will avoid that the same local file is linked to different remote files.
+ *
+ * @param file Object representing a remote file which local copy must be observed.
+ * @param account OwnCloud account containing file.
+ */
+ private void addObservedFile(OCFile file, Account account) {
+ if (file == null) {
+ Log.e(TAG, "Trying to add a NULL file to observer");
return;
}
- for (int i = 0; i < mObservers.size(); ++i) {
- OwnCloudFileObserver observer = mObservers.get(i);
- if (observer.getPath().equals(path)) {
- observer.stopWatching();
- mObservers.remove(i);
- break;
- }
- }
- Log.d(TAG, "Stopped watching " + path);
- }
-
- private void addDownloadingFile(String remotePath) {
- OwnCloudFileObserver observer = null;
- for (OwnCloudFileObserver o : mObservers) {
- if (o.getRemotePath().equals(remotePath)) {
- observer = o;
- break;
- }
+ String localPath = file.getStoragePath();
+ if (localPath == null || localPath.length() <= 0) { // file downloading / to be download for the first time
+ localPath = FileStorageUtils.getDefaultSavePathFor(account.name, file);
}
+ OwnCloudFileObserver observer = mObserversMap.get(localPath);
if (observer == null) {
- Log.e(TAG, "Couldn't find observer for remote file " + remotePath);
- return;
+ /// the local file was never registered to observe before
+ observer = new OwnCloudFileObserver( localPath,
+ account,
+ getApplicationContext(),
+ OwnCloudFileObserver.CHANGES_ONLY);
+ mObserversMap.put(localPath, observer);
+ Log.d(TAG, "Observer added for path " + localPath);
+
+ if (file.isDown()) {
+ observer.startWatching();
+ Log.d(TAG, "Started watching " + localPath);
+ } // else - the observance can't be started on a file not already down; mDownloadReceiver will get noticed when the download of the file finishes
}
- observer.stopWatching();
- DownloadCompletedReceiver dcr = new DownloadCompletedReceiver(observer.getPath(), observer);
- registerReceiver(dcr, new IntentFilter(FileDownloader.DOWNLOAD_FINISH_MESSAGE));
+
}
- private static void addReceiverToList(DownloadCompletedReceiver r) {
- synchronized(mReceiverListLock) {
- mDownloadReceivers.add(r);
+ /**
+ * Unregisters the local copy of a remote file to be observed for local changes.
+ *
+ * Starts to watch it, if the file has a local copy to watch.
+ *
+ * TODO We are ignoring that, currently, a local file can be linked to different files
+ * in ownCloud if it's uploaded several times. That's something pending to update: we
+ * will avoid that the same local file is linked to different remote files.
+ *
+ * @param file Object representing a remote file which local copy must be not observed longer.
+ * @param account OwnCloud account containing file.
+ */
+ private void removeObservedFile(OCFile file, Account account) {
+ if (file == null) {
+ Log.e(TAG, "Trying to remove a NULL file");
+ return;
}
- }
-
- private static void removeReceiverFromList(DownloadCompletedReceiver r) {
- synchronized(mReceiverListLock) {
- mDownloadReceivers.remove(r);
+ String localPath = file.getStoragePath();
+ if (localPath == null || localPath.length() <= 0) {
+ localPath = FileStorageUtils.getDefaultSavePathFor(account.name, file);
}
- }
-
- @Override
- public void OnObservedFileStatusUpdate(String localPath, String remotePath, Account account, Status status) {
- switch (status) {
- case CONFLICT:
- {
- Intent i = new Intent(getApplicationContext(), ConflictsResolveActivity.class);
- i.setFlags(i.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
- i.putExtra("remotepath", remotePath);
- i.putExtra("localpath", localPath);
- i.putExtra("account", account);
- startActivity(i);
- break;
- }
- case SENDING_TO_UPLOADER:
- case INCORRECT_MASK:
- break;
- default:
- Log.wtf(TAG, "Unhandled status " + status);
+
+ OwnCloudFileObserver observer = mObserversMap.get(localPath);
+ if (observer != null) {
+ observer.stopWatching();
+ mObserversMap.remove(observer);
+ Log.d(TAG, "Stopped watching " + localPath);
}
+
}
- private class DownloadCompletedReceiver extends BroadcastReceiver {
- String mPath;
- OwnCloudFileObserver mObserver;
-
- public DownloadCompletedReceiver(String path, OwnCloudFileObserver observer) {
- mPath = path;
- mObserver = observer;
- addReceiverToList(this);
- }
+
+ /**
+ * Private receiver listening to events broadcast by the FileDownloader service.
+ *
+ * Starts and stops the observance on registered files when they are being download,
+ * in order to avoid to start unnecessary synchronizations.
+ */
+ private class DownloadCompletedReceiverBis extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
- if (mPath.equals(intent.getStringExtra(FileDownloader.EXTRA_FILE_PATH))) {
- context.unregisterReceiver(this);
- removeReceiverFromList(this);
- mObserver.startWatching();
- Log.d(TAG, "Started watching " + mPath);
- return;
+ String downloadPath = intent.getStringExtra(FileDownloader.EXTRA_FILE_PATH);
+ OwnCloudFileObserver observer = mObserversMap.get(downloadPath);
+ if (observer != null) {
+ if (intent.getAction().equals(FileDownloader.DOWNLOAD_FINISH_MESSAGE) &&
+ new File(downloadPath).exists()) { // the download could be successful, or not; in both cases, the file could be down, due to a former download or upload
+ observer.startWatching();
+ Log.d(TAG, "Watching again " + downloadPath);
+
+ } else if (intent.getAction().equals(FileDownloader.DOWNLOAD_ADDED_MESSAGE)) {
+ observer.stopWatching();
+ Log.d(TAG, "Disabling observance of " + downloadPath);
+ }
}
}
- @Override
- public boolean equals(Object o) {
- if (o instanceof DownloadCompletedReceiver)
- return mPath.equals(((DownloadCompletedReceiver)o).mPath);
- return super.equals(o);
- }
}
+
}
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 static final String UPLOAD_FINISH_MESSAGE = "UPLOAD_FINISH";
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_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();
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
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);
+ sendStickyBroadcast(end);
}
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
+import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.http.HttpStatus;
import com.owncloud.android.datamodel.OCFile;
-import com.owncloud.android.files.services.FileDownloader;
import com.owncloud.android.operations.RemoteOperation;
import com.owncloud.android.operations.RemoteOperationResult;
+import com.owncloud.android.utils.FileStorageUtils;
import eu.alefzero.webdav.OnDatatransferProgressListener;
import eu.alefzero.webdav.WebdavClient;
private OCFile mFile;
private Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<OnDatatransferProgressListener>();
private final AtomicBoolean mCancellationRequested = new AtomicBoolean(false);
+ private long mModificationTimestamp = 0;
public DownloadFileOperation(Account account, OCFile file) {
}
public String getSavePath() {
- return FileDownloader.getSavePath(mAccount.name) + mFile.getRemotePath();
+ String path = mFile.getStoragePath(); // re-downloads should be done over the original file
+ if (path != null && path.length() > 0) {
+ return path;
+ }
+ return FileStorageUtils.getDefaultSavePathFor(mAccount.name, mFile);
}
public String getTmpPath() {
- return FileDownloader.getTemporalPath(mAccount.name) + mFile.getRemotePath();
+ return FileStorageUtils.getTemporalPath(mAccount.name) + mFile.getRemotePath();
}
public String getRemotePath() {
}
public String getMimeType() {
- String mimeType = mFile.getMimetype(); // TODO fix the mime types in OCFiles FOREVER
+ String mimeType = mFile.getMimetype();
if (mimeType == null || mimeType.length() <= 0) {
try {
mimeType = MimeTypeMap.getSingleton()
return mFile.getFileLength();
}
+ public long getModificationTimestamp() {
+ return (mModificationTimestamp > 0) ? mModificationTimestamp : mFile.getModificationTimestamp();
+ }
+
public void addDatatransferProgressListener (OnDatatransferProgressListener listener) {
mDataTransferListeners.add(listener);
}
}
savedFile = true;
+ Header modificationTime = get.getResponseHeader("Last-Modified");
+ if (modificationTime != null) {
+ Date d = WebdavUtils.parseResponseDate((String) modificationTime.getValue());
+ mModificationTimestamp = (d != null) ? d.getTime() : 0;
+ }
} else {
client.exhaustResponse(get.getResponseBodyAsStream());
*/
public class RemoteOperationResult implements Serializable {
- /** Generated - to refresh every time the class changes */
- private static final long serialVersionUID = -7805531062432602444L;
+ /** Generated - should be refreshed every time the class changes!! */
+ private static final long serialVersionUID = 5336333154035462033L;
public enum ResultCode {
CANCELLED,
INVALID_LOCAL_FILE_NAME,
INVALID_OVERWRITE,
- CONFLICT
+ CONFLICT,
+ SYNC_CONFLICT
}
private boolean mSuccess = false;
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 void saveLocalDirectory() {
mStorageManager.moveDirectory(mFile, mNewRemotePath);
- String localPath = FileDownloader.getSavePath(mAccount.name) + mFile.getRemotePath();
+ String localPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, mFile);
File localDir = new File(localPath);
if (localDir.exists()) {
- localDir.renameTo(new File(FileDownloader.getSavePath(mAccount.name) + mNewRemotePath));
+ localDir.renameTo(new File(FileStorageUtils.getSavePath(mAccount.name) + mNewRemotePath));
// TODO - if renameTo fails, children files that are already down will result unlinked
}
}
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
}
-
- // 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 android.accounts.Account;
import android.content.Context;
+import android.content.Intent;
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.files.services.FileUploader;
+import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
import eu.alefzero.webdav.WebdavClient;
import eu.alefzero.webdav.WebdavEntry;
public class SynchronizeFileOperation extends RemoteOperation {
private String TAG = SynchronizeFileOperation.class.getSimpleName();
+ private static final int SYNC_READ_TIMEOUT = 10000;
+ private static final int SYNC_CONNECTION_TIMEOUT = 5000;
- private String mRemotePath;
-
+ private OCFile mLocalFile;
+ private OCFile mServerFile;
private DataStorageManager mStorageManager;
-
private Account mAccount;
+ private boolean mSyncFileContents;
+ private boolean mLocalChangeAlreadyKnown;
+ private Context mContext;
+
+ private boolean mTransferWasRequested = false;
public SynchronizeFileOperation(
- String remotePath,
- DataStorageManager dataStorageManager,
+ OCFile localFile,
+ OCFile serverFile, // make this null to let the operation checks the server; added to reuse info from SynchronizeFolderOperation
+ DataStorageManager storageManager,
Account account,
- Context context ) {
- mRemotePath = remotePath;
- mStorageManager = dataStorageManager;
+ boolean syncFileContents,
+ boolean localChangeAlreadyKnown,
+ Context context) {
+
+ mLocalFile = localFile;
+ mServerFile = serverFile;
+ mStorageManager = storageManager;
mAccount = account;
+ mSyncFileContents = syncFileContents;
+ mLocalChangeAlreadyKnown = localChangeAlreadyKnown;
+ mContext = context;
}
+
@Override
protected RemoteOperationResult run(WebdavClient client) {
+
PropFindMethod propfind = null;
RemoteOperationResult result = null;
+ mTransferWasRequested = false;
try {
- propfind = new PropFindMethod(client.getBaseUri() + WebdavUtils.encodePath(mRemotePath));
- int status = client.executeMethod(propfind);
- boolean isMultiStatus = status == HttpStatus.SC_MULTI_STATUS;
- Boolean isConflict = Boolean.FALSE;
- if (isMultiStatus) {
- MultiStatus resp = propfind.getResponseBodyAsMultiStatus();
- WebdavEntry we = new WebdavEntry(resp.getResponses()[0],
+ if (!mLocalFile.isDown()) {
+ /// easy decision
+ requestForDownload(mLocalFile);
+ result = new RemoteOperationResult(ResultCode.OK);
+
+ } else {
+ /// local copy in the device -> need to think a bit more before do anything
+
+ if (mServerFile == null) {
+ /// take the duty of check the server for the current state of the file there
+ propfind = new PropFindMethod(client.getBaseUri() + WebdavUtils.encodePath(mLocalFile.getRemotePath()));
+ int status = client.executeMethod(propfind, SYNC_READ_TIMEOUT, SYNC_CONNECTION_TIMEOUT);
+ boolean isMultiStatus = status == HttpStatus.SC_MULTI_STATUS;
+ if (isMultiStatus) {
+ MultiStatus resp = propfind.getResponseBodyAsMultiStatus();
+ WebdavEntry we = new WebdavEntry(resp.getResponses()[0],
client.getBaseUri().getPath());
- OCFile file = fillOCFile(we);
- OCFile oldFile = mStorageManager.getFileByPath(file.getRemotePath());
- if (oldFile.getFileLength() != file.getFileLength() ||
- oldFile.getModificationTimestamp() != file.getModificationTimestamp()) {
- isConflict = Boolean.TRUE;
- }
+ mServerFile = fillOCFile(we);
+ mServerFile.setLastSyncDateForProperties(System.currentTimeMillis());
+
+ } else {
+ client.exhaustResponse(propfind.getResponseBodyAsStream());
+ result = new RemoteOperationResult(false, status);
+ }
+ }
+
+ if (result == null) { // true if the server was not checked, or nothing was wrong with the remote request
+
+ /// check changes in server and local file
+ boolean serverChanged = false;
+ if (mServerFile.getEtag() != null) {
+ serverChanged = (!mServerFile.getEtag().equals(mLocalFile.getEtag())); // TODO could this be dangerous when the user upgrades the server from non-tagged to tagged?
+ } else {
+ // server without etags
+ serverChanged = (mServerFile.getModificationTimestamp() > mLocalFile.getModificationTimestamp());
+ }
+ boolean localChanged = (mLocalChangeAlreadyKnown || mLocalFile.getLocalModificationTimestamp() > mLocalFile.getLastSyncDateForData());
+ // TODO this will be always true after the app is upgraded to database version 3; will result in unnecessary uploads
+
+ /// decide action to perform depending upon changes
+ if (localChanged && serverChanged) {
+ result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT);
+
+ } else if (localChanged) {
+ if (mSyncFileContents) {
+ requestForUpload(mLocalFile);
+ // the local update of file properties will be done by the FileUploader service when the upload finishes
+ } else {
+ // NOTHING TO DO HERE: updating the properties of the file in the server without uploading the contents would be stupid;
+ // So, an instance of SynchronizeFileOperation created with syncFileContents == false is completely useless when we suspect
+ // that an upload is necessary (for instance, in FileObserverService).
+ }
+ result = new RemoteOperationResult(ResultCode.OK);
+
+ } else if (serverChanged) {
+ if (mSyncFileContents) {
+ requestForDownload(mLocalFile); // local, not server; we won't to keep the value of keepInSync!
+ // the update of local data will be done later by the FileUploader service when the upload finishes
+ } else {
+ // TODO CHECK: is this really useful in some point in the code?
+ mServerFile.setKeepInSync(mLocalFile.keepInSync());
+ mServerFile.setLastSyncDateForData(mLocalFile.getLastSyncDateForData());
+ mServerFile.setStoragePath(mLocalFile.getStoragePath());
+ mServerFile.setParentId(mLocalFile.getParentId());
+ mStorageManager.saveFile(mServerFile);
+
+ }
+ result = new RemoteOperationResult(ResultCode.OK);
- } else {
- client.exhaustResponse(propfind.getResponseBodyAsStream());
- }
+ } else {
+ // nothing changed, nothing to do
+ result = new RemoteOperationResult(ResultCode.OK);
+ }
+
+ }
+
+ }
+
+ Log.i(TAG, "Synchronizing " + mAccount.name + ", file " + mLocalFile.getRemotePath() + ": " + result.getLogMessage());
- result = new RemoteOperationResult(isMultiStatus, status);
- result.setExtraData(isConflict);
- Log.i(TAG, "Synchronizing " + mAccount.name + ", file " + mRemotePath + ": " + result.getLogMessage());
} catch (Exception e) {
result = new RemoteOperationResult(e);
- Log.e(TAG, "Synchronizing " + mAccount.name + ", file " + mRemotePath + ": " + result.getLogMessage(), result.getException());
+ Log.e(TAG, "Synchronizing " + mAccount.name + ", file " + mLocalFile.getRemotePath() + ": " + result.getLogMessage(), result.getException());
} finally {
if (propfind != null)
}
return result;
}
+
/**
+ * Requests for an upload to the FileUploader service
+ *
+ * @param file OCFile object representing the file to upload
+ */
+ private void requestForUpload(OCFile file) {
+ Intent i = new Intent(mContext, FileUploader.class);
+ i.putExtra(FileUploader.KEY_ACCOUNT, mAccount);
+ i.putExtra(FileUploader.KEY_FILE, file);
+ /*i.putExtra(FileUploader.KEY_REMOTE_FILE, mRemotePath); // doing this we would lose the value of keepInSync in the road, and maybe it's not updated in the database when the FileUploader service gets it!
+ i.putExtra(FileUploader.KEY_LOCAL_FILE, localFile.getStoragePath());*/
+ i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
+ i.putExtra(FileUploader.KEY_FORCE_OVERWRITE, true);
+ mContext.startService(i);
+ mTransferWasRequested = true;
+ }
+
+
+ /**
+ * Requests for a download to the FileDownloader service
+ *
+ * @param file OCFile object representing the file to download
+ */
+ private void requestForDownload(OCFile file) {
+ Intent i = new Intent(mContext, FileDownloader.class);
+ i.putExtra(FileDownloader.EXTRA_ACCOUNT, mAccount);
+ i.putExtra(FileDownloader.EXTRA_FILE, file);
+ mContext.startService(i);
+ mTransferWasRequested = true;
+ }
+
+
+ /**
* Creates and populates a new {@link OCFile} object with the data read from the server.
*
* @param we WebDAV entry read from the server for a WebDAV resource (remote file or folder).
file.setFileLength(we.contentLength());
file.setMimetype(we.contentType());
file.setModificationTimestamp(we.modifiedTimesamp());
- file.setLastSyncDate(System.currentTimeMillis());
return file;
}
+
+ public boolean transferWasRequested() {
+ return mTransferWasRequested;
+ }
+
}
package com.owncloud.android.operations;
+import java.io.File;
import java.util.List;
import java.util.Vector;
import android.accounts.Account;
import android.content.Context;
-import android.content.Intent;
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.files.services.FileObserverService;
+import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
+import com.owncloud.android.utils.FileStorageUtils;
import eu.alefzero.webdav.WebdavClient;
import eu.alefzero.webdav.WebdavEntry;
/** Files and folders contained in the synchronized folder */
private List<OCFile> mChildren;
+
+ private int mConflictsFound;
+
+ private int mFailsInFavouritesFound;
public SynchronizeFolderOperation( String remotePath,
}
+ public int getConflictsFound() {
+ return mConflictsFound;
+ }
+
+ public int getFailsInFavouritesFound() {
+ return mFailsInFavouritesFound;
+ }
+
/**
* Returns the list of files and folders contained in the synchronized folder, if called after synchronization is complete.
*
@Override
protected RemoteOperationResult run(WebdavClient client) {
RemoteOperationResult result = null;
+ mFailsInFavouritesFound = 0;
+ mConflictsFound = 0;
// code before in FileSyncAdapter.fetchData
PropFindMethod query = null;
if (mParentId == DataStorageManager.ROOT_PARENT_ID) {
WebdavEntry we = new WebdavEntry(resp.getResponses()[0], client.getBaseUri().getPath());
OCFile parent = fillOCFile(we);
- parent.setParentId(mParentId);
mStorageManager.saveFile(parent);
mParentId = parent.getFileId();
}
// read contents in folder
List<OCFile> updatedFiles = new Vector<OCFile>(resp.getResponses().length - 1);
+ List<SynchronizeFileOperation> filesToSyncContents = new Vector<SynchronizeFileOperation>();
for (int i = 1; i < resp.getResponses().length; ++i) {
+ /// new OCFile instance with the data from the server
WebdavEntry we = new WebdavEntry(resp.getResponses()[i], client.getBaseUri().getPath());
OCFile file = fillOCFile(we);
- file.setParentId(mParentId);
+
+ /// set data about local state, keeping unchanged former data if existing
+ file.setLastSyncDateForProperties(mCurrentSyncTime);
OCFile oldFile = mStorageManager.getFileByPath(file.getRemotePath());
if (oldFile != null) {
- if (oldFile.keepInSync() && file.getModificationTimestamp() > oldFile.getModificationTimestamp()) {
- disableObservance(file); // first disable observer so we won't get file upload right after download
- requestContentDownload(file);
- }
file.setKeepInSync(oldFile.keepInSync());
+ file.setLastSyncDateForData(oldFile.getLastSyncDateForData());
+ file.setStoragePath(oldFile.getStoragePath());
+ }
+
+ /// scan default location if local copy of file is not linked in OCFile instance
+ if (file.getStoragePath() == null && !file.isDirectory()) {
+ File f = new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file));
+ if (f.exists()) {
+ file.setStoragePath(f.getAbsolutePath());
+ file.setLastSyncDateForData(f.lastModified());
+ }
+ }
+
+ /// prepare content synchronization for kept-in-sync files
+ if (file.keepInSync()) {
+ SynchronizeFileOperation operation = new SynchronizeFileOperation( oldFile,
+ file,
+ mStorageManager,
+ mAccount,
+ true,
+ false,
+ mContext
+ );
+ filesToSyncContents.add(operation);
}
updatedFiles.add(file);
// save updated contents in local database; all at once, trying to get a best performance in database update (not a big deal, indeed)
mStorageManager.saveFiles(updatedFiles);
-
+ // request for the synchronization of files AFTER saving last properties
+ SynchronizeFileOperation op = null;
+ RemoteOperationResult contentsResult = null;
+ for (int i=0; i < filesToSyncContents.size(); i++) {
+ op = filesToSyncContents.get(i);
+ contentsResult = op.execute(client); // returns without waiting for upload or download finishes
+ if (!contentsResult.isSuccess()) {
+ if (contentsResult.getCode() == ResultCode.SYNC_CONFLICT) {
+ mConflictsFound++;
+ } else {
+ mFailsInFavouritesFound++;
+ if (contentsResult.getException() != null) {
+ Log.d(TAG, "Error while synchronizing favourites : " + contentsResult.getLogMessage(), contentsResult.getException());
+ } else {
+ Log.d(TAG, "Error while synchronizing favourites : " + contentsResult.getLogMessage());
+ }
+ }
+ } // won't let these fails break the synchronization process
+ }
+
+
// removal of obsolete files
mChildren = mStorageManager.getDirectoryContent(mStorageManager.getFileById(mParentId));
OCFile file;
- String currentSavePath = FileDownloader.getSavePath(mAccount.name);
+ String currentSavePath = FileStorageUtils.getSavePath(mAccount.name);
for (int i=0; i < mChildren.size(); ) {
file = mChildren.get(i);
- if (file.getLastSyncDate() != mCurrentSyncTime) {
+ if (file.getLastSyncDateForProperties() != mCurrentSyncTime) {
Log.d(TAG, "removing file: " + file);
mStorageManager.removeFile(file, (file.isDown() && file.getStoragePath().startsWith(currentSavePath)));
mChildren.remove(i);
}
// prepare result object
- result = new RemoteOperationResult(isMultiStatus(status), status);
+ if (isMultiStatus(status)) {
+ if (mConflictsFound > 0 || mFailsInFavouritesFound > 0) {
+ result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT); // should be different result, but will do the job
+
+ } else {
+ result = new RemoteOperationResult(true, status);
+ }
+ } else {
+ result = new RemoteOperationResult(false, status);
+ }
Log.i(TAG, "Synchronizing " + mAccount.name + ", folder " + mRemotePath + ": " + result.getLogMessage());
file.setFileLength(we.contentLength());
file.setMimetype(we.contentType());
file.setModificationTimestamp(we.modifiedTimesamp());
- file.setLastSyncDate(mCurrentSyncTime);
+ file.setParentId(mParentId);
return file;
}
-
- /**
- * Request to stop the observance of local updates for a file.
- *
- * @param file OCFile representing the remote file to stop to monitor for local updates
- */
- private void disableObservance(OCFile file) {
- Log.d(TAG, "Disabling observation of remote file" + file.getRemotePath());
- Intent intent = new Intent(mContext, FileObserverService.class);
- intent.putExtra(FileObserverService.KEY_FILE_CMD, FileObserverService.CMD_ADD_DOWNLOADING_FILE);
- intent.putExtra(FileObserverService.KEY_CMD_ARG, file.getRemotePath());
- mContext.startService(intent);
-
- }
-
-
- /**
- * Requests a download to the file download service
- *
- * @param file OCFile representing the remote file to download
- */
- private void requestContentDownload(OCFile file) {
- Intent intent = new Intent(mContext, FileDownloader.class);
- intent.putExtra(FileDownloader.EXTRA_ACCOUNT, mAccount);
- intent.putExtra(FileDownloader.EXTRA_FILE, file);
- mContext.startService(intent);
- }
-
}
*/
public class UpdateOCVersionOperation extends RemoteOperation {
- private static final String TAG = UploadFileOperation.class.getSimpleName();
+ private static final String TAG = UpdateOCVersionOperation.class.getSimpleName();
private Account mAccount;
private Context mContext;
private Account mAccount;
private OCFile mFile;
+ private OCFile mOldFile;
private String mRemotePath = null;
private boolean mIsInstant = false;
private boolean mRemoteFolderToBeCreated = false;
private boolean mForceOverwrite = false;
+ private boolean mWasRenamed = false;
PutMethod mPutMethod = null;
private Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<OnDatatransferProgressListener>();
private final AtomicBoolean mCancellationRequested = new AtomicBoolean(false);
+
public UploadFileOperation( Account account,
OCFile file,
return mFile;
}
+ public OCFile getOldFile() {
+ return mOldFile;
+ }
+
public String getStoragePath() {
return mFile.getStoragePath();
}
public String getRemotePath() {
- //return mFile.getRemotePath(); // DON'T MAKE THIS ; the remotePath used can be different to mFile.getRemotePath() if mForceOverwrite is 'false'; see run(...)
- return mRemotePath;
+ return mFile.getRemotePath();
}
public String getMimeType() {
return mForceOverwrite;
}
+ public boolean wasRenamed() {
+ return mWasRenamed;
+ }
public Set<OnDatatransferProgressListener> getDataTransferListeners() {
return mDataTransferListeners;
try {
/// rename the file to upload, if necessary
if (!mForceOverwrite) {
- mRemotePath = getAvailableRemotePath(client, mRemotePath);
+ String remotePath = getAvailableRemotePath(client, mRemotePath);
+ mWasRenamed = !remotePath.equals(mRemotePath);
+ if (mWasRenamed) {
+ createNewOCFile(remotePath);
+ }
}
/// perform the upload
}
+ private void createNewOCFile(String newRemotePath) {
+ // a new OCFile instance must be created for a new remote path
+ OCFile newFile = new OCFile(newRemotePath);
+ newFile.setCreationTimestamp(mFile.getCreationTimestamp());
+ newFile.setFileLength(mFile.getFileLength());
+ newFile.setMimetype(mFile.getMimetype());
+ newFile.setModificationTimestamp(mFile.getModificationTimestamp());
+ // newFile.setEtag(mFile.getEtag())
+ newFile.setKeepInSync(mFile.keepInSync());
+ newFile.setLastSyncDateForProperties(mFile.getLastSyncDateForProperties());
+ newFile.setLastSyncDateForData(mFile.getLastSyncDateForData());
+ newFile.setStoragePath(mFile.getStoragePath());
+ newFile.setParentId(mFile.getParentId());
+ mOldFile = mFile;
+ mFile = newFile;
+ }
+
+
public boolean isSuccess(int status) {
return ((status == HttpStatus.SC_OK || status == HttpStatus.SC_CREATED || status == HttpStatus.SC_NO_CONTENT));
}
ProviderTableMeta.FILE_STORAGE_PATH);\r
mProjectionMap.put(ProviderTableMeta.FILE_LAST_SYNC_DATE,\r
ProviderTableMeta.FILE_LAST_SYNC_DATE);\r
+ mProjectionMap.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA,\r
+ ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA);\r
mProjectionMap.put(ProviderTableMeta.FILE_KEEP_IN_SYNC,\r
ProviderTableMeta.FILE_KEEP_IN_SYNC);\r
mProjectionMap.put(ProviderTableMeta.FILE_ACCOUNT_OWNER,\r
+ ProviderTableMeta.FILE_STORAGE_PATH + " TEXT, "\r
+ ProviderTableMeta.FILE_ACCOUNT_OWNER + " TEXT, "\r
+ ProviderTableMeta.FILE_LAST_SYNC_DATE + " INTEGER, "\r
- + ProviderTableMeta.FILE_KEEP_IN_SYNC + " INTEGER );");\r
+ + ProviderTableMeta.FILE_KEEP_IN_SYNC + " INTEGER, "\r
+ + ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA + " INTEGER );"\r
+ );\r
}\r
\r
@Override\r
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {\r
Log.i("SQL", "Entering in onUpgrade");\r
+ boolean upgraded = false; \r
if (oldVersion == 1 && newVersion >= 2) {\r
- Log.i("SQL", "Entering in the ADD in onUpgrade");\r
+ Log.i("SQL", "Entering in the #1 ADD in onUpgrade");\r
db.execSQL("ALTER TABLE " + ProviderTableMeta.DB_NAME +\r
" ADD COLUMN " + ProviderTableMeta.FILE_KEEP_IN_SYNC + " INTEGER " +\r
" DEFAULT 0");\r
- } else Log.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion + ", newVersion == " + newVersion);\r
+ upgraded = true;\r
+ }\r
+ if (oldVersion < 3 && newVersion >= 3) {\r
+ Log.i("SQL", "Entering in the #2 ADD in onUpgrade");\r
+ db.execSQL("ALTER TABLE " + ProviderTableMeta.DB_NAME +\r
+ " ADD COLUMN " + ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA + " INTEGER " +\r
+ " DEFAULT 0");\r
+ upgraded = true;\r
+ }\r
+ if (!upgraded)\r
+ Log.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion + ", newVersion == " + newVersion);\r
}\r
\r
}\r
import com.owncloud.android.datamodel.DataStorageManager;\r
import com.owncloud.android.datamodel.FileDataStorageManager;\r
import com.owncloud.android.datamodel.OCFile;\r
-//<<<<<<< HEAD
import com.owncloud.android.operations.RemoteOperationResult;\r
import com.owncloud.android.operations.SynchronizeFolderOperation;\r
import com.owncloud.android.operations.UpdateOCVersionOperation;\r
-/*=======
-import com.owncloud.android.files.services.FileDownloader;\r
-import com.owncloud.android.files.services.FileObserverService;\r
-import com.owncloud.android.utils.OwnCloudVersion;\r
->>>>>>> origin/master*/
+import com.owncloud.android.operations.RemoteOperationResult.ResultCode;\r
\r
import android.accounts.Account;\r
import android.app.Notification;\r
private int mFailedResultsCounter; \r
private RemoteOperationResult mLastFailedResult;\r
private SyncResult mSyncResult;\r
+ private int mConflictsFound;\r
+ private int mFailsInFavouritesFound;\r
\r
public FileSyncAdapter(Context context, boolean autoInitialize) {\r
super(context, autoInitialize);\r
mIsManualSync = extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);\r
mFailedResultsCounter = 0;\r
mLastFailedResult = null;\r
+ mConflictsFound = 0;\r
+ mFailsInFavouritesFound = 0;\r
mSyncResult = syncResult;\r
- \r
+ mSyncResult.fullSyncRequested = false;\r
+ mSyncResult.delayUntil = 60*60*24; // sync after 24h\r
+\r
this.setAccount(account);\r
this.setContentProvider(provider);\r
this.setStorageManager(new FileDataStorageManager(account, getContentProvider()));\r
\r
/// notify the user about the failure of MANUAL synchronization\r
notifyFailedSynchronization();\r
+ \r
+ } else if (mConflictsFound > 0 || mFailsInFavouritesFound > 0) {\r
+ notifyFailsInFavourites();\r
}\r
sendStickyBroadcast(false, null, mLastFailedResult); // message to signal the end to the UI\r
}\r
\r
}\r
- \r
- \r
+\r
\r
/**\r
* Called by system SyncManager when a synchronization is required to be cancelled.\r
// synchronized folder -> notice to UI - ALWAYS, although !result.isSuccess\r
sendStickyBroadcast(true, remotePath, null);\r
\r
- if (result.isSuccess()) {\r
+ if (result.isSuccess() || result.getCode() == ResultCode.SYNC_CONFLICT) {\r
+ \r
+ if (result.getCode() == ResultCode.SYNC_CONFLICT) {\r
+ mConflictsFound += synchFolderOp.getConflictsFound();\r
+ mFailsInFavouritesFound += synchFolderOp.getFailsInFavouritesFound();\r
+ }\r
// synchronize children folders \r
List<OCFile> children = synchFolderOp.getChildren();\r
fetchChildren(children); // beware of the 'hidden' recursion here!\r
\r
-//<<<<<<< HEAD
} else {\r
if (result.getCode() == RemoteOperationResult.ResultCode.UNAUTHORIZED) {\r
mSyncResult.stats.numAuthExceptions++;\r
\r
} else if (result.getException() instanceof IOException) { \r
mSyncResult.stats.numIoExceptions++;\r
-/*=======
- // insertion or update of files\r
- List<OCFile> updatedFiles = new Vector<OCFile>(resp.getResponses().length - 1);\r
- for (int i = 1; i < resp.getResponses().length; ++i) {\r
- WebdavEntry we = new WebdavEntry(resp.getResponses()[i], getUri().getPath());\r
- OCFile file = fillOCFile(we);\r
- file.setParentId(parentId);\r
- if (getStorageManager().getFileByPath(file.getRemotePath()) != null &&\r
- getStorageManager().getFileByPath(file.getRemotePath()).keepInSync() &&\r
- file.getModificationTimestamp() > getStorageManager().getFileByPath(file.getRemotePath())\r
- .getModificationTimestamp()) {\r
- // first disable observer so we won't get file upload right after download\r
- Log.d(TAG, "Disabling observation of remote file" + file.getRemotePath());\r
- Intent intent = new Intent(getContext(), FileObserverService.class);\r
- intent.putExtra(FileObserverService.KEY_FILE_CMD, FileObserverService.CMD_ADD_DOWNLOADING_FILE);\r
- intent.putExtra(FileObserverService.KEY_CMD_ARG, file.getRemotePath());\r
- getContext().startService(intent);\r
- intent = new Intent(this.getContext(), FileDownloader.class);\r
- intent.putExtra(FileDownloader.EXTRA_ACCOUNT, getAccount());\r
- intent.putExtra(FileDownloader.EXTRA_FILE, file);\r
- file.setKeepInSync(true);\r
- getContext().startService(intent);\r
- }\r
- if (getStorageManager().getFileByPath(file.getRemotePath()) != null)\r
- file.setKeepInSync(getStorageManager().getFileByPath(file.getRemotePath()).keepInSync());\r
->>>>>>> origin/master*/
- \r
}\r
mFailedResultsCounter++;\r
mLastFailedResult = result;\r
((NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE)).notify(R.string.sync_fail_ticker, notification);\r
}\r
\r
- \r
+\r
+ /**\r
+ * Notifies the user about conflicts and strange fails when trying to synchronize the contents of favourite files.\r
+ * \r
+ * By now, we won't consider a failed synchronization.\r
+ */\r
+ private void notifyFailsInFavourites() {\r
+ if (mFailedResultsCounter > 0) {\r
+ Notification notification = new Notification(R.drawable.icon, getContext().getString(R.string.sync_fail_in_favourites_ticker), System.currentTimeMillis());\r
+ notification.flags |= Notification.FLAG_AUTO_CANCEL;\r
+ // TODO put something smart in the contentIntent below\r
+ notification.contentIntent = PendingIntent.getActivity(getContext().getApplicationContext(), (int)System.currentTimeMillis(), new Intent(), 0);\r
+ notification.setLatestEventInfo(getContext().getApplicationContext(), \r
+ getContext().getString(R.string.sync_fail_in_favourites_ticker), \r
+ String.format(getContext().getString(R.string.sync_fail_in_favourites_content), mFailedResultsCounter + mConflictsFound, mConflictsFound), \r
+ notification.contentIntent);\r
+ ((NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE)).notify(R.string.sync_fail_in_favourites_ticker, notification);\r
+ \r
+ } else {\r
+ Notification notification = new Notification(R.drawable.icon, getContext().getString(R.string.sync_conflicts_in_favourites_ticker), System.currentTimeMillis());\r
+ notification.flags |= Notification.FLAG_AUTO_CANCEL;\r
+ // TODO put something smart in the contentIntent below\r
+ notification.contentIntent = PendingIntent.getActivity(getContext().getApplicationContext(), (int)System.currentTimeMillis(), new Intent(), 0);\r
+ notification.setLatestEventInfo(getContext().getApplicationContext(), \r
+ getContext().getString(R.string.sync_conflicts_in_favourites_ticker), \r
+ String.format(getContext().getString(R.string.sync_conflicts_in_favourites_content), mConflictsFound), \r
+ notification.contentIntent);\r
+ ((NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE)).notify(R.string.sync_conflicts_in_favourites_ticker, notification);\r
+ } \r
+ }\r
\r
}\r
onFocusChange(findViewById(R.id.host_URL), false);\r
} else if (v.getId() == R.id.viewPassword) {\r
TextView view = (TextView) findViewById(R.id.account_password);\r
- int input_type = InputType.TYPE_CLASS_TEXT\r
- | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD;\r
+ int input_type = view.getInputType();\r
+ if ((input_type & InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) == InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) {\r
+ input_type = InputType.TYPE_CLASS_TEXT\r
+ | InputType.TYPE_TEXT_VARIATION_PASSWORD;\r
+ } else {\r
+ input_type = InputType.TYPE_CLASS_TEXT\r
+ | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD;\r
+ }\r
view.setInputType(input_type);\r
}\r
}\r
package com.owncloud.android.ui.activity;
import com.actionbarsherlock.app.SherlockFragmentActivity;
+import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.files.services.FileUploader;
import com.owncloud.android.ui.dialog.ConflictsResolveDialog;
import com.owncloud.android.ui.dialog.ConflictsResolveDialog.Decision;
*/
public class ConflictsResolveActivity extends SherlockFragmentActivity implements OnConflictDecisionMadeListener {
+ public static final String EXTRA_FILE = "FILE";
+ public static final String EXTRA_ACCOUNT = "ACCOUNT";
+
private String TAG = ConflictsResolveActivity.class.getSimpleName();
- private String mRemotePath;
+ //private String mRemotePath;
- private String mLocalPath;
+ //private String mLocalPath;
+ private OCFile mFile;
private Account mOCAccount;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mRemotePath = getIntent().getStringExtra("remotepath");
- mLocalPath = getIntent().getStringExtra("localpath");
- mOCAccount = getIntent().getParcelableExtra("account");
- ConflictsResolveDialog d = ConflictsResolveDialog.newInstance(mRemotePath, this);
+
+ //mRemotePath = getIntent().getStringExtra("remotepath");
+ //mLocalPath = getIntent().getStringExtra("localpath");
+ mFile = getIntent().getParcelableExtra(EXTRA_FILE);
+ mOCAccount = getIntent().getParcelableExtra(EXTRA_ACCOUNT);
+ ConflictsResolveDialog d = ConflictsResolveDialog.newInstance(mFile.getRemotePath(), this);
d.showDialog(this);
}
switch (decision) {
case CANCEL:
+ finish();
return;
case OVERWRITE:
i.putExtra(FileUploader.KEY_FORCE_OVERWRITE, true);
return;
}
i.putExtra(FileUploader.KEY_ACCOUNT, mOCAccount);
- i.putExtra(FileUploader.KEY_REMOTE_FILE, mRemotePath);
- i.putExtra(FileUploader.KEY_LOCAL_FILE, mLocalPath);
+ //i.putExtra(FileUploader.KEY_REMOTE_FILE, mRemotePath);
+ //i.putExtra(FileUploader.KEY_LOCAL_FILE, mLocalPath);
+ i.putExtra(FileUploader.KEY_FILE, mFile);
i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
startService(i);
}
}
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ mListener.ConflictDecisionMade(Decision.CANCEL);
+ }
+
public interface OnConflictDecisionMadeListener {
public void ConflictDecisionMade(Decision decision);
}
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
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
@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(false);\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
\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(false); // it updates the buttons; must be called although !downloadWasFine\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(false); // it updates the buttons; must be called although !uploadWasFine; interrupted uploads still leave an incomplete file in the server\r
}\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
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 Bartek Przybylski
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package com.owncloud.android.utils;
+
+import java.io.File;
+
+import android.net.Uri;
+import android.os.Environment;
+import com.owncloud.android.datamodel.OCFile;
+
+
+public class FileStorageUtils {
+
+ public static final String getSavePath(String accountName) {
+ File sdCard = Environment.getExternalStorageDirectory();
+ return sdCard.getAbsolutePath() + "/owncloud/" + Uri.encode(accountName, "@");
+ // 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
+ }
+
+ public static final String getDefaultSavePathFor(String accountName, OCFile file) {
+ return getSavePath(accountName) + file.getRemotePath();
+ }
+
+ public static final String getTemporalPath(String accountName) {
+ File sdCard = Environment.getExternalStorageDirectory();
+ return sdCard.getAbsolutePath() + "/owncloud/tmp/" + Uri.encode(accountName, "@");
+ // 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
+ }
+
+
+}
\ No newline at end of file