android:id="@+id/user_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:ems="10" >
+ android:ems="10"
+ android:inputType="textNoSuggestions"
+ >
- <requestFocus />
</EditText>
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:gravity="center_horizontal" >
-
- <Button
- android:id="@+id/cancel"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/common_cancel" />
-
- <Button
- android:id="@+id/ok"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/common_ok" />
-
- </LinearLayout>
-
</LinearLayout>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:id="@+id/open_file_item"
+ android:title="@string/filedetails_open"
+ android:icon="@android:drawable/ic_menu_edit"
+ />
+
+ <item android:id="@+id/download_file_item"
+ android:title="@string/filedetails_download"
+ />
+
+ <item android:id="@+id/cancel_download_item"
+ android:title="@string/common_cancel_download"
+ android:icon="@android:drawable/ic_menu_close_clear_cancel"
+ />
+
+ <item android:id="@+id/cancel_upload_item"
+ android:title="@string/common_cancel_upload"
+ android:icon="@android:drawable/ic_menu_close_clear_cancel"
+ />
+
+ <item android:id="@+id/rename_file_item"
+ android:title="@string/common_rename"
+ android:icon="@android:drawable/ic_menu_set_as"
+ />
+
+ <item android:id="@+id/remove_file_item"
+ android:title="@string/common_remove"
+ android:icon="@android:drawable/ic_menu_delete"
+ />
+
+</menu>
--- /dev/null
+<!-- Large screen boolean values -->
+<resources>
+ <bool name="large_layout">true</bool>
+</resources>
\ No newline at end of file
--- /dev/null
+<!-- Default boolean values -->
+<resources>
+ <bool name="large_layout">false</bool>
+</resources>
\ No newline at end of file
<string name="common_yes">Yes</string>
<string name="common_no">No</string>
<string name="common_ok">OK</string>
- <string name="common_cancel">Cancel</string>
+ <string name="common_cancel_download">Cancel download</string>
+ <string name="common_cancel_upload">Cancel upload</string>
+ <string name="common_cancel">Cancel</string>
<string name="common_save_exit">Save & Exit</string>
<string name="common_exit">Leave ownCloud</string>
<string name="common_error">Error</string>
<string name="uploader_upload_failed_content_multiple">Upload failed: %1$d/%2$d files were upload</string>
<string name="downloader_download_in_progress_ticker">Downloading …</string>
<string name="downloader_download_in_progress_content">%1$d%% Downloading %2$s</string>
- <string name="downloader_download_succeeded_ticker">Download suceeded</string>
+ <string name="downloader_download_succeeded_ticker">Download succeeded</string>
<string name="downloader_download_succeeded_content">%1$s was successfully download</string>
<string name="downloader_download_failed_ticker">Download failed</string>
<string name="downloader_download_failed_content">Download of %1$s could not be completed</string>
<string name="common_rename">Rename</string>
<string name="common_remove">Remove</string>
- <string name="confirmation_remove_alert">"Do you really want to remove %1$s ?"</string>
- <string name="confirmation_remove_local">Local only</string>
- <string name="confirmation_remove_remote">Remove from server</string>
- <string name="confirmation_remove_remote_and_local">Remote and local</string>
+ <string name="confirmation_remove_alert">"Do you really want to remove %1$s ?"</string>
+ <string name="confirmation_remove_folder_alert">"Do you really want to remove %1$s and its contents ?"</string>
+ <string name="confirmation_remove_local">Local only</string>
+ <string name="confirmation_remove_folder_local">Local contents only</string>
+ <string name="confirmation_remove_remote">Remove from server</string>
+ <string name="confirmation_remove_remote_and_local">Remote and local</string>
<string name="remove_success_msg">"Successful removal"</string>
<string name="remove_fail_msg">"Removal could not be completed"</string>
+ <string name="rename_dialog_title">Enter a new name</string>
<string name="rename_local_fail_msg">"Local copy could not be renamed; try a differente new name"</string>
<string name="rename_server_fail_msg">"Rename could not be completed"</string>
public Vector<OCFile> getDirectoryContent(OCFile f);
public void removeFile(OCFile file, boolean removeLocalCopy);
+
+ public void removeDirectory(OCFile dir, boolean removeDBData, boolean removeLocalContent);
+
+ public void moveDirectory(OCFile dir, String newPath);
}
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());
- 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) {
new String[] { String.valueOf(file.getFileId()) })
.build());
+ } else if (fileExists(file.getFileId())) {
+ OCFile oldFile = getFileById(file.getFileId());
+ if (file.getStoragePath() == null && oldFile.getStoragePath() != null)
+ file.setStoragePath(oldFile.getStoragePath());
+ if (!file.isDirectory());
+ cv.put(ProviderTableMeta.FILE_STORAGE_PATH, file.getStoragePath());
+
+ operations.add(ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI).
+ withValues(cv).
+ withSelection( ProviderTableMeta._ID + "=?",
+ new String[] { String.valueOf(file.getFileId()) })
+ .build());
+
} else {
operations.add(ContentProviderOperation.newInsert(ProviderTableMeta.CONTENT_URI).withValues(cv).build());
}
if (file.isDown() && removeLocalCopy) {
new File(file.getStoragePath()).delete();
}
+ if (file.isDirectory() && removeLocalCopy) {
+ File f = new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file));
+ if (f.exists() && f.isDirectory() && (f.list() == null || f.list().length == 0)) {
+ f.delete();
+ }
+ }
+ }
+
+ @Override
+ public void removeDirectory(OCFile dir, boolean removeDBData, boolean removeLocalContent) {
+ // TODO consider possible failures
+ if (dir != null && dir.isDirectory() && dir.getFileId() != -1) {
+ Vector<OCFile> children = getDirectoryContent(dir);
+ if (children != null) {
+ OCFile child = null;
+ for (int i=0; i<children.size(); i++) {
+ child = children.get(i);
+ if (child.isDirectory()) {
+ removeDirectory(child, removeDBData, removeLocalContent);
+ } else {
+ if (removeDBData) {
+ removeFile(child, removeLocalContent);
+ } else if (removeLocalContent) {
+ if (child.isDown()) {
+ new File(child.getStoragePath()).delete();
+ }
+ }
+ }
+ }
+ if (removeDBData) {
+ removeFile(dir, true);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Updates database for a folder that was moved to a different location.
+ *
+ * TODO explore better (faster) implementations
+ * TODO throw exceptions up !
+ */
+ @Override
+ public void moveDirectory(OCFile dir, String newPath) {
+ // TODO check newPath
+
+ if (dir != null && dir.isDirectory() && dir.fileExists() && !dir.getFileName().equals(OCFile.PATH_SEPARATOR)) {
+ /// 1. get all the descendants of 'dir' in a single QUERY (including 'dir')
+ Cursor c = null;
+ if (getContentProvider() != null) {
+ try {
+ c = getContentProvider().query(ProviderTableMeta.CONTENT_URI,
+ null,
+ ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " + ProviderTableMeta.FILE_PATH + " LIKE ?",
+ new String[] { mAccount.name, dir.getRemotePath() + "%" }, null);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.getMessage());
+ }
+ } else {
+ c = getContentResolver().query(ProviderTableMeta.CONTENT_URI,
+ null,
+ ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " + ProviderTableMeta.FILE_PATH + " LIKE ?",
+ new String[] { mAccount.name, dir.getRemotePath() + "%" }, null);
+ }
+
+ /// 2. prepare a batch of update operations to change all the descendants
+ ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>(c.getCount());
+ int lengthOfOldPath = dir.getRemotePath().length();
+ String defaultSavePath = FileStorageUtils.getSavePath(mAccount.name);
+ int lengthOfOldStoragePath = defaultSavePath.length() + lengthOfOldPath;
+ if (c.moveToFirst()) {
+ do {
+ ContentValues cv = new ContentValues(); // don't take the constructor out of the loop and clear the object
+ OCFile child = createFileInstance(c);
+ cv.put(ProviderTableMeta.FILE_PATH, newPath + child.getRemotePath().substring(lengthOfOldPath));
+ if (child.getStoragePath() != null && child.getStoragePath().startsWith(defaultSavePath)) {
+ cv.put(ProviderTableMeta.FILE_STORAGE_PATH, defaultSavePath + newPath + child.getStoragePath().substring(lengthOfOldStoragePath));
+ }
+ operations.add(ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI).
+ withValues(cv).
+ withSelection( ProviderTableMeta._ID + "=?",
+ new String[] { String.valueOf(child.getFileId()) })
+ .build());
+ } while (c.moveToNext());
+ }
+ c.close();
+
+ /// 3. apply updates in batch
+ try {
+ if (getContentResolver() != null) {
+ getContentResolver().applyBatch(ProviderMeta.AUTHORITY_FILES, operations);
+
+ } else {
+ getContentProvider().applyBatch(operations);
+ }
+
+ } catch (OperationApplicationException e) {
+ Log.e(TAG, "Fail to update descendants of " + dir.getFileId() + " in database", e);
+
+ } catch (RemoteException e) {
+ Log.e(TAG, "Fail to update desendants of " + dir.getFileId() + " in database", e);
+ }
+
+ }
}
}
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.Log;
public class OCFile implements Parcelable, Comparable<OCFile> {
};
public static final String PATH_SEPARATOR = "/";
+
+ private static final String TAG = OCFile.class.getSimpleName();
private long mId;
private long mParentId;
*/
public String getFileName() {
File f = new File(getRemotePath());
- return f.getName().length() == 0 ? "/" : f.getName();
+ return f.getName().length() == 0 ? PATH_SEPARATOR : f.getName();
+ }
+
+ /**
+ * Sets the name of the file
+ *
+ * Does nothing if the new name is null, empty or includes "/" ; or if the file is the root directory
+ */
+ public void setFileName(String name) {
+ Log.d(TAG, "OCFile name changin from " + mRemotePath);
+ if (name != null && name.length() > 0 && !name.contains(PATH_SEPARATOR) && !mRemotePath.equals(PATH_SEPARATOR)) {
+ String parent = (new File(getRemotePath())).getParent();
+ parent = (parent.endsWith(PATH_SEPARATOR)) ? parent : parent + PATH_SEPARATOR;
+ mRemotePath = parent + name;
+ if (isDirectory()) {
+ mRemotePath += PATH_SEPARATOR;
+ }
+ Log.d(TAG, "OCFile name changed to " + mRemotePath);
+ }
}
/**
@Override
public String toString() {
String asString = "[id=%s, name=%s, mime=%s, downloaded=%s, local=%s, remote=%s, parentId=%s, keepInSinc=%s]";
- asString = String.format(asString, new Long(mId), getFileName(), mMimeType, isDown(), mLocalPath, mRemotePath, new Long(mParentId), new Boolean(mKeepInSync));
+ asString = String.format(asString, Long.valueOf(mId), getFileName(), mMimeType, isDown(), mLocalPath, mRemotePath, Long.valueOf(mParentId), Boolean.valueOf(mKeepInSync));
return asString;
}
\r
\r
/**\r
- * Returns True when the file described by 'file' in the ownCloud account 'account' is downloading or waiting to download\r
+ * Returns True when the file described by 'file' in the ownCloud account 'account' is downloading or waiting to download.\r
+ * \r
+ * If 'file' is a directory, returns 'true' if some of its descendant files is downloading or waiting to download. \r
* \r
* @param account Owncloud account where the remote file is stored.\r
* @param file A file that could be in the queue of downloads.\r
*/\r
public boolean isDownloading(Account account, OCFile file) {\r
+ String targetKey = buildRemoteName(account, file);\r
synchronized (mPendingDownloads) {\r
- return (mPendingDownloads.containsKey(buildRemoteName(account, file)));\r
+ if (file.isDirectory()) {\r
+ // this can be slow if there are many downloads :(\r
+ Iterator<String> it = mPendingDownloads.keySet().iterator();\r
+ boolean found = false;\r
+ while (it.hasNext() && !found) {\r
+ found = it.next().startsWith(targetKey);\r
+ }\r
+ return found;\r
+ } else {\r
+ return (mPendingDownloads.containsKey(targetKey));\r
+ }\r
}\r
}\r
}\r
public class FileUploader extends Service implements OnDatatransferProgressListener {
public static final String UPLOAD_FINISH_MESSAGE = "UPLOAD_FINISH";
- public static final String EXTRA_PARENT_DIR_ID = "PARENT_DIR_ID";
public static final String EXTRA_UPLOAD_RESULT = "RESULT";
public static final String EXTRA_REMOTE_PATH = "REMOTE_PATH";
public static final String EXTRA_OLD_REMOTE_PATH = "OLD_REMOTE_PATH";
public static final String ACCOUNT_NAME = "ACCOUNT_NAME";
public static final String KEY_FILE = "FILE";
- public static final String KEY_LOCAL_FILE = "LOCAL_FILE"; // TODO remove this as a possible input argument ; use KEY_FILE everywhere
- public static final String KEY_REMOTE_FILE = "REMOTE_FILE"; // TODO remove this as a possible input argument ; use KEY_FILE everywhere
+ public static final String KEY_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";
/**
* Returns True when the file described by 'file' is being uploaded to the ownCloud account 'account' or waiting for it
*
+ * If 'file' is a directory, returns 'true' if some of its descendant files is downloading or waiting to download.
+ *
* @param account Owncloud account where the remote file will be stored.
* @param file A file that could be in the queue of pending uploads
*/
public boolean isUploading(Account account, OCFile file) {
+ String targetKey = buildRemoteName(account, file);
synchronized (mPendingUploads) {
- return (mPendingUploads.containsKey(buildRemoteName(account, file)));
+ if (file.isDirectory()) {
+ // this can be slow if there are many downloads :(
+ Iterator<String> it = mPendingUploads.keySet().iterator();
+ boolean found = false;
+ while (it.hasNext() && !found) {
+ found = it.next().startsWith(targetKey);
+ }
+ return found;
+ } else {
+ return (mPendingUploads.containsKey(targetKey));
+ }
}
}
}
// parent dir
String parentPath = new File(remotePath).getParent();
- parentPath = parentPath.endsWith("/")?parentPath:parentPath+"/" ;
+ parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : parentPath + OCFile.PATH_SEPARATOR ;
OCFile parentDir = storageManager.getFileByPath(parentPath);
if (parentDir == null) {
throw new IllegalStateException("Can not upload a file to a non existing remote location: " + parentPath);
end.putExtra(EXTRA_FILE_PATH, upload.getStoragePath());
end.putExtra(ACCOUNT_NAME, upload.getAccount().name);
end.putExtra(EXTRA_UPLOAD_RESULT, uploadResult.isSuccess());
- end.putExtra(EXTRA_PARENT_DIR_ID, upload.getFile().getParentId());
sendStickyBroadcast(end);
}
package com.owncloud.android.operations;
+import org.apache.commons.httpclient.HttpStatus;
import org.apache.jackrabbit.webdav.client.methods.DeleteMethod;
import android.util.Log;
/**
+ * Getter for the file to remove (or removed, if the operation was successfully performed).
+ *
+ * @return File to remove or already removed.
+ */
+ public OCFile getFile() {
+ return mFileToRemove;
+ }
+
+
+ /**
* Performs the remove operation
*
* @param client Client object to communicate with the remote ownCloud server.
try {
delete = new DeleteMethod(client.getBaseUri() + WebdavUtils.encodePath(mFileToRemove.getRemotePath()));
int status = client.executeMethod(delete, REMOVE_READ_TIMEOUT, REMOVE_CONNECTION_TIMEOUT);
- if (delete.succeeded()) {
- mDataStorageManager.removeFile(mFileToRemove, mDeleteLocalCopy);
+ if (delete.succeeded() || status == HttpStatus.SC_NOT_FOUND) {
+ if (mFileToRemove.isDirectory()) {
+ mDataStorageManager.removeDirectory(mFileToRemove, true, mDeleteLocalCopy);
+ } else {
+ mDataStorageManager.removeFile(mFileToRemove, mDeleteLocalCopy);
+ }
}
delete.getResponseBodyAsString(); // exhaust the response, although not interesting
- result = new RemoteOperationResult(delete.succeeded(), status);
+ result = new RemoteOperationResult((delete.succeeded() || status == HttpStatus.SC_NOT_FOUND), status);
Log.i(TAG, "Remove " + mFileToRemove.getRemotePath() + ": " + result.getLogMessage());
} catch (Exception e) {
import org.apache.jackrabbit.webdav.client.methods.DavMethodBase;
//import org.apache.jackrabbit.webdav.client.methods.MoveMethod;
+import android.accounts.Account;
import android.util.Log;
import com.owncloud.android.datamodel.DataStorageManager;
private OCFile mFile;
+ private Account mAccount;
private String mNewName;
+ private String mNewRemotePath;
private DataStorageManager mStorageManager;
* Constructor
*
* @param file OCFile instance describing the remote file or folder to rename
+ * @param account OwnCloud account containing the remote file
* @param newName New name to set as the name of file.
* @param storageManager Reference to the local database corresponding to the account where the file is contained.
*/
- public RenameFileOperation(OCFile file, String newName, DataStorageManager storageManager) {
+ public RenameFileOperation(OCFile file, Account account, String newName, DataStorageManager storageManager) {
mFile = file;
+ mAccount = account;
mNewName = newName;
+ mNewRemotePath = null;
mStorageManager = storageManager;
}
RemoteOperationResult result = null;
LocalMoveMethod move = null;
- //MoveMethod move = null; // TODO find out why not use this
- String newRemotePath = null;
+ mNewRemotePath = null;
try {
if (mNewName.equals(mFile.getFileName())) {
return new RemoteOperationResult(ResultCode.OK);
}
- newRemotePath = (new File(mFile.getRemotePath())).getParent() + mNewName;
+ String parent = (new File(mFile.getRemotePath())).getParent();
+ parent = (parent.endsWith(OCFile.PATH_SEPARATOR)) ? parent : parent + OCFile.PATH_SEPARATOR;
+ mNewRemotePath = parent + mNewName;
+ if (mFile.isDirectory()) {
+ mNewRemotePath += OCFile.PATH_SEPARATOR;
+ }
// check if the new name is valid in the local file system
if (!isValidNewName()) {
return new RemoteOperationResult(ResultCode.INVALID_LOCAL_FILE_NAME);
}
- // check if a remote file with the new name already exists
- if (client.existsFile(newRemotePath)) {
+ // check if a file with the new name already exists
+ if (client.existsFile(mNewRemotePath) || // remote check could fail by network failure, or by indeterminate behavior of HEAD for folders ...
+ mStorageManager.getFileByPath(mNewRemotePath) != null) { // ... so local check is convenient
return new RemoteOperationResult(ResultCode.INVALID_OVERWRITE);
}
- /*move = new MoveMethod( client.getBaseUri() + WebdavUtils.encodePath(mFile.getRemotePath()),
- client.getBaseUri() + WebdavUtils.encodePath(newRemotePath),
- false);*/
move = new LocalMoveMethod( client.getBaseUri() + WebdavUtils.encodePath(mFile.getRemotePath()),
- client.getBaseUri() + WebdavUtils.encodePath(newRemotePath));
+ client.getBaseUri() + WebdavUtils.encodePath(mNewRemotePath));
int status = client.executeMethod(move, RENAME_READ_TIMEOUT, RENAME_CONNECTION_TIMEOUT);
if (move.succeeded()) {
- // create new OCFile instance for the renamed file
- OCFile newFile = obtainUpdatedFile();
- OCFile oldFile = mFile;
- mFile = newFile;
-
- // try to rename the local copy of the file
- if (oldFile.isDown()) {
- File f = new File(oldFile.getStoragePath());
- String newStoragePath = f.getParent() + mNewName;
- if (f.renameTo(new File(newStoragePath))) {
- mFile.setStoragePath(newStoragePath);
- }
- // else - NOTHING: the link to the local file is kept although the local name can't be updated
- // TODO - study conditions when this could be a problem
+ if (mFile.isDirectory()) {
+ saveLocalDirectory();
+
+ } else {
+ saveLocalFile();
+
}
-
- mStorageManager.removeFile(oldFile, false);
- mStorageManager.saveFile(mFile);
+
+ /*
+ *} else if (mFile.isDirectory() && (status == 207 || status >= 500)) {
+ * // TODO
+ * // if server fails in the rename of a folder, some children files could have been moved to a folder with the new name while some others
+ * // stayed in the old folder;
+ * //
+ * // easiest and heaviest solution is synchronizing the parent folder (or the full account);
+ * //
+ * // a better solution is synchronizing the folders with the old and new names;
+ *}
+ */
}
move.getResponseBodyAsString(); // exhaust response, although not interesting
result = new RemoteOperationResult(move.succeeded(), status);
- Log.i(TAG, "Rename " + mFile.getRemotePath() + " to " + newRemotePath + ": " + result.getLogMessage());
+ Log.i(TAG, "Rename " + mFile.getRemotePath() + " to " + mNewRemotePath + ": " + result.getLogMessage());
} catch (Exception e) {
result = new RemoteOperationResult(e);
- Log.e(TAG, "Rename " + mFile.getRemotePath() + " to " + ((newRemotePath==null) ? mNewName : newRemotePath) + ": " + result.getLogMessage(), e);
+ Log.e(TAG, "Rename " + mFile.getRemotePath() + " to " + ((mNewRemotePath==null) ? mNewName : mNewRemotePath) + ": " + result.getLogMessage(), e);
} finally {
if (move != null)
}
+ private void saveLocalDirectory() {
+ mStorageManager.moveDirectory(mFile, mNewRemotePath);
+ String localPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, mFile);
+ File localDir = new File(localPath);
+ if (localDir.exists()) {
+ localDir.renameTo(new File(FileStorageUtils.getSavePath(mAccount.name) + mNewRemotePath));
+ // TODO - if renameTo fails, children files that are already down will result unlinked
+ }
+ }
+
+ private void saveLocalFile() {
+ mFile.setFileName(mNewName);
+
+ // try to rename the local copy of the file
+ if (mFile.isDown()) {
+ File f = new File(mFile.getStoragePath());
+ String parentStoragePath = f.getParent();
+ if (!parentStoragePath.endsWith(File.separator))
+ parentStoragePath += File.separator;
+ if (f.renameTo(new File(parentStoragePath + mNewName))) {
+ mFile.setStoragePath(parentStoragePath + mNewName);
+ }
+ // else - NOTHING: the link to the local file is kept although the local name can't be updated
+ // TODO - study conditions when this could be a problem
+ }
+
+ mStorageManager.saveFile(mFile);
+ }
+
/**
* Checks if the new name to set is valid in the file system
*
}
- /**
- * Creates a new OCFile for the new remote name of the renamed file.
- *
- * @return OCFile object with the same information than mFile, but the renamed remoteFile and the storagePath (empty)
- */
- private OCFile obtainUpdatedFile() {
- OCFile file = new OCFile(mStorageManager.getFileById(mFile.getParentId()).getRemotePath() + mNewName);
- file.setCreationTimestamp(mFile.getCreationTimestamp());
- file.setFileId(mFile.getFileId());
- file.setFileLength(mFile.getFileLength());
- file.setKeepInSync(mFile.keepInSync());
- file.setLastSyncDateForProperties(mFile.getLastSyncDateForProperties());
- file.setLastSyncDateForData(mFile.getLastSyncDateForData());
- file.setMimetype(mFile.getMimetype());
- file.setModificationTimestamp(mFile.getModificationTimestamp());
- file.setParentId(mFile.getParentId());
- return file;
- }
-
-
- // move operation - TODO: find out why org.apache.jackrabbit.webdav.client.methods.MoveMethod is not used instead ¿?
+ // move operation
private class LocalMoveMethod extends DavMethodBase {
public LocalMoveMethod(String uri, String dest) {
return mTransferWasRequested;
}
+
+ public OCFile getLocalFile() {
+ return mLocalFile;
+ }
+
}
}
}
- return false;
+ return true;
}
private void populateAccountList() {
}\r
FileDetailFragment fragment = (FileDetailFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);\r
if (fragment != null)\r
- fragment.updateFileDetails(); // let the fragment gets the mDownloadBinder through getDownloadBinder() (see FileDetailFragment#updateFileDetais())\r
+ fragment.updateFileDetails(false); // let the fragment gets the mDownloadBinder through getDownloadBinder() (see FileDetailFragment#updateFileDetais())\r
}\r
\r
@Override\r
case android.R.id.home:\r
backToDisplayActivity();\r
returnValue = true;\r
+ break;\r
+ default:\r
+ returnValue = super.onOptionsItemSelected(item);\r
}\r
\r
return returnValue;\r
super.onResume();\r
if (!mConfigurationChangedToLandscape) { \r
FileDetailFragment fragment = (FileDetailFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);\r
- fragment.updateFileDetails();\r
+ fragment.updateFileDetails(false);\r
}\r
}\r
\r
import com.owncloud.android.files.services.FileUploader;\r
import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;\r
import com.owncloud.android.network.OwnCloudClientUtils;\r
+import com.owncloud.android.operations.OnRemoteOperationListener;\r
+import com.owncloud.android.operations.RemoteOperation;\r
import com.owncloud.android.operations.RemoteOperationResult;\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.operations.RemoteOperationResult.ResultCode;\r
import com.owncloud.android.syncadapter.FileSyncService;\r
import com.owncloud.android.ui.dialog.SslValidatorDialog;\r
import com.owncloud.android.ui.dialog.SslValidatorDialog.OnSslValidatorListener;\r
*/\r
\r
public class FileDisplayActivity extends SherlockFragmentActivity implements\r
- OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNavigationListener, OnSslValidatorListener {\r
+ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNavigationListener, OnSslValidatorListener, OnRemoteOperationListener {\r
\r
private ArrayAdapter<String> mDirectories;\r
private OCFile mCurrentDir = null;\r
break;\r
}\r
default:\r
- retval = false;\r
+ retval = super.onOptionsItemSelected(item);\r
}\r
return retval;\r
}\r
*/\r
@Override\r
public void onReceive(Context context, Intent intent) {\r
- long parentDirId = intent.getLongExtra(FileUploader.EXTRA_PARENT_DIR_ID, -1);\r
- OCFile parentDir = mStorageManager.getFileById(parentDirId);\r
+ String uploadedRemotePath = intent.getStringExtra(FileDownloader.EXTRA_REMOTE_PATH);\r
String accountName = intent.getStringExtra(FileUploader.ACCOUNT_NAME);\r
-\r
- if (accountName.equals(AccountUtils.getCurrentOwnCloudAccount(context).name) &&\r
- parentDir != null && \r
- ( (mCurrentDir == null && parentDir.getFileName().equals("/")) ||\r
- parentDir.equals(mCurrentDir)\r
- )\r
- ) {\r
+ boolean sameAccount = accountName.equals(AccountUtils.getCurrentOwnCloudAccount(context).name);\r
+ boolean isDescendant = (mCurrentDir != null) && (uploadedRemotePath != null) && (uploadedRemotePath.startsWith(mCurrentDir.getRemotePath()));\r
+ if (sameAccount && isDescendant) {\r
OCFileListFragment fileListFragment = (OCFileListFragment) getSupportFragmentManager().findFragmentById(R.id.fileList);\r
if (fileListFragment != null) { \r
fileListFragment.listDirectory();\r
public void onReceive(Context context, Intent intent) {\r
String downloadedRemotePath = intent.getStringExtra(FileDownloader.EXTRA_REMOTE_PATH);\r
String accountName = intent.getStringExtra(FileDownloader.ACCOUNT_NAME);\r
-\r
- if (accountName.equals(AccountUtils.getCurrentOwnCloudAccount(context).name) &&\r
- mCurrentDir != null && mCurrentDir.getFileId() == mStorageManager.getFileByPath(downloadedRemotePath).getParentId()) {\r
+ boolean sameAccount = accountName.equals(AccountUtils.getCurrentOwnCloudAccount(context).name);\r
+ boolean isDescendant = (mCurrentDir != null) && (downloadedRemotePath != null) && (downloadedRemotePath.startsWith(mCurrentDir.getRemotePath()));\r
+ if (sameAccount && isDescendant) {\r
OCFileListFragment fileListFragment = (OCFileListFragment) getSupportFragmentManager().findFragmentById(R.id.fileList);\r
if (fileListFragment != null) { \r
fileListFragment.listDirectory();\r
if (mDualPane) {\r
FileDetailFragment fragment = (FileDetailFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);\r
if (fragment != null)\r
- fragment.updateFileDetails();\r
+ fragment.updateFileDetails(false);\r
}\r
}\r
\r
}\r
\r
\r
+ /**\r
+ * Updates the view associated to the activity after the finish of some operation over files\r
+ * in the current account.\r
+ * \r
+ * @param operation Removal operation performed.\r
+ * @param result Result of the removal.\r
+ */\r
+ @Override\r
+ public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {\r
+ if (operation instanceof RemoveFileOperation) {\r
+ onRemoveFileOperationFinish((RemoveFileOperation)operation, result);\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
+ * Updates the view associated to the activity after the finish of an operation trying to remove a \r
+ * file. \r
+ * \r
+ * @param operation Removal operation performed.\r
+ * @param result Result of the removal.\r
+ */\r
+ private void onRemoveFileOperationFinish(RemoveFileOperation operation, RemoteOperationResult result) {\r
+ dismissDialog(DIALOG_SHORT_WAIT);\r
+ if (result.isSuccess()) {\r
+ Toast msg = Toast.makeText(this, R.string.remove_success_msg, Toast.LENGTH_LONG);\r
+ msg.show();\r
+ OCFile removedFile = operation.getFile();\r
+ if (mDualPane) {\r
+ FileDetailFragment details = (FileDetailFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);\r
+ if (details != null && removedFile.equals(details.getDisplayedFile()) ) {\r
+ FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();\r
+ transaction.replace(R.id.file_details_container, new FileDetailFragment(null, null)); // empty FileDetailFragment\r
+ transaction.commit();\r
+ }\r
+ }\r
+ if (mStorageManager.getFileById(removedFile.getParentId()).equals(mCurrentDir)) {\r
+ mFileList.listDirectory();\r
+ }\r
+ \r
+ } else {\r
+ Toast msg = Toast.makeText(this, R.string.remove_fail_msg, Toast.LENGTH_LONG); \r
+ msg.show();\r
+ if (result.isSslRecoverableException()) {\r
+ mLastSslUntrustedServerResult = result;\r
+ showDialog(DIALOG_SSL_VALIDATOR); \r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Updates the view associated to the activity after the finish of an operation trying to rename a \r
+ * file. \r
+ * \r
+ * @param operation Renaming operation performed.\r
+ * @param result Result of the renaming.\r
+ */\r
+ private void onRenameFileOperationFinish(RenameFileOperation operation, RemoteOperationResult result) {\r
+ dismissDialog(DIALOG_SHORT_WAIT);\r
+ OCFile renamedFile = operation.getFile();\r
+ if (result.isSuccess()) {\r
+ if (mDualPane) {\r
+ FileDetailFragment details = (FileDetailFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);\r
+ if (details != null && renamedFile.equals(details.getDisplayedFile()) ) {\r
+ details.updateFileDetails(renamedFile, AccountUtils.getCurrentOwnCloudAccount(this));\r
+ }\r
+ }\r
+ if (mStorageManager.getFileById(renamedFile.getParentId()).equals(mCurrentDir)) {\r
+ mFileList.listDirectory();\r
+ }\r
+ \r
+ } else {\r
+ if (result.getCode().equals(ResultCode.INVALID_LOCAL_FILE_NAME)) {\r
+ Toast msg = Toast.makeText(this, R.string.rename_local_fail_msg, Toast.LENGTH_LONG); \r
+ msg.show();\r
+ // TODO throw again the new rename dialog\r
+ } else {\r
+ Toast msg = Toast.makeText(this, R.string.rename_server_fail_msg, Toast.LENGTH_LONG); \r
+ msg.show();\r
+ if (result.isSslRecoverableException()) {\r
+ mLastSslUntrustedServerResult = result;\r
+ showDialog(DIALOG_SSL_VALIDATOR); \r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+\r
+ private void onSynchronizeFileOperationFinish(SynchronizeFileOperation operation, RemoteOperationResult result) {\r
+ dismissDialog(DIALOG_SHORT_WAIT);\r
+ OCFile syncedFile = operation.getLocalFile();\r
+ if (!result.isSuccess()) {\r
+ if (result.getCode() == ResultCode.SYNC_CONFLICT) {\r
+ Intent i = new Intent(this, ConflictsResolveActivity.class);\r
+ i.putExtra(ConflictsResolveActivity.EXTRA_FILE, syncedFile);\r
+ i.putExtra(ConflictsResolveActivity.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));\r
+ startActivity(i);\r
+ \r
+ } else {\r
+ Toast msg = Toast.makeText(this, R.string.sync_file_fail_msg, Toast.LENGTH_LONG); \r
+ msg.show();\r
+ }\r
+ \r
+ } else {\r
+ if (operation.transferWasRequested()) {\r
+ mFileList.listDirectory();\r
+ onTransferStateChanged(syncedFile, true, true);\r
+ \r
+ } else {\r
+ Toast msg = Toast.makeText(this, R.string.sync_file_nothing_to_do_msg, Toast.LENGTH_LONG); \r
+ msg.show();\r
+ }\r
+ }\r
+ }\r
+\r
+\r
+ /**\r
+ * {@inheritDoc}\r
+ */\r
+ @Override\r
+ public void onTransferStateChanged(OCFile file, boolean downloading, boolean uploading) {\r
+ /*OCFileListFragment fileListFragment = (OCFileListFragment) getSupportFragmentManager().findFragmentById(R.id.fileList);\r
+ if (fileListFragment != null) { \r
+ fileListFragment.listDirectory();\r
+ }*/\r
+ if (mDualPane) {\r
+ FileDetailFragment details = (FileDetailFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);\r
+ if (details != null && file.equals(details.getDisplayedFile()) ) {\r
+ if (downloading || uploading) {\r
+ details.updateFileDetails(file, AccountUtils.getCurrentOwnCloudAccount(this));\r
+ } else {\r
+ details.updateFileDetails(downloading || uploading);\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+\r
+ \r
+\r
+\r
}\r
break;
}
default:
- retval = false;
+ retval = onOptionsItemSelected(item);
}
return retval;
}
fileIcon.setImageResource(R.drawable.ic_menu_archive);\r
}\r
ImageView localStateView = (ImageView) view.findViewById(R.id.imageView2);\r
- //if (FileDownloader.isDownloading(mAccount, file.getRemotePath())) {\r
FileDownloaderBinder downloaderBinder = mTransferServiceGetter.getFileDownloaderBinder();\r
FileUploaderBinder uploaderBinder = mTransferServiceGetter.getFileUploaderBinder();\r
if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, file)) {\r
package com.owncloud.android.ui.dialog;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewGroup;
-import android.view.View.OnClickListener;
import android.view.WindowManager.LayoutParams;
-import android.widget.Button;
import android.widget.TextView;
import com.actionbarsherlock.app.SherlockDialogFragment;
/**
- * Dialog to request the user about a certificate that could not be validated with the certificates store in the system.
+ * Dialog to request the user to input a name, optionally initialized with a former name.
*
* @author Bartek Przybylski
+ * @author David A. Velasco
*/
-public class EditNameDialog extends SherlockDialogFragment implements OnClickListener {
+public class EditNameDialog extends SherlockDialogFragment implements DialogInterface.OnClickListener {
+ public static final String TAG = EditNameDialog.class.getSimpleName();
+
+ protected static final String ARG_TITLE = "title";
+ protected static final String ARG_NAME = "name";
+
private String mNewFilename;
private boolean mResult;
private EditNameDialogListener mListener;
- static public EditNameDialog newInstance(String filename) {
+ /**
+ * Public factory method to get dialog instances.
+ *
+ * @param title Text to show as title in the dialog.
+ * @param name Optional text to include in the text input field when the dialog is shown.
+ * @param listener Instance to notify when the dialog is dismissed.
+ * @return New dialog instance, ready to show.
+ */
+ static public EditNameDialog newInstance(String title, String name, EditNameDialogListener listener) {
EditNameDialog f = new EditNameDialog();
Bundle args = new Bundle();
- args.putString("filename", filename);
+ args.putString(ARG_TITLE, title);
+ args.putString(ARG_NAME, name);
f.setArguments(args);
+ f.setOnDismissListener(listener);
return f;
}
+
+ /**
+ * {@inheritDoc}
+ */
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View v = inflater.inflate(R.layout.edit_box_dialog, container, false);
-
- String currentName = getArguments().getString("filename");
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ String currentName = getArguments().getString(ARG_NAME);
if (currentName == null)
currentName = "";
+ String title = getArguments().getString(ARG_TITLE);
- ((Button)v.findViewById(R.id.cancel)).setOnClickListener(this);
- ((Button)v.findViewById(R.id.ok)).setOnClickListener(this);
- ((TextView)v.findViewById(R.id.user_input)).setText(currentName);
- ((TextView)v.findViewById(R.id.user_input)).requestFocus();
- getDialog().getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_VISIBLE);
+ // Inflate the layout for the dialog
+ LayoutInflater inflater = getSherlockActivity().getLayoutInflater();
+ View v = inflater.inflate(R.layout.edit_box_dialog, null); // null parent view because it will go in the dialog layout
+ TextView inputText = ((TextView)v.findViewById(R.id.user_input));
+ inputText.setText(currentName);
+
+ // Set it to the dialog
+ AlertDialog.Builder builder = new AlertDialog.Builder(getSherlockActivity());
+ builder.setView(v)
+ .setPositiveButton(R.string.common_ok, this)
+ .setNegativeButton(R.string.common_cancel, this);
+ if (title != null) {
+ builder.setTitle(title);
+ }
+
mResult = false;
- return v;
- }
+
+ Dialog d = builder.create();
+
+ inputText.requestFocus();
+ d.getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_VISIBLE);
+ return d;
+ }
+
+ /**
+ * Performs the corresponding action when a dialog button is clicked.
+ *
+ * Saves the text in the input field to be accessed through {@link #getNewFilename()} when the positive
+ * button is clicked.
+ *
+ * Notify the current listener in any case.
+ */
@Override
- public void onClick(View view) {
- switch (view.getId()) {
- case R.id.ok: {
- mNewFilename = ((TextView)getView().findViewById(R.id.user_input)).getText().toString();
+ public void onClick(DialogInterface dialog, int which) {
+ switch (which) {
+ case AlertDialog.BUTTON_POSITIVE: {
+ mNewFilename = ((TextView)(getDialog().findViewById(R.id.user_input))).getText().toString();
mResult = true;
}
- case R.id.cancel: { // fallthought
+ case AlertDialog.BUTTON_NEGATIVE: { // fall through
dismiss();
if (mListener != null)
mListener.onDismiss(this);
}
}
- public void setOnDismissListener(EditNameDialogListener listener) {
+ protected void setOnDismissListener(EditNameDialogListener listener) {
mListener = listener;
}
+ /**
+ * Returns the text in the input field after the user clicked the positive button.
+ *
+ * @return Text in the input field.
+ */
public String getNewFilename() {
return mNewFilename;
}
- // true if user clicked ok
+ /**
+ *
+ * @return True when the user clicked the positive button.
+ */
public boolean getResult() {
return mResult;
}
+ /**
+ * Interface to receive a notification when any button in the dialog is clicked.
+ */
public interface EditNameDialogListener {
public void onDismiss(EditNameDialog dialog);
}
-
+
+
}
mPreview = (ImageView)mView.findViewById(R.id.fdPreview);\r
}\r
\r
- updateFileDetails();\r
+ updateFileDetails(false);\r
return view;\r
}\r
\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
break;\r
}\r
case R.id.fdRenameBtn: {\r
- EditNameDialog dialog = EditNameDialog.newInstance(mFile.getFileName());\r
- dialog.setOnDismissListener(this);\r
+ EditNameDialog dialog = EditNameDialog.newInstance(getString(R.string.rename_dialog_title), mFile.getFileName(), this);\r
dialog.show(getFragmentManager(), "nameeditdialog");\r
break;\r
} \r
try {\r
Intent i = new Intent(Intent.ACTION_VIEW);\r
mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(storagePath.substring(storagePath.lastIndexOf('.') + 1));\r
- if (mimeType != null && !mimeType.equals(mFile.getMimetype())) {\r
- i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), mimeType);\r
+ if (mimeType == null || !mimeType.equals(mFile.getMimetype())) {\r
+ if (mimeType != null) {\r
+ i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), mimeType);\r
+ } else {\r
+ // desperate try\r
+ i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), "*/*");\r
+ }\r
i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);\r
startActivity(i);\r
toastIt = false;\r
mStorageManager = new FileDataStorageManager(ocAccount, getActivity().getApplicationContext().getContentResolver());\r
}\r
mAccount = ocAccount;\r
- updateFileDetails();\r
+ updateFileDetails(false);\r
}\r
\r
\r
/**\r
* Updates the view with all relevant details about that file.\r
+ *\r
+ * TODO Remove parameter when the transferring state of files is kept in database. \r
+ * \r
+ * @param transferring Flag signaling if the file should be considered as downloading or uploading, \r
+ * although {@link FileDownloaderBinder#isDownloading(Account, OCFile)} and \r
+ * {@link FileUploaderBinder#isUploading(Account, OCFile)} return false.\r
+ * \r
*/\r
- public void updateFileDetails() {\r
+ public void updateFileDetails(boolean transferring) {\r
\r
if (mFile != null && mAccount != null && mLayout == R.layout.file_details_fragment) {\r
\r
//if (FileDownloader.isDownloading(mAccount, mFile.getRemotePath()) || FileUploader.isUploading(mAccount, mFile.getRemotePath())) {\r
FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();\r
FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder();\r
- if ((downloaderBinder != null && downloaderBinder.isDownloading(mAccount, mFile)) || (uploaderBinder != null && uploaderBinder.isUploading(mAccount, mFile))) {\r
+ if (transferring || (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, mFile)) || (uploaderBinder != null && uploaderBinder.isUploading(mAccount, mFile))) {\r
setButtonsForTransferring();\r
\r
} else if (mFile.isDown()) {\r
setButtonsForDown();\r
\r
} else {\r
+ // TODO load default preview image; when the local file is removed, the preview remains there\r
setButtonsForRemote();\r
}\r
}\r
+ getView().invalidate();\r
}\r
\r
\r
if (!isEmpty()) {\r
Button downloadButton = (Button) getView().findViewById(R.id.fdDownloadBtn);\r
downloadButton.setText(R.string.filedetails_sync_file);\r
- //downloadButton.setEnabled(true);\r
\r
((Button) getView().findViewById(R.id.fdOpenBtn)).setEnabled(true);\r
((Button) getView().findViewById(R.id.fdRenameBtn)).setEnabled(true);\r
if (downloadWasFine) {\r
mFile = mStorageManager.getFileByPath(downloadedRemotePath);\r
}\r
- updateFileDetails(); // it updates the buttons; must be called although !downloadWasFine\r
+ updateFileDetails(false); // it updates the buttons; must be called although !downloadWasFine\r
}\r
}\r
}\r
private class UploadFinishReceiver extends BroadcastReceiver {\r
@Override\r
public void onReceive(Context context, Intent intent) {\r
- Log.d(TAG, "Received broacast! : " + intent.getStringExtra(FileUploader.EXTRA_REMOTE_PATH));\r
String accountName = intent.getStringExtra(FileUploader.ACCOUNT_NAME);\r
\r
if (!isEmpty() && accountName.equals(mAccount.name)) {\r
Toast msg = Toast.makeText(getActivity().getApplicationContext(), String.format(getString(R.string.filedetails_renamed_in_upload_msg), newName), Toast.LENGTH_LONG);\r
msg.show();\r
}\r
- updateFileDetails(); // it updates the buttons; must be called although !uploadWasFine; interrupted uploads still leave an incomplete file in the server\r
+ updateFileDetails(false); // it updates the buttons; must be called although !uploadWasFine; interrupted uploads still leave an incomplete file in the server\r
}\r
}\r
}\r
String newFilename = dialog.getNewFilename();\r
Log.d(TAG, "name edit dialog dismissed with new name " + newFilename);\r
mLastRemoteOperation = new RenameFileOperation( mFile, \r
+ mAccount, \r
newFilename, \r
new FileDataStorageManager(mAccount, getActivity().getContentResolver()));\r
WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getSherlockActivity().getApplicationContext());\r
*/
package com.owncloud.android.ui.fragment;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.owncloud.android.AccountUtils;
+import com.owncloud.android.R;
import com.owncloud.android.datamodel.DataStorageManager;
import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
+import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
+import com.owncloud.android.network.OwnCloudClientUtils;
+import com.owncloud.android.operations.OnRemoteOperationListener;
+import com.owncloud.android.operations.RemoteOperation;
+import com.owncloud.android.operations.RemoveFileOperation;
+import com.owncloud.android.operations.RenameFileOperation;
+import com.owncloud.android.operations.SynchronizeFileOperation;
import com.owncloud.android.ui.FragmentListView;
+import com.owncloud.android.ui.activity.FileDisplayActivity;
import com.owncloud.android.ui.activity.TransferServiceGetter;
import com.owncloud.android.ui.adapter.FileListListAdapter;
+import com.owncloud.android.ui.dialog.EditNameDialog;
+import com.owncloud.android.ui.dialog.EditNameDialog.EditNameDialogListener;
+import com.owncloud.android.ui.fragment.ConfirmationDialogFragment.ConfirmationDialogFragmentListener;
+
+import eu.alefzero.webdav.WebdavClient;
+import eu.alefzero.webdav.WebdavUtils;
+import android.accounts.Account;
import android.app.Activity;
+import android.content.ActivityNotFoundException;
+import android.content.Intent;
+import android.net.Uri;
import android.os.Bundle;
+import android.os.Handler;
import android.util.Log;
+import android.view.ContextMenu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
import android.view.View;
+import android.webkit.MimeTypeMap;
import android.widget.AdapterView;
+import android.widget.Toast;
+import android.widget.AdapterView.AdapterContextMenuInfo;
/**
* A Fragment that lists all files and folders in a given path.
* @author Bartek Przybylski
*
*/
-public class OCFileListFragment extends FragmentListView {
+public class OCFileListFragment extends FragmentListView implements EditNameDialogListener, ConfirmationDialogFragmentListener {
private static final String TAG = "FileListFragment";
private static final String SAVED_LIST_POSITION = "LIST_POSITION";
private OCFile mFile = null;
private FileListListAdapter mAdapter;
+
+ private Handler mHandler;
+ private OCFile mTargetFile;
/**
setReferencePosition(position);
}
+ registerForContextMenu(getListView());
+ getListView().setOnCreateContextMenuListener(this);
+
+ mHandler = new Handler();
+
Log.i(TAG, "onActivityCreated() stop");
}
}
}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onCreateContextMenu (ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
+ super.onCreateContextMenu(menu, v, menuInfo);
+ MenuInflater inflater = getActivity().getMenuInflater();
+ inflater.inflate(R.menu.file_context_menu, menu);
+ AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
+ OCFile targetFile = (OCFile) mAdapter.getItem(info.position);
+ List<Integer> toHide = new ArrayList<Integer>();
+ List<Integer> toDisable = new ArrayList<Integer>();
+
+ MenuItem item = null;
+ if (targetFile.isDirectory()) {
+ // contextual menu for folders
+ toHide.add(R.id.open_file_item);
+ toHide.add(R.id.download_file_item);
+ toHide.add(R.id.cancel_download_item);
+ toHide.add(R.id.cancel_upload_item);
+ if ( mContainerActivity.getFileDownloaderBinder().isDownloading(AccountUtils.getCurrentOwnCloudAccount(getActivity()), targetFile) ||
+ mContainerActivity.getFileUploaderBinder().isUploading(AccountUtils.getCurrentOwnCloudAccount(getActivity()), targetFile) ) {
+ toDisable.add(R.id.rename_file_item);
+ toDisable.add(R.id.remove_file_item);
+
+ }
+
+ } else {
+ // contextual menu for regular files
+ if (targetFile.isDown()) {
+ toHide.add(R.id.cancel_download_item);
+ toHide.add(R.id.cancel_upload_item);
+ item = menu.findItem(R.id.download_file_item);
+ if (item != null) {
+ item.setTitle(R.string.filedetails_sync_file);
+ }
+ } else {
+ toHide.add(R.id.open_file_item);
+ }
+ if ( mContainerActivity.getFileDownloaderBinder().isDownloading(AccountUtils.getCurrentOwnCloudAccount(getActivity()), targetFile)) {
+ toHide.add(R.id.download_file_item);
+ toHide.add(R.id.cancel_upload_item);
+ toDisable.add(R.id.open_file_item);
+ toDisable.add(R.id.rename_file_item);
+ toDisable.add(R.id.remove_file_item);
+
+ } else if ( mContainerActivity.getFileUploaderBinder().isUploading(AccountUtils.getCurrentOwnCloudAccount(getActivity()), targetFile)) {
+ toHide.add(R.id.download_file_item);
+ toHide.add(R.id.cancel_download_item);
+ toDisable.add(R.id.open_file_item);
+ toDisable.add(R.id.rename_file_item);
+ toDisable.add(R.id.remove_file_item);
+
+ } else {
+ toHide.add(R.id.cancel_download_item);
+ toHide.add(R.id.cancel_upload_item);
+ }
+ }
+
+ for (int i : toHide) {
+ item = menu.findItem(i);
+ if (item != null) {
+ item.setVisible(false);
+ item.setEnabled(false);
+ }
+ }
+
+ for (int i : toDisable) {
+ item = menu.findItem(i);
+ if (item != null) {
+ item.setEnabled(false);
+ }
+ }
+ }
+
+
+ /**
+ * {@inhericDoc}
+ */
+ @Override
+ public boolean onContextItemSelected (MenuItem item) {
+ AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
+ mTargetFile = (OCFile) mAdapter.getItem(info.position);
+ switch (item.getItemId()) {
+ case R.id.rename_file_item: {
+ EditNameDialog dialog = EditNameDialog.newInstance(getString(R.string.rename_dialog_title), mTargetFile.getFileName(), this);
+ dialog.show(getFragmentManager(), EditNameDialog.TAG);
+ return true;
+ }
+ case R.id.remove_file_item: {
+ int messageStringId = R.string.confirmation_remove_alert;
+ int posBtnStringId = R.string.confirmation_remove_remote;
+ int neuBtnStringId = -1;
+ if (mTargetFile.isDirectory()) {
+ messageStringId = R.string.confirmation_remove_folder_alert;
+ posBtnStringId = R.string.confirmation_remove_remote_and_local;
+ neuBtnStringId = R.string.confirmation_remove_folder_local;
+ } else if (mTargetFile.isDown()) {
+ posBtnStringId = R.string.confirmation_remove_remote_and_local;
+ neuBtnStringId = R.string.confirmation_remove_local;
+ }
+ ConfirmationDialogFragment confDialog = ConfirmationDialogFragment.newInstance(
+ messageStringId,
+ new String[]{mTargetFile.getFileName()},
+ posBtnStringId,
+ neuBtnStringId,
+ R.string.common_cancel);
+ confDialog.setOnConfirmationListener(this);
+ confDialog.show(getFragmentManager(), FileDetailFragment.FTAG_CONFIRMATION);
+ return true;
+ }
+ case R.id.open_file_item: {
+ String storagePath = mTargetFile.getStoragePath();
+ String encodedStoragePath = WebdavUtils.encodePath(storagePath);
+ try {
+ Intent i = new Intent(Intent.ACTION_VIEW);
+ i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), mTargetFile.getMimetype());
+ i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ startActivity(i);
+
+ } catch (Throwable t) {
+ Log.e(TAG, "Fail when trying to open with the mimeType provided from the ownCloud server: " + mTargetFile.getMimetype());
+ boolean toastIt = true;
+ String mimeType = "";
+ try {
+ Intent i = new Intent(Intent.ACTION_VIEW);
+ mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(storagePath.substring(storagePath.lastIndexOf('.') + 1));
+ if (mimeType == null || !mimeType.equals(mTargetFile.getMimetype())) {
+ if (mimeType != null) {
+ i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), mimeType);
+ } else {
+ // desperate try
+ i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), "*/*");
+ }
+ i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ startActivity(i);
+ toastIt = false;
+ }
+
+ } catch (IndexOutOfBoundsException e) {
+ Log.e(TAG, "Trying to find out MIME type of a file without extension: " + storagePath);
+
+ } catch (ActivityNotFoundException e) {
+ Log.e(TAG, "No activity found to handle: " + storagePath + " with MIME type " + mimeType + " obtained from extension");
+
+ } catch (Throwable th) {
+ Log.e(TAG, "Unexpected problem when opening: " + storagePath, th);
+
+ } finally {
+ if (toastIt) {
+ Toast.makeText(getActivity(), "There is no application to handle file " + mTargetFile.getFileName(), Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ }
+ return true;
+ }
+ case R.id.download_file_item: {
+ Account account = AccountUtils.getCurrentOwnCloudAccount(getSherlockActivity());
+ RemoteOperation operation = new SynchronizeFileOperation(mTargetFile, null, mContainerActivity.getStorageManager(), account, true, false, getSherlockActivity());
+ WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(account, getSherlockActivity().getApplicationContext());
+ operation.execute(wc, mContainerActivity, mHandler);
+ getSherlockActivity().showDialog(FileDisplayActivity.DIALOG_SHORT_WAIT);
+ return true;
+ }
+ case R.id.cancel_download_item: {
+ FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();
+ Account account = AccountUtils.getCurrentOwnCloudAccount(getActivity());
+ if (downloaderBinder != null && downloaderBinder.isDownloading(account, mTargetFile)) {
+ downloaderBinder.cancel(account, mTargetFile);
+ listDirectory();
+ mContainerActivity.onTransferStateChanged(mTargetFile, false, false);
+ }
+ return true;
+ }
+ case R.id.cancel_upload_item: {
+ FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder();
+ Account account = AccountUtils.getCurrentOwnCloudAccount(getActivity());
+ if (uploaderBinder != null && uploaderBinder.isUploading(account, mTargetFile)) {
+ uploaderBinder.cancel(account, mTargetFile);
+ listDirectory();
+ mContainerActivity.onTransferStateChanged(mTargetFile, false, false);
+ }
+ return true;
+ }
+ default:
+ return super.onContextItemSelected(item);
+ }
+ }
+
/**
* Call this, when the user presses the up button
*
* @author David A. Velasco
*/
- public interface ContainerActivity extends TransferServiceGetter {
+ public interface ContainerActivity extends TransferServiceGetter, OnRemoteOperationListener {
/**
* Callback method invoked when a directory is clicked by the user on the files list
public OCFile getInitialDirectory();
+ /**
+ * Callback method invoked when a the 'transfer state' of a file changes.
+ *
+ * This happens when a download or upload is started or ended for a file.
+ *
+ * This method is necessary by now to update the user interface of the double-pane layout in tablets
+ * because methods {@link FileDownloaderBinder#isDownloading(Account, OCFile)} and {@link FileUploaderBinder#isUploading(Account, OCFile)}
+ * won't provide the needed response before the method where this is called finishes.
+ *
+ * TODO Remove this when the transfer state of a file is kept in the database (other thing TODO)
+ *
+ * @param file OCFile which state changed.
+ * @param downloading Flag signaling if the file is now downloading.
+ * @param uploading Flag signaling if the file is now uploading.
+ */
+ public void onTransferStateChanged(OCFile file, boolean downloading, boolean uploading);
+
}
+
+
+ @Override
+ public void onDismiss(EditNameDialog dialog) {
+ if (dialog.getResult()) {
+ String newFilename = dialog.getNewFilename();
+ Log.d(TAG, "name edit dialog dismissed with new name " + newFilename);
+ RemoteOperation operation = new RenameFileOperation(mTargetFile,
+ AccountUtils.getCurrentOwnCloudAccount(getActivity()),
+ newFilename,
+ mContainerActivity.getStorageManager());
+ WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(AccountUtils.getCurrentOwnCloudAccount(getSherlockActivity()), getSherlockActivity().getApplicationContext());
+ operation.execute(wc, mContainerActivity, mHandler);
+ getActivity().showDialog(FileDisplayActivity.DIALOG_SHORT_WAIT);
+ }
+ }
+
+
+ @Override
+ public void onConfirmation(String callerTag) {
+ if (callerTag.equals(FileDetailFragment.FTAG_CONFIRMATION)) {
+ if (mContainerActivity.getStorageManager().getFileById(mTargetFile.getFileId()) != null) {
+ RemoteOperation operation = new RemoveFileOperation( mTargetFile,
+ true,
+ mContainerActivity.getStorageManager());
+ WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(AccountUtils.getCurrentOwnCloudAccount(getSherlockActivity()), getSherlockActivity().getApplicationContext());
+ operation.execute(wc, mContainerActivity, mHandler);
+
+ getActivity().showDialog(FileDisplayActivity.DIALOG_SHORT_WAIT);
+ }
+ }
+ }
+
+ @Override
+ public void onNeutral(String callerTag) {
+ File f = null;
+ if (mTargetFile.isDirectory()) {
+ // TODO run in a secondary thread?
+ mContainerActivity.getStorageManager().removeDirectory(mTargetFile, false, true);
+
+ } else if (mTargetFile.isDown() && (f = new File(mTargetFile.getStoragePath())).exists()) {
+ f.delete();
+ mTargetFile.setStoragePath(null);
+ mContainerActivity.getStorageManager().saveFile(mTargetFile);
+ }
+ listDirectory();
+ mContainerActivity.onTransferStateChanged(mTargetFile, false, false);
+ }
+
+ @Override
+ public void onCancel(String callerTag) {
+ Log.d(TAG, "REMOVAL CANCELED");
+ }
+
}