From: David A. Velasco Date: Wed, 6 Nov 2013 09:36:34 +0000 (+0100) Subject: Merge branch 'develop' into setup_buttons X-Git-Tag: oc-android-1.5.5~139^2~3 X-Git-Url: http://git.linex4red.de/pub/Android/ownCloud.git/commitdiff_plain/d252a09f53999a90036048d4db33c8b4f2f04c4f?ds=sidebyside;hp=-c Merge branch 'develop' into setup_buttons --- d252a09f53999a90036048d4db33c8b4f2f04c4f diff --combined AndroidManifest.xml index f448d533,951301ec..2035a9de --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@@ -92,9 -92,9 +92,9 @@@ - + - @@@ -153,8 -153,6 +153,6 @@@ - - diff --combined src/com/owncloud/android/Uploader.java index 8e69516e,a15ef3ab..29a4d6a4 --- a/src/com/owncloud/android/Uploader.java +++ b/src/com/owncloud/android/Uploader.java @@@ -26,13 -26,6 +26,12 @@@ import java.util.List import java.util.Stack; import java.util.Vector; +import com.owncloud.android.R; +import com.owncloud.android.authentication.AccountAuthenticator; - import com.owncloud.android.datamodel.DataStorageManager; +import com.owncloud.android.datamodel.FileDataStorageManager; +import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.files.services.FileUploader; + import android.accounts.Account; import android.accounts.AccountManager; import android.app.AlertDialog; @@@ -61,8 -54,13 +60,7 @@@ import android.widget.EditText import android.widget.SimpleAdapter; import android.widget.Toast; -import com.owncloud.android.authentication.AccountAuthenticator; -import com.owncloud.android.datamodel.FileDataStorageManager; -import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.files.services.FileUploader; - -import com.owncloud.android.R; - /** * This can be used to upload things to an ownCloud instance. * @@@ -78,7 -76,7 +76,7 @@@ public class Uploader extends ListActiv private ArrayList mStreamsToUpload; private boolean mCreateDir; private String mUploadPath; - private DataStorageManager mStorageManager; + private FileDataStorageManager mStorageManager; private OCFile mFile; private final static int DIALOG_NO_ACCOUNT = 0; @@@ -231,12 -229,12 +229,12 @@@ public void onItemClick(AdapterView parent, View view, int position, long id) { // click on folder in the list Log_OC.d(TAG, "on item click"); - Vector tmpfiles = mStorageManager.getDirectoryContent(mFile); + Vector tmpfiles = mStorageManager.getFolderContent(mFile); if (tmpfiles.size() <= 0) return; // filter on dirtype Vector files = new Vector(); for (OCFile f : tmpfiles) - if (f.isDirectory()) + if (f.isFolder()) files.add(f); if (files.size() < position) { throw new IndexOutOfBoundsException("Incorrect item selected"); @@@ -296,11 -294,11 +294,11 @@@ mFile = mStorageManager.getFileByPath(full_path); if (mFile != null) { - Vector files = mStorageManager.getDirectoryContent(mFile); + Vector files = mStorageManager.getFolderContent(mFile); List> data = new LinkedList>(); for (OCFile f : files) { HashMap h = new HashMap(); - if (f.isDirectory()) { + if (f.isFolder()) { h.put("dirname", f.getFileName()); data.add(h); } diff --combined src/com/owncloud/android/datamodel/FileDataStorageManager.java index 9933e360,6597616a..635e10ca --- a/src/com/owncloud/android/datamodel/FileDataStorageManager.java +++ b/src/com/owncloud/android/datamodel/FileDataStorageManager.java @@@ -20,9 -20,9 +20,9 @@@ package com.owncloud.android.datamodel import java.io.File; import java.util.ArrayList; + import java.util.Collection; import java.util.Collections; import java.util.Iterator; - import java.util.List; import java.util.Vector; import com.owncloud.android.Log_OC; @@@ -30,39 -30,67 +30,68 @@@ import com.owncloud.android.MainApp import com.owncloud.android.db.ProviderMeta.ProviderTableMeta; import com.owncloud.android.utils.FileStorageUtils; + import android.accounts.Account; import android.content.ContentProviderClient; import android.content.ContentProviderOperation; import android.content.ContentProviderResult; import android.content.ContentResolver; + import android.content.ContentUris; import android.content.ContentValues; import android.content.OperationApplicationException; import android.database.Cursor; import android.net.Uri; import android.os.RemoteException; - public class FileDataStorageManager implements DataStorageManager { + public class FileDataStorageManager { + + public static final int ROOT_PARENT_ID = 0; private ContentResolver mContentResolver; - private ContentProviderClient mContentProvider; + private ContentProviderClient mContentProviderClient; private Account mAccount; - private static String TAG = "FileDataStorageManager"; + private static String TAG = FileDataStorageManager.class.getSimpleName(); + public FileDataStorageManager(Account account, ContentResolver cr) { - mContentProvider = null; + mContentProviderClient = null; mContentResolver = cr; mAccount = account; } public FileDataStorageManager(Account account, ContentProviderClient cp) { - mContentProvider = cp; + mContentProviderClient = cp; mContentResolver = null; mAccount = account; } - @Override + + public void setAccount(Account account) { + mAccount = account; + } + + public Account getAccount() { + return mAccount; + } + + public void setContentResolver(ContentResolver cr) { + mContentResolver = cr; + } + + public ContentResolver getContentResolver() { + return mContentResolver; + } + + public void setContentProviderClient(ContentProviderClient cp) { + mContentProviderClient = cp; + } + + public ContentProviderClient getContentProviderClient() { + return mContentProviderClient; + } + + public OCFile getFileByPath(String path) { Cursor c = getCursorForValue(ProviderTableMeta.FILE_PATH, path); OCFile file = null; @@@ -70,22 -98,13 +99,13 @@@ file = createFileInstance(c); } c.close(); - if (file == null && OCFile.PATH_SEPARATOR.equals(path)) { + if (file == null && OCFile.ROOT_PATH.equals(path)) { return createRootDir(); // root should always exist } return file; } - private OCFile createRootDir() { - OCFile file = new OCFile(OCFile.PATH_SEPARATOR); - file.setMimetype("DIR"); - file.setParentId(DataStorageManager.ROOT_PARENT_ID); - saveFile(file); - return file; - } - - @Override public OCFile getFileById(long id) { Cursor c = getCursorForValue(ProviderTableMeta._ID, String.valueOf(id)); OCFile file = null; @@@ -106,17 -125,42 +126,42 @@@ return file; } - @Override public boolean fileExists(long id) { return fileExists(ProviderTableMeta._ID, String.valueOf(id)); } - @Override public boolean fileExists(String path) { return fileExists(ProviderTableMeta.FILE_PATH, path); } - @Override + + public Vector getFolderContent(OCFile f) { + if (f != null && f.isFolder() && f.getFileId() != -1) { + return getFolderContent(f.getFileId()); + + } else { + return new Vector(); + } + } + + + public Vector getFolderImages(OCFile folder) { + Vector ret = new Vector(); + if (folder != null) { + // TODO better implementation, filtering in the access to database (if possible) instead of here + Vector tmp = getFolderContent(folder); + OCFile current = null; + for (int i=0; i files) { + /** + * Inserts or updates the list of files contained in a given folder. + * + * CALLER IS THE RESPONSIBLE FOR GRANTING RIGHT UPDATE OF INFORMATION, NOT THIS METHOD. + * HERE ONLY DATA CONSISTENCY SHOULD BE GRANTED + * + * @param folder + * @param files + * @param removeNotUpdated + */ + public void saveFolder(OCFile folder, Collection updatedFiles, Collection filesToRemove) { + + Log_OC.d(TAG, "Saving folder " + folder.getRemotePath() + " with " + updatedFiles.size() + " children and " + filesToRemove.size() + " files to remove"); - Iterator filesIt = files.iterator(); - ArrayList operations = new ArrayList(files.size()); - OCFile file = null; + ArrayList operations = new ArrayList(updatedFiles.size()); - // prepare operations to perform - while (filesIt.hasNext()) { - file = filesIt.next(); + // prepare operations to insert or update files to save in the given folder + for (OCFile file : updatedFiles) { ContentValues cv = new ContentValues(); cv.put(ProviderTableMeta.FILE_MODIFIED, file.getModificationTimestamp()); cv.put(ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA, file.getModificationTimestampAtLastSyncForData()); @@@ -222,43 -266,21 +267,21 @@@ cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, file.getFileLength()); cv.put(ProviderTableMeta.FILE_CONTENT_TYPE, file.getMimetype()); cv.put(ProviderTableMeta.FILE_NAME, file.getFileName()); - if (file.getParentId() != 0) - cv.put(ProviderTableMeta.FILE_PARENT, file.getParentId()); + //cv.put(ProviderTableMeta.FILE_PARENT, file.getParentId()); + cv.put(ProviderTableMeta.FILE_PARENT, folder.getFileId()); cv.put(ProviderTableMeta.FILE_PATH, file.getRemotePath()); - if (!file.isDirectory()) + if (!file.isFolder()) { cv.put(ProviderTableMeta.FILE_STORAGE_PATH, file.getStoragePath()); + } cv.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, mAccount.name); 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); + cv.put(ProviderTableMeta.FILE_ETAG, file.getEtag()); - if (fileExists(file.getRemotePath())) { - OCFile oldFile = getFileByPath(file.getRemotePath()); - file.setFileId(oldFile.getFileId()); - - if (file.isDirectory()) { - cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, oldFile.getFileLength()); - file.setFileLength(oldFile.getFileLength()); - } - - operations.add(ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI). - withValues(cv). - withSelection( ProviderTableMeta._ID + "=?", - new String[] { String.valueOf(file.getFileId()) }) - .build()); - - } else if (fileExists(file.getFileId())) { - OCFile oldFile = getFileById(file.getFileId()); - if (file.getStoragePath() == null && oldFile.getStoragePath() != null) - file.setStoragePath(oldFile.getStoragePath()); - - if (!file.isDirectory()) - cv.put(ProviderTableMeta.FILE_STORAGE_PATH, file.getStoragePath()); - else { - cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, oldFile.getFileLength()); - file.setFileLength(oldFile.getFileLength()); - } - + boolean existsByPath = fileExists(file.getRemotePath()); + if (existsByPath || fileExists(file.getFileId())) { + // updating an existing file operations.add(ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI). withValues(cv). withSelection( ProviderTableMeta._ID + "=?", @@@ -266,81 -288,277 +289,277 @@@ .build()); } else { + // adding a new file operations.add(ContentProviderOperation.newInsert(ProviderTableMeta.CONTENT_URI).withValues(cv).build()); } } + + // prepare operations to remove files in the given folder + String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?" + " AND " + ProviderTableMeta.FILE_PATH + "=?"; + String [] whereArgs = null; + for (OCFile file : filesToRemove) { + if (file.getParentId() == folder.getFileId()) { + whereArgs = new String[]{mAccount.name, file.getRemotePath()}; + //Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_FILE, "" + file.getFileId()); + if (file.isFolder()) { + operations.add(ContentProviderOperation + .newDelete(ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_DIR, file.getFileId())).withSelection(where, whereArgs) + .build()); + // TODO remove local folder + } else { + operations.add(ContentProviderOperation + .newDelete(ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILE, file.getFileId())).withSelection(where, whereArgs) + .build()); + if (file.isDown()) { + new File(file.getStoragePath()).delete(); + // TODO move the deletion of local contents after success of deletions + } + } + } + } + + // update metadata of folder + ContentValues cv = new ContentValues(); + cv.put(ProviderTableMeta.FILE_MODIFIED, folder.getModificationTimestamp()); + cv.put(ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA, folder.getModificationTimestampAtLastSyncForData()); + cv.put(ProviderTableMeta.FILE_CREATION, folder.getCreationTimestamp()); + cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, 0); // FileContentProvider calculates the right size + cv.put(ProviderTableMeta.FILE_CONTENT_TYPE, folder.getMimetype()); + cv.put(ProviderTableMeta.FILE_NAME, folder.getFileName()); + cv.put(ProviderTableMeta.FILE_PARENT, folder.getParentId()); + cv.put(ProviderTableMeta.FILE_PATH, folder.getRemotePath()); + cv.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, mAccount.name); + cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE, folder.getLastSyncDateForProperties()); + cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, folder.getLastSyncDateForData()); + cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, folder.keepInSync() ? 1 : 0); + cv.put(ProviderTableMeta.FILE_ETAG, folder.getEtag()); + operations.add(ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI). + withValues(cv). + withSelection( ProviderTableMeta._ID + "=?", + new String[] { String.valueOf(folder.getFileId()) }) + .build()); // apply operations in batch ContentProviderResult[] results = null; + Log_OC.d(TAG, "Sending " + operations.size() + " operations to FileContentProvider"); try { if (getContentResolver() != null) { results = getContentResolver().applyBatch(MainApp.getAuthority(), operations); } else { - results = getContentProvider().applyBatch(operations); + results = getContentProviderClient().applyBatch(operations); } } catch (OperationApplicationException e) { - Log_OC.e(TAG, "Fail to update/insert list of files to database " + e.getMessage()); + Log_OC.e(TAG, "Exception in batch of operations " + e.getMessage()); } catch (RemoteException e) { - Log_OC.e(TAG, "Fail to update/insert list of files to database " + e.getMessage()); + Log_OC.e(TAG, "Exception in batch of operations " + e.getMessage()); } // update new id in file objects for insertions if (results != null) { long newId; + Iterator filesIt = updatedFiles.iterator(); + OCFile file = null; for (int i=0; i FileDataStorageManager.ROOT_PARENT_ID) { + Log_OC.d(TAG, "Updating size of " + id); + if (getContentResolver() != null) { + getContentResolver().update(ProviderTableMeta.CONTENT_URI_DIR, null, + ProviderTableMeta._ID + "=?", + new String[] { String.valueOf(id) }); + } else { + try { + getContentProviderClient().update(ProviderTableMeta.CONTENT_URI_DIR, null, + ProviderTableMeta._ID + "=?", + new String[] { String.valueOf(id) }); + + } catch (RemoteException e) { + Log_OC.e(TAG, "Exception in update of folder size through compatibility patch " + e.getMessage()); + } + } + } else { + Log_OC.e(TAG, "not updating size for folder " + id); + } } + - public void setAccount(Account account) { - mAccount = account; + public void removeFile(OCFile file, boolean removeDBData, boolean removeLocalCopy) { + if (file != null) { + if (file.isFolder()) { + removeFolder(file, removeDBData, removeLocalCopy); + + } else { + if (removeDBData) { + //Uri file_uri = Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_FILE, ""+file.getFileId()); + Uri file_uri = ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILE, file.getFileId()); + String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?" + " AND " + ProviderTableMeta.FILE_PATH + "=?"; + String [] whereArgs = new String[]{mAccount.name, file.getRemotePath()}; + if (getContentProviderClient() != null) { + try { + getContentProviderClient().delete(file_uri, where, whereArgs); + } catch (RemoteException e) { + e.printStackTrace(); + } + } else { + getContentResolver().delete(file_uri, where, whereArgs); + } + updateFolderSize(file.getParentId()); + } + if (removeLocalCopy && file.isDown()) { + boolean success = new File(file.getStoragePath()).delete(); + if (!removeDBData && success) { + // maybe unnecessary, but should be checked TODO remove if unnecessary + file.setStoragePath(null); + saveFile(file); + } + } + } + } } + - public Account getAccount() { - return mAccount; + public void removeFolder(OCFile folder, boolean removeDBData, boolean removeLocalContent) { + if (folder != null && folder.isFolder()) { + if (removeDBData && folder.getFileId() != -1) { + removeFolderInDb(folder); + } + if (removeLocalContent) { + File localFolder = new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, folder)); + removeLocalFolder(localFolder); + } + } } - public void setContentResolver(ContentResolver cr) { - mContentResolver = cr; + private void removeFolderInDb(OCFile folder) { + Uri folder_uri = Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_DIR, ""+ folder.getFileId()); // URI for recursive deletion + String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?" + " AND " + ProviderTableMeta.FILE_PATH + "=?"; + String [] whereArgs = new String[]{mAccount.name, folder.getRemotePath()}; + if (getContentProviderClient() != null) { + try { + getContentProviderClient().delete(folder_uri, where, whereArgs); + } catch (RemoteException e) { + e.printStackTrace(); + } + } else { + getContentResolver().delete(folder_uri, where, whereArgs); + } + updateFolderSize(folder.getParentId()); } - public ContentResolver getContentResolver() { - return mContentResolver; + private void removeLocalFolder(File folder) { + if (folder.exists()) { + File[] files = folder.listFiles(); + if (files != null) { + for (File file : files) { + if (file.isDirectory()) { + removeLocalFolder(file); + } else { + file.delete(); + } + } + } + folder.delete(); + } } - public void setContentProvider(ContentProviderClient cp) { - mContentProvider = cp; - } + /** + * Updates database for a folder that was moved to a different location. + * + * TODO explore better (faster) implementations + * TODO throw exceptions up ! + */ + public void moveFolder(OCFile folder, String newPath) { + // TODO check newPath - public ContentProviderClient getContentProvider() { - return mContentProvider; - } - - @Override - public Vector getDirectoryContent(OCFile f) { - if (f != null && f.isDirectory() && f.getFileId() != -1) { - return getDirectoryContent(f.getFileId()); + if (folder != null && folder.isFolder() && folder.fileExists() && !OCFile.ROOT_PATH.equals(folder.getFileName())) { + /// 1. get all the descendants of 'dir' in a single QUERY (including 'dir') + Cursor c = null; + if (getContentProviderClient() != null) { + try { + c = getContentProviderClient().query(ProviderTableMeta.CONTENT_URI, + null, + ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " + ProviderTableMeta.FILE_PATH + " LIKE ? ", + new String[] { mAccount.name, folder.getRemotePath() + "%" }, ProviderTableMeta.FILE_PATH + " ASC "); + } catch (RemoteException e) { + Log_OC.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, folder.getRemotePath() + "%" }, ProviderTableMeta.FILE_PATH + " ASC "); + } + + /// 2. prepare a batch of update operations to change all the descendants + ArrayList operations = new ArrayList(c.getCount()); + int lengthOfOldPath = folder.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(MainApp.getAuthority(), operations); + + } else { + getContentProviderClient().applyBatch(operations); + } + + } catch (OperationApplicationException e) { + Log_OC.e(TAG, "Fail to update descendants of " + folder.getFileId() + " in database", e); + + } catch (RemoteException e) { + Log_OC.e(TAG, "Fail to update desendants of " + folder.getFileId() + " in database", e); + } - } else { - return new Vector(); } } - private Vector getDirectoryContent(long parentId) { + + private Vector getFolderContent(long parentId) { Vector ret = new Vector(); @@@ -349,9 -567,9 +568,9 @@@ String.valueOf(parentId)); Cursor c = null; - if (getContentProvider() != null) { + if (getContentProviderClient() != null) { try { - c = getContentProvider().query(req_uri, null, + c = getContentProviderClient().query(req_uri, null, ProviderTableMeta.FILE_PARENT + "=?" , new String[] { String.valueOf(parentId)}, null); } catch (RemoteException e) { @@@ -379,6 -597,13 +598,13 @@@ } + private OCFile createRootDir() { + OCFile file = new OCFile(OCFile.ROOT_PATH); + file.setMimetype("DIR"); + file.setParentId(FileDataStorageManager.ROOT_PARENT_ID); + saveFile(file); + return file; + } private boolean fileExists(String cmp_key, String value) { Cursor c; @@@ -392,7 -617,7 +618,7 @@@ new String[] { value, mAccount.name }, null); } else { try { - c = getContentProvider().query( + c = getContentProviderClient().query( ProviderTableMeta.CONTENT_URI, null, cmp_key + "=? AND " @@@ -422,7 -647,7 +648,7 @@@ new String[] { value, mAccount.name }, null); } else { try { - c = getContentProvider().query( + c = getContentProviderClient().query( ProviderTableMeta.CONTENT_URI, null, key + "=? AND " + ProviderTableMeta.FILE_ACCOUNT_OWNER @@@ -446,7 -671,7 +672,7 @@@ .getColumnIndex(ProviderTableMeta.FILE_PARENT))); file.setMimetype(c.getString(c .getColumnIndex(ProviderTableMeta.FILE_CONTENT_TYPE))); - if (!file.isDirectory()) { + if (!file.isFolder()) { file.setStoragePath(c.getString(c .getColumnIndex(ProviderTableMeta.FILE_STORAGE_PATH))); if (file.getStoragePath() == null) { @@@ -472,219 -697,10 +698,10 @@@ getColumnIndex(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA))); file.setKeepInSync(c.getInt( c.getColumnIndex(ProviderTableMeta.FILE_KEEP_IN_SYNC)) == 1 ? true : false); + file.setEtag(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_ETAG))); + } return file; } - @Override - public void removeFile(OCFile file, boolean removeLocalCopy) { - Uri file_uri = Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_FILE, ""+file.getFileId()); - if (getContentProvider() != null) { - try { - getContentProvider().delete(file_uri, - ProviderTableMeta.FILE_ACCOUNT_OWNER+"=?", - new String[]{mAccount.name}); - } catch (RemoteException e) { - e.printStackTrace(); - } - } else { - getContentResolver().delete(file_uri, - ProviderTableMeta.FILE_ACCOUNT_OWNER+"=?", - new String[]{mAccount.name}); - } - 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(); - } - } - - if (file.getFileLength() > 0) { - updateSizesToTheRoot(file.getParentId()); - } - } - - @Override - public void removeDirectory(OCFile dir, boolean removeDBData, boolean removeLocalContent) { - // TODO consider possible failures - if (dir != null && dir.isDirectory() && dir.getFileId() != -1) { - Vector children = getDirectoryContent(dir); - if (children.size() > 0) { - OCFile child = null; - for (int i=0; i 0) { - updateSizesToTheRoot(dir.getParentId()); - } - } - } - - - /** - * 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() + "%" }, ProviderTableMeta.FILE_PATH + " ASC "); - } catch (RemoteException e) { - Log_OC.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() + "%" }, ProviderTableMeta.FILE_PATH + " ASC "); - } - - /// 2. prepare a batch of update operations to change all the descendants - ArrayList operations = new ArrayList(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(MainApp.getAuthority(), operations); - - } else { - getContentProvider().applyBatch(operations); - } - - } catch (OperationApplicationException e) { - Log_OC.e(TAG, "Fail to update descendants of " + dir.getFileId() + " in database", e); - - } catch (RemoteException e) { - Log_OC.e(TAG, "Fail to update desendants of " + dir.getFileId() + " in database", e); - } - - } - } - - @Override - public Vector getDirectoryImages(OCFile directory) { - Vector ret = new Vector(); - if (directory != null) { - // TODO better implementation, filtering in the access to database (if possible) instead of here - Vector tmp = getDirectoryContent(directory); - OCFile current = null; - for (int i=0; i files = getDirectoryContent(id); - - for (OCFile f: files) - { - folderSize = folderSize + f.getFileLength(); - } - - updateSize(id, folderSize); - } - - /** - * Update the size value of an OCFile in DB - */ - private int updateSize(long id, long size) { - ContentValues cv = new ContentValues(); - cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, size); - int result = -1; - if (getContentResolver() != null) { - result = getContentResolver().update(ProviderTableMeta.CONTENT_URI, cv, ProviderTableMeta._ID + "=?", - new String[] { String.valueOf(id) }); - } else { - try { - result = getContentProvider().update(ProviderTableMeta.CONTENT_URI, cv, ProviderTableMeta._ID + "=?", - new String[] { String.valueOf(id) }); - } catch (RemoteException e) { - Log_OC.e(TAG,"Fail to update size column into database " + e.getMessage()); - } - } - return result; - } - - /** - * Update the size of a subtree of folder from a file to the root - * @param parentId: parent of the file - */ - private void updateSizesToTheRoot(long parentId) { - - OCFile file; - - while (parentId != 0) { - - // Update the size of the parent - calculateFolderSize(parentId); - - // search the next parent - file = getFileById(parentId); - parentId = file.getParentId(); - - } - - } - } diff --combined src/com/owncloud/android/datamodel/OCFile.java index 76fb6b2f,9bd84abd..67de5009 --- a/src/com/owncloud/android/datamodel/OCFile.java +++ b/src/com/owncloud/android/datamodel/OCFile.java @@@ -22,7 -22,6 +22,7 @@@ import java.io.File import com.owncloud.android.Log_OC; + import android.os.Parcel; import android.os.Parcelable; import android.webkit.MimeTypeMap; @@@ -42,6 -41,7 +42,7 @@@ public class OCFile implements Parcelab }; public static final String PATH_SEPARATOR = "/"; + public static final String ROOT_PATH = PATH_SEPARATOR; private static final String TAG = OCFile.class.getSimpleName(); @@@ -61,6 -61,7 +62,7 @@@ private String mEtag; + /** * Create new {@link OCFile} with given path. * @@@ -96,6 -97,7 +98,7 @@@ mKeepInSync = source.readInt() == 1; mLastSyncDateForProperties = source.readLong(); mLastSyncDateForData = source.readLong(); + mEtag = source.readString(); } @Override @@@ -113,6 -115,7 +116,7 @@@ dest.writeInt(mKeepInSync ? 1 : 0); dest.writeLong(mLastSyncDateForProperties); dest.writeLong(mLastSyncDateForData); + dest.writeString(mEtag); } /** @@@ -144,11 -147,11 +148,11 @@@ } /** - * Use this to find out if this file is a Directory + * Use this to find out if this file is a folder. * - * @return true if it is a directory + * @return true if it is a folder */ - public boolean isDirectory() { + public boolean isFolder() { return mMimeType != null && mMimeType.equals("DIR"); } @@@ -255,7 -258,7 +259,7 @@@ */ public String getFileName() { File f = new File(getRemotePath()); - return f.getName().length() == 0 ? PATH_SEPARATOR : f.getName(); + return f.getName().length() == 0 ? ROOT_PATH : f.getName(); } /** @@@ -265,11 -268,11 +269,11 @@@ */ public void setFileName(String name) { Log_OC.d(TAG, "OCFile name changin from " + mRemotePath); - if (name != null && name.length() > 0 && !name.contains(PATH_SEPARATOR) && !mRemotePath.equals(PATH_SEPARATOR)) { + if (name != null && name.length() > 0 && !name.contains(PATH_SEPARATOR) && !mRemotePath.equals(ROOT_PATH)) { String parent = (new File(getRemotePath())).getParent(); parent = (parent.endsWith(PATH_SEPARATOR)) ? parent : parent + PATH_SEPARATOR; mRemotePath = parent + name; - if (isDirectory()) { + if (isFolder()) { mRemotePath += PATH_SEPARATOR; } Log_OC.d(TAG, "OCFile name changed to " + mRemotePath); @@@ -294,7 -297,7 +298,7 @@@ * not a directory */ public void addFile(OCFile file) throws IllegalStateException { - if (isDirectory()) { + if (isFolder()) { file.mParentId = mId; mNeedsUpdating = true; return; @@@ -320,6 -323,7 +324,7 @@@ mLastSyncDateForData = 0; mKeepInSync = false; mNeedsUpdating = false; + mEtag = null; } /** @@@ -416,11 -420,11 +421,11 @@@ @Override public int compareTo(OCFile another) { - if (isDirectory() && another.isDirectory()) { + if (isFolder() && another.isFolder()) { return getRemotePath().toLowerCase().compareTo(another.getRemotePath().toLowerCase()); - } else if (isDirectory()) { + } else if (isFolder()) { return -1; - } else if (another.isDirectory()) { + } else if (another.isFolder()) { return 1; } return getRemotePath().toLowerCase().compareTo(another.getRemotePath().toLowerCase()); @@@ -440,8 -444,8 +445,8 @@@ @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, Long.valueOf(mId), getFileName(), mMimeType, isDown(), mLocalPath, mRemotePath, Long.valueOf(mParentId), Boolean.valueOf(mKeepInSync)); + String asString = "[id=%s, name=%s, mime=%s, downloaded=%s, local=%s, remote=%s, parentId=%s, keepInSinc=%s etag=%s]"; + asString = String.format(asString, Long.valueOf(mId), getFileName(), mMimeType, isDown(), mLocalPath, mRemotePath, Long.valueOf(mParentId), Boolean.valueOf(mKeepInSync), mEtag); return asString; } @@@ -449,6 -453,10 +454,10 @@@ return mEtag; } + public void setEtag(String etag) { + this.mEtag = etag; + } + public long getLocalModificationTimestamp() { if (mLocalPath != null && mLocalPath.length() > 0) { File f = new File(mLocalPath); diff --combined src/com/owncloud/android/files/OwnCloudFileObserver.java index 8f2e3ee6,5383520e..d887256e --- a/src/com/owncloud/android/files/OwnCloudFileObserver.java +++ b/src/com/owncloud/android/files/OwnCloudFileObserver.java @@@ -29,7 -29,6 +29,7 @@@ import com.owncloud.android.operations. import com.owncloud.android.ui.activity.ConflictsResolveActivity; + import android.accounts.Account; import android.content.Context; import android.content.Intent; @@@ -84,7 -83,6 +84,6 @@@ public class OwnCloudFileObserver exten storageManager, mOCAccount, true, - true, mContext); RemoteOperationResult result = sfo.execute(mOCAccount, mContext); if (result.getCode() == ResultCode.SYNC_CONFLICT) { diff --combined src/com/owncloud/android/files/services/FileDownloader.java index 09103a1e,1e2bffa6..2e479f4d --- a/src/com/owncloud/android/files/services/FileDownloader.java +++ b/src/com/owncloud/android/files/services/FileDownloader.java @@@ -28,12 -28,11 +28,12 @@@ import java.util.Vector import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import com.owncloud.android.Log_OC; +import com.owncloud.android.MainApp; +import com.owncloud.android.R; import com.owncloud.android.authentication.AuthenticatorActivity; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; -import eu.alefzero.webdav.OnDatatransferProgressListener; - import com.owncloud.android.network.OwnCloudClientUtils; import com.owncloud.android.operations.DownloadFileOperation; import com.owncloud.android.operations.RemoteOperationResult; @@@ -43,9 -42,6 +43,9 @@@ import com.owncloud.android.ui.activity import com.owncloud.android.ui.preview.PreviewImageActivity; import com.owncloud.android.ui.preview.PreviewImageFragment; +import eu.alefzero.webdav.OnDatatransferProgressListener; + + import android.accounts.Account; import android.accounts.AccountsException; import android.app.Notification; @@@ -62,6 -58,9 +62,6 @@@ import android.os.Message import android.os.Process; import android.widget.RemoteViews; -import com.owncloud.android.Log_OC; -import com.owncloud.android.MainApp; -import com.owncloud.android.R; import eu.alefzero.webdav.WebdavClient; public class FileDownloader extends Service implements OnDatatransferProgressListener { @@@ -240,7 -239,7 +240,7 @@@ if (account == null || file == null) return false; String targetKey = buildRemoteName(account, file); synchronized (mPendingDownloads) { - if (file.isDirectory()) { + if (file.isFolder()) { // this can be slow if there are many downloads :( Iterator it = mPendingDownloads.keySet().iterator(); boolean found = false; diff --combined src/com/owncloud/android/files/services/FileUploader.java index 51f8238a,4d2df0fe..7f45b6b9 --- a/src/com/owncloud/android/files/services/FileUploader.java +++ b/src/com/owncloud/android/files/services/FileUploader.java @@@ -33,15 -33,10 +33,15 @@@ import org.apache.jackrabbit.webdav.Dav import org.apache.jackrabbit.webdav.MultiStatus; import org.apache.jackrabbit.webdav.client.methods.PropFindMethod; +import com.owncloud.android.Log_OC; +import com.owncloud.android.MainApp; +import com.owncloud.android.R; import com.owncloud.android.authentication.AccountAuthenticator; import com.owncloud.android.authentication.AuthenticatorActivity; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.db.DbHandler; +import com.owncloud.android.network.OwnCloudClientUtils; import com.owncloud.android.operations.ChunkedUploadFileOperation; import com.owncloud.android.operations.CreateFolderOperation; import com.owncloud.android.operations.ExistenceCheckOperation; @@@ -49,19 -44,13 +49,19 @@@ import com.owncloud.android.operations. import com.owncloud.android.operations.RemoteOperationResult; import com.owncloud.android.operations.UploadFileOperation; import com.owncloud.android.operations.RemoteOperationResult.ResultCode; +import com.owncloud.android.ui.activity.FailedUploadActivity; +import com.owncloud.android.ui.activity.FileActivity; +import com.owncloud.android.ui.activity.FileDisplayActivity; +import com.owncloud.android.ui.activity.InstantUploadActivity; +import com.owncloud.android.ui.preview.PreviewImageActivity; +import com.owncloud.android.ui.preview.PreviewImageFragment; 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; import android.accounts.Account; import android.accounts.AccountManager; @@@ -81,6 -70,16 +81,6 @@@ import android.os.Process import android.webkit.MimeTypeMap; import android.widget.RemoteViews; -import com.owncloud.android.Log_OC; -import com.owncloud.android.MainApp; -import com.owncloud.android.R; -import com.owncloud.android.db.DbHandler; -import com.owncloud.android.ui.activity.FailedUploadActivity; -import com.owncloud.android.ui.activity.FileActivity; -import com.owncloud.android.ui.activity.FileDisplayActivity; -import com.owncloud.android.ui.activity.InstantUploadActivity; -import com.owncloud.android.ui.preview.PreviewImageActivity; -import com.owncloud.android.ui.preview.PreviewImageFragment; import eu.alefzero.webdav.WebdavClient; @@@ -384,7 -383,7 +384,7 @@@ public class FileUploader extends Servi return false; String targetKey = buildRemoteName(account, file); synchronized (mPendingUploads) { - if (file.isDirectory()) { + if (file.isFolder()) { // this can be slow if there are many uploads :( Iterator it = mPendingUploads.keySet().iterator(); boolean found = false; diff --combined src/com/owncloud/android/operations/CreateFolderOperation.java index b3456ee2,c86d2fc3..f28ad29e --- a/src/com/owncloud/android/operations/CreateFolderOperation.java +++ b/src/com/owncloud/android/operations/CreateFolderOperation.java @@@ -23,10 -23,9 +23,10 @@@ import org.apache.commons.httpclient.Ht import org.apache.jackrabbit.webdav.client.methods.MkColMethod; import com.owncloud.android.Log_OC; - import com.owncloud.android.datamodel.DataStorageManager; + import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; + import eu.alefzero.webdav.WebdavClient; import eu.alefzero.webdav.WebdavUtils; @@@ -44,7 -43,7 +44,7 @@@ public class CreateFolderOperation exte protected String mRemotePath; protected boolean mCreateFullPath; - protected DataStorageManager mStorageManager; + protected FileDataStorageManager mStorageManager; /** * Constructor @@@ -53,7 -52,7 +53,7 @@@ * @param createFullPath 'True' means that all the ancestor folders should be created if don't exist yet. * @param storageManager Reference to the local database corresponding to the account where the file is contained. */ - public CreateFolderOperation(String remotePath, boolean createFullPath, DataStorageManager storageManager) { + public CreateFolderOperation(String remotePath, boolean createFullPath, FileDataStorageManager storageManager) { mRemotePath = remotePath; mCreateFullPath = createFullPath; mStorageManager = storageManager; diff --combined src/com/owncloud/android/operations/RemoteOperation.java index fbb93e61,604d62ea..6e674c46 --- a/src/com/owncloud/android/operations/RemoteOperation.java +++ b/src/com/owncloud/android/operations/RemoteOperation.java @@@ -26,7 -26,6 +26,7 @@@ import com.owncloud.android.network.Bea import com.owncloud.android.network.OwnCloudClientUtils; import com.owncloud.android.operations.RemoteOperationResult.ResultCode; + import android.accounts.Account; import android.accounts.AccountManager; import android.accounts.AccountsException; @@@ -137,14 -136,8 +137,8 @@@ public abstract class RemoteOperation i mCallerActivity = callerActivity; mClient = null; // the client instance will be created from mAccount and mContext in the runnerThread to create below - if (listener == null) { - throw new IllegalArgumentException("Trying to execute a remote operation asynchronously without a listener to notiy the result"); - } mListener = listener; - if (listenerHandler == null) { - throw new IllegalArgumentException("Trying to execute a remote operation asynchronously without a handler to the listener's thread"); - } mListenerHandler = listenerHandler; Thread runnerThread = new Thread(this); diff --combined src/com/owncloud/android/operations/RemoveFileOperation.java index f8a6c4ab,ffb3a91a..659037b6 --- a/src/com/owncloud/android/operations/RemoveFileOperation.java +++ b/src/com/owncloud/android/operations/RemoveFileOperation.java @@@ -21,10 -21,9 +21,10 @@@ import org.apache.commons.httpclient.Ht import org.apache.jackrabbit.webdav.client.methods.DeleteMethod; import com.owncloud.android.Log_OC; - import com.owncloud.android.datamodel.DataStorageManager; + import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; + import eu.alefzero.webdav.WebdavClient; import eu.alefzero.webdav.WebdavUtils; @@@ -42,7 -41,7 +42,7 @@@ public class RemoveFileOperation extend OCFile mFileToRemove; boolean mDeleteLocalCopy; - DataStorageManager mDataStorageManager; + FileDataStorageManager mDataStorageManager; /** @@@ -52,7 -51,7 +52,7 @@@ * @param deleteLocalCopy When 'true', and a local copy of the file exists, it is also removed. * @param storageManager Reference to the local database corresponding to the account where the file is contained. */ - public RemoveFileOperation(OCFile fileToRemove, boolean deleteLocalCopy, DataStorageManager storageManager) { + public RemoveFileOperation(OCFile fileToRemove, boolean deleteLocalCopy, FileDataStorageManager storageManager) { mFileToRemove = fileToRemove; mDeleteLocalCopy = deleteLocalCopy; mDataStorageManager = storageManager; @@@ -82,11 -81,7 +82,7 @@@ delete = new DeleteMethod(client.getBaseUri() + WebdavUtils.encodePath(mFileToRemove.getRemotePath())); int status = client.executeMethod(delete, REMOVE_READ_TIMEOUT, REMOVE_CONNECTION_TIMEOUT); if (delete.succeeded() || status == HttpStatus.SC_NOT_FOUND) { - if (mFileToRemove.isDirectory()) { - mDataStorageManager.removeDirectory(mFileToRemove, true, mDeleteLocalCopy); - } else { - mDataStorageManager.removeFile(mFileToRemove, mDeleteLocalCopy); - } + mDataStorageManager.removeFile(mFileToRemove, true, mDeleteLocalCopy); } delete.getResponseBodyAsString(); // exhaust the response, although not interesting result = new RemoteOperationResult((delete.succeeded() || status == HttpStatus.SC_NOT_FOUND), status, delete.getResponseHeaders()); diff --combined src/com/owncloud/android/operations/RenameFileOperation.java index 86d27122,f9bb4242..a2370f3b --- a/src/com/owncloud/android/operations/RenameFileOperation.java +++ b/src/com/owncloud/android/operations/RenameFileOperation.java @@@ -21,16 -21,15 +21,16 @@@ import java.io.File import java.io.IOException; import org.apache.jackrabbit.webdav.client.methods.DavMethodBase; -//import org.apache.jackrabbit.webdav.client.methods.MoveMethod; - -import android.accounts.Account; import com.owncloud.android.Log_OC; - import com.owncloud.android.datamodel.DataStorageManager; + import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.operations.RemoteOperationResult.ResultCode; import com.owncloud.android.utils.FileStorageUtils; +//import org.apache.jackrabbit.webdav.client.methods.MoveMethod; + +import android.accounts.Account; + import eu.alefzero.webdav.WebdavClient; import eu.alefzero.webdav.WebdavUtils; @@@ -52,7 -51,7 +52,7 @@@ public class RenameFileOperation extend private Account mAccount; private String mNewName; private String mNewRemotePath; - private DataStorageManager mStorageManager; + private FileDataStorageManager mStorageManager; /** @@@ -63,7 -62,7 +63,7 @@@ * @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, Account account, String newName, DataStorageManager storageManager) { + public RenameFileOperation(OCFile file, Account account, String newName, FileDataStorageManager storageManager) { mFile = file; mAccount = account; mNewName = newName; @@@ -95,7 -94,7 +95,7 @@@ String parent = (new File(mFile.getRemotePath())).getParent(); parent = (parent.endsWith(OCFile.PATH_SEPARATOR)) ? parent : parent + OCFile.PATH_SEPARATOR; mNewRemotePath = parent + mNewName; - if (mFile.isDirectory()) { + if (mFile.isFolder()) { mNewRemotePath += OCFile.PATH_SEPARATOR; } @@@ -114,7 -113,7 +114,7 @@@ int status = client.executeMethod(move, RENAME_READ_TIMEOUT, RENAME_CONNECTION_TIMEOUT); if (move.succeeded()) { - if (mFile.isDirectory()) { + if (mFile.isFolder()) { saveLocalDirectory(); } else { @@@ -153,7 -152,7 +153,7 @@@ private void saveLocalDirectory() { - mStorageManager.moveDirectory(mFile, mNewRemotePath); + mStorageManager.moveFolder(mFile, mNewRemotePath); String localPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, mFile); File localDir = new File(localPath); if (localDir.exists()) { diff --combined src/com/owncloud/android/operations/SynchronizeFileOperation.java index 211ba705,b5f6575a..2e15c2fe --- a/src/com/owncloud/android/operations/SynchronizeFileOperation.java +++ b/src/com/owncloud/android/operations/SynchronizeFileOperation.java @@@ -23,18 -23,17 +23,18 @@@ import org.apache.jackrabbit.webdav.Dav import org.apache.jackrabbit.webdav.MultiStatus; import org.apache.jackrabbit.webdav.client.methods.PropFindMethod; -import android.accounts.Account; -import android.content.Context; -import android.content.Intent; - import com.owncloud.android.Log_OC; - import com.owncloud.android.datamodel.DataStorageManager; + import com.owncloud.android.datamodel.FileDataStorageManager; 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 android.accounts.Account; +import android.content.Context; +import android.content.Intent; + + import eu.alefzero.webdav.WebdavClient; import eu.alefzero.webdav.WebdavEntry; import eu.alefzero.webdav.WebdavUtils; @@@ -47,10 -46,9 +47,9 @@@ public class SynchronizeFileOperation e private OCFile mLocalFile; private OCFile mServerFile; - private DataStorageManager mStorageManager; + private FileDataStorageManager mStorageManager; private Account mAccount; private boolean mSyncFileContents; - private boolean mLocalChangeAlreadyKnown; private Context mContext; private boolean mTransferWasRequested = false; @@@ -58,10 -56,9 +57,9 @@@ public SynchronizeFileOperation( OCFile localFile, OCFile serverFile, // make this null to let the operation checks the server; added to reuse info from SynchronizeFolderOperation - DataStorageManager storageManager, + FileDataStorageManager storageManager, Account account, boolean syncFileContents, - boolean localChangeAlreadyKnown, Context context) { mLocalFile = localFile; @@@ -69,7 -66,6 +67,6 @@@ mStorageManager = storageManager; mAccount = account; mSyncFileContents = syncFileContents; - mLocalChangeAlreadyKnown = localChangeAlreadyKnown; mContext = context; } @@@ -113,16 -109,18 +110,18 @@@ /// check changes in server and local file boolean serverChanged = false; + /* time for eTag is coming, but not yet if (mServerFile.getEtag() != null) { serverChanged = (!mServerFile.getEtag().equals(mLocalFile.getEtag())); // TODO could this be dangerous when the user upgrades the server from non-tagged to tagged? - } else { + } else { */ // server without etags serverChanged = (mServerFile.getModificationTimestamp() > mLocalFile.getModificationTimestampAtLastSyncForData()); - } - boolean localChanged = (mLocalChangeAlreadyKnown || mLocalFile.getLocalModificationTimestamp() > mLocalFile.getLastSyncDateForData()); + //} + boolean localChanged = (mLocalFile.getLocalModificationTimestamp() > mLocalFile.getLastSyncDateForData()); // TODO this will be always true after the app is upgraded to database version 2; will result in unnecessary uploads /// decide action to perform depending upon changes + //if (!mLocalFile.getEtag().isEmpty() && localChanged && serverChanged) { if (localChanged && serverChanged) { result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT); @@@ -219,6 -217,8 +218,8 @@@ file.setFileLength(we.contentLength()); file.setMimetype(we.contentType()); file.setModificationTimestamp(we.modifiedTimestamp()); + file.setEtag(we.etag()); + return file; } diff --combined src/com/owncloud/android/providers/FileContentProvider.java index 7f6a1a4f,917fd667..41b643c3 --- a/src/com/owncloud/android/providers/FileContentProvider.java +++ b/src/com/owncloud/android/providers/FileContentProvider.java @@@ -18,19 -18,23 +18,24 @@@ package com.owncloud.android.providers; + import java.util.ArrayList; import java.util.HashMap; import com.owncloud.android.Log_OC; import com.owncloud.android.R; + import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.db.ProviderMeta; import com.owncloud.android.db.ProviderMeta.ProviderTableMeta; + import android.content.ContentProvider; + import android.content.ContentProviderOperation; + import android.content.ContentProviderResult; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; + import android.content.OperationApplicationException; import android.content.UriMatcher; import android.database.Cursor; import android.database.SQLException; @@@ -44,6 -48,7 +49,7 @@@ import android.text.TextUtils * The ContentProvider for the ownCloud App. * * @author Bartek Przybylski + * @author David A. Velasco * */ public class FileContentProvider extends ContentProvider { @@@ -80,44 -85,110 +86,110 @@@ ProviderTableMeta.FILE_KEEP_IN_SYNC); mProjectionMap.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, ProviderTableMeta.FILE_ACCOUNT_OWNER); + mProjectionMap.put(ProviderTableMeta.FILE_ETAG, + ProviderTableMeta.FILE_ETAG); } private static final int SINGLE_FILE = 1; private static final int DIRECTORY = 2; private static final int ROOT_DIRECTORY = 3; - - private UriMatcher mUriMatcher; - // static { - // mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); - // mUriMatcher.addURI(ProviderMeta.AUTHORITY_FILES, null, ROOT_DIRECTORY); - // mUriMatcher.addURI(ProviderMeta.AUTHORITY_FILES, "file/", SINGLE_FILE); - // mUriMatcher.addURI(ProviderMeta.AUTHORITY_FILES, "file/#", SINGLE_FILE); - // mUriMatcher.addURI(ProviderMeta.AUTHORITY_FILES, "dir/#", DIRECTORY); - // } + private UriMatcher mUriMatcher; @Override public int delete(Uri uri, String where, String[] whereArgs) { + //Log_OC.d(TAG, "Deleting " + uri + " at provider " + this); + int count = 0; SQLiteDatabase db = mDbHelper.getWritableDatabase(); + db.beginTransaction(); + try { + count = delete(db, uri, where, whereArgs); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + getContext().getContentResolver().notifyChange(uri, null); + return count; + } + + + private int delete(SQLiteDatabase db, Uri uri, String where, String[] whereArgs) { int count = 0; switch (mUriMatcher.match(uri)) { case SINGLE_FILE: + /*Cursor c = query(db, uri, null, where, whereArgs, null); + String remotePath = "(unexisting)"; + if (c != null && c.moveToFirst()) { + remotePath = c.getString(c.getColumnIndex(ProviderTableMeta.FILE_PATH)); + } + Log_OC.d(TAG, "Removing FILE " + remotePath); + */ count = db.delete(ProviderTableMeta.DB_NAME, ProviderTableMeta._ID + "=" + uri.getPathSegments().get(1) + (!TextUtils.isEmpty(where) ? " AND (" + where + ")" : ""), whereArgs); + /* just for log + if (c!=null) { + c.close(); + } + */ + break; + case DIRECTORY: + // deletion of folder is recursive + /* + Uri folderUri = ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILE, Long.parseLong(uri.getPathSegments().get(1))); + Cursor folder = query(db, folderUri, null, null, null, null); + String folderName = "(unknown)"; + if (folder != null && folder.moveToFirst()) { + folderName = folder.getString(folder.getColumnIndex(ProviderTableMeta.FILE_PATH)); + } + */ + Cursor children = query(uri, null, null, null, null); + if (children != null && children.moveToFirst()) { + long childId; + boolean isDir; + //String remotePath; + while (!children.isAfterLast()) { + childId = children.getLong(children.getColumnIndex(ProviderTableMeta._ID)); + isDir = "DIR".equals(children.getString(children.getColumnIndex(ProviderTableMeta.FILE_CONTENT_TYPE))); + //remotePath = children.getString(children.getColumnIndex(ProviderTableMeta.FILE_PATH)); + if (isDir) { + count += delete(db, ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_DIR, childId), null, null); + } else { + count += delete(db, ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILE, childId), null, null); + } + children.moveToNext(); + } + children.close(); + } /*else { + Log_OC.d(TAG, "No child to remove in DIRECTORY " + folderName); + } + Log_OC.d(TAG, "Removing DIRECTORY " + folderName + " (or maybe not) "); + */ + count += db.delete(ProviderTableMeta.DB_NAME, + ProviderTableMeta._ID + + "=" + + uri.getPathSegments().get(1) + + (!TextUtils.isEmpty(where) ? " AND (" + where + + ")" : ""), whereArgs); + /* Just for log + if (folder != null) { + folder.close(); + }*/ break; case ROOT_DIRECTORY: + //Log_OC.d(TAG, "Removing ROOT!"); count = db.delete(ProviderTableMeta.DB_NAME, where, whereArgs); break; default: + //Log_OC.e(TAG, "Unknown uri " + uri); throw new IllegalArgumentException("Unknown uri: " + uri.toString()); } - getContext().getContentResolver().notifyChange(uri, null); return count; } + @Override public String getType(Uri uri) { @@@ -134,24 -205,52 +206,52 @@@ @Override public Uri insert(Uri uri, ContentValues values) { + //Log_OC.d(TAG, "Inserting " + values.getAsString(ProviderTableMeta.FILE_PATH) + " at provider " + this); + Uri newUri = null; + SQLiteDatabase db = mDbHelper.getWritableDatabase(); + db.beginTransaction(); + try { + newUri = insert(db, uri, values); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + getContext().getContentResolver().notifyChange(newUri, null); + return newUri; + } + + private Uri insert(SQLiteDatabase db, Uri uri, ContentValues values) { if (mUriMatcher.match(uri) != SINGLE_FILE && - mUriMatcher.match(uri) != ROOT_DIRECTORY) { - + mUriMatcher.match(uri) != ROOT_DIRECTORY) { + //Log_OC.e(TAG, "Inserting invalid URI: " + uri); throw new IllegalArgumentException("Unknown uri id: " + uri); } - SQLiteDatabase db = mDbHelper.getWritableDatabase(); - long rowId = db.insert(ProviderTableMeta.DB_NAME, null, values); - if (rowId > 0) { - Uri insertedFileUri = ContentUris.withAppendedId( - ProviderTableMeta.CONTENT_URI_FILE, rowId); - getContext().getContentResolver().notifyChange(insertedFileUri, - null); + String remotePath = values.getAsString(ProviderTableMeta.FILE_PATH); + String accountName = values.getAsString(ProviderTableMeta.FILE_ACCOUNT_OWNER); + String[] projection = new String[] {ProviderTableMeta._ID, ProviderTableMeta.FILE_PATH, ProviderTableMeta.FILE_ACCOUNT_OWNER }; + String where = ProviderTableMeta.FILE_PATH + "=? AND " + ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?"; + String[] whereArgs = new String[] {remotePath, accountName}; + Cursor doubleCheck = query(db, uri, projection, where, whereArgs, null); + if (doubleCheck == null || !doubleCheck.moveToFirst()) { // ugly patch; serious refactorization is needed to reduce work in FileDataStorageManager and bring it to FileContentProvider + long rowId = db.insert(ProviderTableMeta.DB_NAME, null, values); + if (rowId > 0) { + Uri insertedFileUri = ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILE, rowId); + //Log_OC.d(TAG, "Inserted " + values.getAsString(ProviderTableMeta.FILE_PATH) + " at provider " + this); + return insertedFileUri; + } else { + //Log_OC.d(TAG, "Error while inserting " + values.getAsString(ProviderTableMeta.FILE_PATH) + " at provider " + this); + throw new SQLException("ERROR " + uri); + } + } else { + // file is already inserted; race condition, let's avoid a duplicated entry + Uri insertedFileUri = ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILE, doubleCheck.getLong(doubleCheck.getColumnIndex(ProviderTableMeta._ID))); + doubleCheck.close(); return insertedFileUri; } - throw new SQLException("ERROR " + uri); } + @Override public boolean onCreate() { mDbHelper = new DataBaseHelper(getContext()); @@@ -161,14 -260,28 +261,28 @@@ mUriMatcher.addURI(authority, null, ROOT_DIRECTORY); mUriMatcher.addURI(authority, "file/", SINGLE_FILE); mUriMatcher.addURI(authority, "file/#", SINGLE_FILE); + mUriMatcher.addURI(authority, "dir/", DIRECTORY); mUriMatcher.addURI(authority, "dir/#", DIRECTORY); return true; } + @Override - public Cursor query(Uri uri, String[] projection, String selection, - String[] selectionArgs, String sortOrder) { + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { + Cursor result = null; + SQLiteDatabase db = mDbHelper.getReadableDatabase(); + db.beginTransaction(); + try { + result = query(db, uri, projection, selection, selectionArgs, sortOrder); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + return result; + } + + private Cursor query(SQLiteDatabase db, Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { SQLiteQueryBuilder sqlQuery = new SQLiteQueryBuilder(); sqlQuery.setTables(ProviderTableMeta.DB_NAME); @@@ -178,8 -291,9 +292,9 @@@ case ROOT_DIRECTORY: break; case DIRECTORY: + String folderId = uri.getPathSegments().get(1); sqlQuery.appendWhere(ProviderTableMeta.FILE_PARENT + "=" - + uri.getPathSegments().get(1)); + + folderId); break; case SINGLE_FILE: if (uri.getPathSegments().size() > 1) { @@@ -198,24 -312,115 +313,115 @@@ order = sortOrder; } - SQLiteDatabase db = mDbHelper.getReadableDatabase(); // DB case_sensitive db.execSQL("PRAGMA case_sensitive_like = true"); - Cursor c = sqlQuery.query(db, projection, selection, selectionArgs, - null, null, order); - + Cursor c = sqlQuery.query(db, projection, selection, selectionArgs, null, null, order); c.setNotificationUri(getContext().getContentResolver(), uri); - return c; } @Override - public int update(Uri uri, ContentValues values, String selection, - String[] selectionArgs) { - return mDbHelper.getWritableDatabase().update( - ProviderTableMeta.DB_NAME, values, selection, selectionArgs); + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + + //Log_OC.d(TAG, "Updating " + values.getAsString(ProviderTableMeta.FILE_PATH) + " at provider " + this); + int count = 0; + SQLiteDatabase db = mDbHelper.getWritableDatabase(); + db.beginTransaction(); + try { + count = update(db, uri, values, selection, selectionArgs); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + getContext().getContentResolver().notifyChange(uri, null); + return count; + } + + + private int update(SQLiteDatabase db, Uri uri, ContentValues values, String selection, String[] selectionArgs) { + switch (mUriMatcher.match(uri)) { + case DIRECTORY: + return updateFolderSize(db, selectionArgs[0]); + default: + return db.update(ProviderTableMeta.DB_NAME, values, selection, selectionArgs); + } + } + + + private int updateFolderSize(SQLiteDatabase db, String folderId) { + int count = 0; + String [] whereArgs = new String[] { folderId }; + + // read current size saved for the folder + long folderSize = 0; + long folderParentId = -1; + Uri selectFolderUri = Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_FILE, folderId); + String[] folderProjection = new String[] { ProviderTableMeta.FILE_CONTENT_LENGTH, ProviderTableMeta.FILE_PARENT}; + String folderWhere = ProviderTableMeta._ID + "=?"; + Cursor folderCursor = query(db, selectFolderUri, folderProjection, folderWhere, whereArgs, null); + if (folderCursor != null && folderCursor.moveToFirst()) { + folderSize = folderCursor.getLong(folderCursor.getColumnIndex(ProviderTableMeta.FILE_CONTENT_LENGTH));; + folderParentId = folderCursor.getLong(folderCursor.getColumnIndex(ProviderTableMeta.FILE_PARENT));; + } + folderCursor.close(); + + // read and sum sizes of children + long childrenSize = 0; + Uri selectChildrenUri = Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_DIR, folderId); + String[] childrenProjection = new String[] { ProviderTableMeta.FILE_CONTENT_LENGTH, ProviderTableMeta.FILE_PARENT}; + String childrenWhere = ProviderTableMeta.FILE_PARENT + "=?"; + Cursor childrenCursor = query(db, selectChildrenUri, childrenProjection, childrenWhere, whereArgs, null); + if (childrenCursor != null && childrenCursor.moveToFirst()) { + while (!childrenCursor.isAfterLast()) { + childrenSize += childrenCursor.getLong(childrenCursor.getColumnIndex(ProviderTableMeta.FILE_CONTENT_LENGTH)); + childrenCursor.moveToNext(); + } + } + childrenCursor.close(); + + // update if needed + if (folderSize != childrenSize) { + Log_OC.d("FileContentProvider", "Updating " + folderSize + " to " + childrenSize); + ContentValues cv = new ContentValues(); + cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, childrenSize); + count = db.update(ProviderTableMeta.DB_NAME, cv, folderWhere, whereArgs); + + // propagate update until root + if (folderParentId > FileDataStorageManager.ROOT_PARENT_ID) { + Log_OC.d("FileContentProvider", "Propagating update to " + folderParentId); + updateFolderSize(db, String.valueOf(folderParentId)); + } else { + Log_OC.d("FileContentProvider", "NOT propagating to " + folderParentId); + } + } else { + Log_OC.d("FileContentProvider", "NOT updating, sizes are " + folderSize + " and " + childrenSize); + } + return count; + } + + + @Override + public ContentProviderResult[] applyBatch (ArrayList operations) throws OperationApplicationException { + Log_OC.d("FileContentProvider", "applying batch in provider " + this + " (temporary: " + isTemporary() + ")" ); + ContentProviderResult[] results = new ContentProviderResult[operations.size()]; + int i=0; + + SQLiteDatabase db = mDbHelper.getWritableDatabase(); + db.beginTransaction(); // it's supposed that transactions can be nested + try { + for (ContentProviderOperation operation : operations) { + results[i] = operation.apply(this, results, i); + i++; + } + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + Log_OC.d("FileContentProvider", "applied batch in provider " + this); + return results; } + class DataBaseHelper extends SQLiteOpenHelper { public DataBaseHelper(Context context) { @@@ -241,7 -446,8 +447,8 @@@ + ProviderTableMeta.FILE_LAST_SYNC_DATE + " INTEGER, " + ProviderTableMeta.FILE_KEEP_IN_SYNC + " INTEGER, " + ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA + " INTEGER, " - + ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA + " INTEGER );" + + ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA + " INTEGER, " + + ProviderTableMeta.FILE_ETAG + " TEXT );" ); } @@@ -295,8 -501,24 +502,24 @@@ } if (!upgraded) Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion + ", newVersion == " + newVersion); + + if (oldVersion < 5 && newVersion >= 5) { + Log_OC.i("SQL", "Entering in the #4 ADD in onUpgrade"); + db.beginTransaction(); + try { + db .execSQL("ALTER TABLE " + ProviderTableMeta.DB_NAME + + " ADD COLUMN " + ProviderTableMeta.FILE_ETAG + " TEXT " + + " DEFAULT NULL"); + + upgraded = true; + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } + if (!upgraded) + Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion + ", newVersion == " + newVersion); } - } } diff --combined src/com/owncloud/android/syncadapter/AbstractOwnCloudSyncAdapter.java index 47730995,34ef4524..a3e7926a --- a/src/com/owncloud/android/syncadapter/AbstractOwnCloudSyncAdapter.java +++ b/src/com/owncloud/android/syncadapter/AbstractOwnCloudSyncAdapter.java @@@ -19,20 -19,16 +19,17 @@@ package com.owncloud.android.syncadapter; import java.io.IOException; - import java.util.Date; import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; import org.apache.http.client.ClientProtocolException; - import org.apache.http.conn.ConnectionKeepAliveStrategy; - import org.apache.http.protocol.HttpContext; import com.owncloud.android.authentication.AccountUtils; import com.owncloud.android.authentication.AccountUtils.AccountNotFoundException; - import com.owncloud.android.datamodel.DataStorageManager; + import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.network.OwnCloudClientUtils; + import android.accounts.Account; import android.accounts.AccountManager; import android.accounts.AuthenticatorException; @@@ -43,11 -39,13 +40,13 @@@ import android.content.Context import eu.alefzero.webdav.WebdavClient; /** - * Base SyncAdapter for OwnCloud Designed to be subclassed for the concrete - * SyncAdapter, like ConcatsSync, CalendarSync, FileSync etc.. + * Base synchronization adapter for ownCloud designed to be subclassed for different + * resource types, like FileSync, ConcatsSync, CalendarSync, etc.. * - * @author sassman + * Implements the standard {@link AbstractThreadedSyncAdapter}. * + * @author sassman + * @author David A. Velasco */ public abstract class AbstractOwnCloudSyncAdapter extends AbstractThreadedSyncAdapter { @@@ -55,8 -53,7 +54,7 @@@ private AccountManager accountManager; private Account account; private ContentProviderClient contentProvider; - private Date lastUpdated; - private DataStorageManager mStoreManager; + private FileDataStorageManager mStoreManager; private WebdavClient mClient = null; @@@ -89,60 -86,14 +87,14 @@@ this.contentProvider = contentProvider; } - public Date getLastUpdated() { - return lastUpdated; - } - - public void setLastUpdated(Date lastUpdated) { - this.lastUpdated = lastUpdated; - } - - public void setStorageManager(DataStorageManager storage_manager) { + public void setStorageManager(FileDataStorageManager storage_manager) { mStoreManager = storage_manager; } - public DataStorageManager getStorageManager() { + public FileDataStorageManager getStorageManager() { return mStoreManager; } - protected ConnectionKeepAliveStrategy getKeepAliveStrategy() { - return new ConnectionKeepAliveStrategy() { - public long getKeepAliveDuration(HttpResponse response, - HttpContext context) { - // Change keep alive straategy basing on response: ie - // forbidden/not found/etc - // should have keep alive 0 - // default return: 5s - int statusCode = response.getStatusLine().getStatusCode(); - - // HTTP 400, 500 Errors as well as HTTP 118 - Connection timed - // out - if ((statusCode >= 400 && statusCode <= 418) - || (statusCode >= 421 && statusCode <= 426) - || (statusCode >= 500 && statusCode <= 510) - || statusCode == 118) { - return 0; - } - - return 5 * 1000; - } - }; - } - - protected HttpResponse fireRawRequest(HttpRequest query) - throws ClientProtocolException, OperationCanceledException, - AuthenticatorException, IOException { - /* - * BasicHttpContext httpContext = new BasicHttpContext(); BasicScheme - * basicAuth = new BasicScheme(); - * httpContext.setAttribute("preemptive-auth", basicAuth); - * - * HttpResponse response = getClient().execute(mHost, query, - * httpContext); - */ - return null; - } - protected void initClientForCurrentAccount() throws OperationCanceledException, AuthenticatorException, IOException, AccountNotFoundException { AccountUtils.constructFullURLForAccount(getContext(), account); mClient = OwnCloudClientUtils.createOwnCloudClient(account, getContext()); @@@ -151,4 -102,13 +103,13 @@@ protected WebdavClient getClient() { return mClient; } + + + /* method called by ContactSyncAdapter, that is never used */ + protected HttpResponse fireRawRequest(HttpRequest query) + throws ClientProtocolException, OperationCanceledException, + AuthenticatorException, IOException { + return null; + } + } diff --combined src/com/owncloud/android/syncadapter/FileSyncAdapter.java index a0ca9881,9204aa0c..7a00b2ef --- a/src/com/owncloud/android/syncadapter/FileSyncAdapter.java +++ b/src/com/owncloud/android/syncadapter/FileSyncAdapter.java @@@ -30,7 -30,6 +30,6 @@@ import com.owncloud.android.Log_OC import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.authentication.AuthenticatorActivity; - import com.owncloud.android.datamodel.DataStorageManager; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.operations.RemoteOperationResult; @@@ -39,12 -38,12 +38,13 @@@ import com.owncloud.android.operations. import com.owncloud.android.operations.RemoteOperationResult.ResultCode; import com.owncloud.android.ui.activity.ErrorsWhileCopyingHandlerActivity; + import android.accounts.Account; import android.accounts.AccountsException; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; + import android.content.AbstractThreadedSyncAdapter; import android.content.ContentProviderClient; import android.content.ContentResolver; import android.content.Context; @@@ -53,36 -52,60 +53,60 @@@ import android.content.SyncResult import android.os.Bundle; /** - * SyncAdapter implementation for syncing sample SyncAdapter contacts to the - * platform ContactOperations provider. + * Implementation of {@link AbstractThreadedSyncAdapter} responsible for synchronizing + * ownCloud files. + * + * Performs a full synchronization of the account recieved in {@link #onPerformSync(Account, Bundle, String, ContentProviderClient, SyncResult)}. * * @author Bartek Przybylski * @author David A. Velasco */ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter { - private final static String TAG = "FileSyncAdapter"; + private final static String TAG = FileSyncAdapter.class.getSimpleName(); - /** - * Maximum number of failed folder synchronizations that are supported before finishing the synchronization operation - */ + /** Maximum number of failed folder synchronizations that are supported before finishing the synchronization operation */ private static final int MAX_FAILED_RESULTS = 3; + + /** Time stamp for the current synchronization process, used to distinguish fresh data */ private long mCurrentSyncTime; + + /** Flag made 'true' when a request to cancel the synchronization is received */ private boolean mCancellation; + + /** When 'true' the process was requested by the user through the user interface; when 'false', it was requested automatically by the system */ private boolean mIsManualSync; - private int mFailedResultsCounter; + + /** Counter for failed operations in the synchronization process */ + private int mFailedResultsCounter; + + /** Result of the last failed operation */ private RemoteOperationResult mLastFailedResult; - private SyncResult mSyncResult; + + /** Counter of conflicts found between local and remote files */ private int mConflictsFound; + + /** Counter of failed operations in synchronization of kept-in-sync files */ private int mFailsInFavouritesFound; + + /** Map of remote and local paths to files that where locally stored in a location out of the ownCloud folder and couldn't be copied automatically into it */ private Map mForgottenLocalFiles; + /** {@link SyncResult} instance to return to the system when the synchronization finish */ + private SyncResult mSyncResult; + + /** + * Creates an {@link FileSyncAdapter} + * + * {@inheritDoc} + */ public FileSyncAdapter(Context context, boolean autoInitialize) { super(context, autoInitialize); } + /** * {@inheritDoc} */ @@@ -100,20 -123,20 +124,20 @@@ mForgottenLocalFiles = new HashMap(); mSyncResult = syncResult; mSyncResult.fullSyncRequested = false; - mSyncResult.delayUntil = 60*60*24; // sync after 24h + mSyncResult.delayUntil = 60*60*24; // avoid too many automatic synchronizations this.setAccount(account); this.setContentProvider(provider); - this.setStorageManager(new FileDataStorageManager(account, getContentProvider())); + this.setStorageManager(new FileDataStorageManager(account, provider)); try { this.initClientForCurrentAccount(); } catch (IOException e) { - /// the account is unknown for the Synchronization Manager, or unreachable for this context; don't try this again + /// the account is unknown for the Synchronization Manager, unreachable this context, or can not be authenticated; don't try this again mSyncResult.tooManyRetries = true; notifyFailedSynchronization(); return; } catch (AccountsException e) { - /// the account is unknown for the Synchronization Manager, or unreachable for this context; don't try this again + /// the account is unknown for the Synchronization Manager, unreachable this context, or can not be authenticated; don't try this again mSyncResult.tooManyRetries = true; notifyFailedSynchronization(); return; @@@ -126,10 -149,10 +150,10 @@@ updateOCVersion(); mCurrentSyncTime = System.currentTimeMillis(); if (!mCancellation) { - fetchData(OCFile.PATH_SEPARATOR, DataStorageManager.ROOT_PARENT_ID); + synchronizeFolder(getStorageManager().getFileByPath(OCFile.ROOT_PATH)); } else { - Log_OC.d(TAG, "Leaving synchronization before any remote request due to cancellation was requested"); + Log_OC.d(TAG, "Leaving synchronization before synchronizing the root folder because cancelation request"); } @@@ -143,14 -166,12 +167,12 @@@ /// notify the user about the failure of MANUAL synchronization notifyFailedSynchronization(); - } if (mConflictsFound > 0 || mFailsInFavouritesFound > 0) { notifyFailsInFavourites(); } if (mForgottenLocalFiles.size() > 0) { notifyForgottenLocalFiles(); - } sendStickyBroadcast(false, null, mLastFailedResult); // message to signal the end to the UI } @@@ -160,8 -181,12 +182,12 @@@ /** * Called by system SyncManager when a synchronization is required to be cancelled. * - * Sets the mCancellation flag to 'true'. THe synchronization will be stopped when before a new folder is fetched. Data of the last folder - * fetched will be still saved in the database. See onPerformSync implementation. + * Sets the mCancellation flag to 'true'. THe synchronization will be stopped later, + * before a new folder is fetched. Data of the last folder synchronized will be still + * locally saved. + * + * See {@link #onPerformSync(Account, Bundle, String, ContentProviderClient, SyncResult)} + * and {@link #synchronizeFolder(String, long)}. */ @Override public void onSyncCanceled() { @@@ -184,20 -209,36 +210,36 @@@ /** - * Synchronize the properties of files and folders contained in a remote folder given by remotePath. + * Synchronizes the list of files contained in a folder identified with its remote path. + * + * Fetches the list and properties of the files contained in the given folder, including their + * properties, and updates the local database with them. + * + * Enters in the child folders to synchronize their contents also, following a recursive + * depth first strategy. * - * @param remotePath Remote path to the folder to synchronize. - * @param parentId Database Id of the folder to synchronize. + * @param folder Folder to synchronize. */ - private void fetchData(String remotePath, long parentId) { + private void synchronizeFolder(OCFile folder) { if (mFailedResultsCounter > MAX_FAILED_RESULTS || isFinisher(mLastFailedResult)) return; - // perform folder synchronization - SynchronizeFolderOperation synchFolderOp = new SynchronizeFolderOperation( remotePath, + /* + OCFile folder, + long currentSyncTime, + boolean updateFolderProperties, + boolean syncFullAccount, + DataStorageManager dataStorageManager, + Account account, + Context context ) { + + } + */ + // folder synchronization + SynchronizeFolderOperation synchFolderOp = new SynchronizeFolderOperation( folder, mCurrentSyncTime, - parentId, + true, getStorageManager(), getAccount(), getContext() @@@ -206,8 -247,9 +248,9 @@@ // synchronized folder -> notice to UI - ALWAYS, although !result.isSuccess - sendStickyBroadcast(true, remotePath, null); + sendStickyBroadcast(true, folder.getRemotePath(), null); + // check the result of synchronizing the folder if (result.isSuccess() || result.getCode() == ResultCode.SYNC_CONFLICT) { if (result.getCode() == ResultCode.SYNC_CONFLICT) { @@@ -217,15 -259,15 +260,15 @@@ if (synchFolderOp.getForgottenLocalFiles().size() > 0) { mForgottenLocalFiles.putAll(synchFolderOp.getForgottenLocalFiles()); } - // synchronize children folders - List children = synchFolderOp.getChildren(); - fetchChildren(children); // beware of the 'hidden' recursion here! - - sendStickyBroadcast(true, remotePath, null); + if (result.isSuccess()) { + // synchronize children folders + List children = synchFolderOp.getChildren(); + fetchChildren(folder, children, synchFolderOp.getRemoteFolderChanged()); // beware of the 'hidden' recursion here! + } } else { + // in failures, the statistics for the global result are updated if (result.getCode() == RemoteOperationResult.ResultCode.UNAUTHORIZED || - // (result.isTemporalRedirection() && result.isIdPRedirection() && ( result.isIdPRedirection() && MainApp.getAuthTokenTypeSamlSessionCookie().equals(getClient().getAuthTokenType()))) { mSyncResult.stats.numAuthExceptions++; @@@ -261,23 -303,31 +304,31 @@@ } /** - * Synchronize data of folders in the list of received files + * Triggers the synchronization of any folder contained in the list of received files. * - * @param files Files to recursively fetch + * @param files Files to recursively synchronize. */ - private void fetchChildren(List files) { + private void fetchChildren(OCFile parent, List files, boolean parentEtagChanged) { int i; + OCFile newFile = null; + String etag = null; + boolean syncDown = false; for (i=0; i < files.size() && !mCancellation; i++) { - OCFile newFile = files.get(i); - if (newFile.isDirectory()) { - fetchData(newFile.getRemotePath(), newFile.getFileId()); - - // Update folder size on DB - getStorageManager().calculateFolderSize(newFile.getFileId()); + newFile = files.get(i); + if (newFile.isFolder()) { + /* + etag = newFile.getEtag(); + syncDown = (parentEtagChanged || etag == null || etag.length() == 0); + if(syncDown) { */ + synchronizeFolder(newFile); + // update the size of the parent folder again after recursive synchronization + //getStorageManager().updateFolderSize(parent.getFileId()); + sendStickyBroadcast(true, parent.getRemotePath(), null); // notify again to refresh size in UI + //} } } - if (mCancellation && i mDirectories; /** Access point to the cached database for the current ownCloud {@link Account} */ - private DataStorageManager mStorageManager = null; + private FileDataStorageManager mStorageManager = null; private SyncBroadcastReceiver mSyncBroadcastReceiver; private UploadFinishReceiver mUploadFinishReceiver; @@@ -118,6 -117,7 +118,7 @@@ private View mRightFragmentContainer; private static final String KEY_WAITING_TO_PREVIEW = "WAITING_TO_PREVIEW"; + private static final String KEY_SYNC_IN_PROGRESS = "SYNC_IN_PROGRESS"; public static final int DIALOG_SHORT_WAIT = 0; private static final int DIALOG_CHOOSE_UPLOAD_SOURCE = 1; @@@ -139,6 -139,8 +140,8 @@@ private OCFile mWaitingToPreview; private Handler mHandler; + private boolean mSyncInProgress = false; + @Override protected void onCreate(Bundle savedInstanceState) { Log_OC.d(TAG, "onCreate() start"); @@@ -167,10 -169,12 +170,12 @@@ /// Load of saved instance state if(savedInstanceState != null) { mWaitingToPreview = (OCFile) savedInstanceState.getParcelable(FileDisplayActivity.KEY_WAITING_TO_PREVIEW); - + mSyncInProgress = savedInstanceState.getBoolean(KEY_SYNC_IN_PROGRESS); + } else { mWaitingToPreview = null; - } + mSyncInProgress = false; + } /// USER INTERFACE @@@ -186,7 -190,7 +191,7 @@@ // Action bar setup mDirectories = new CustomArrayAdapter(this, R.layout.sherlock_spinner_dropdown_item); getSupportActionBar().setHomeButtonEnabled(true); // mandatory since Android ICS, according to the official documentation - setSupportProgressBarIndeterminateVisibility(false); // always AFTER setContentView(...) ; to work around bug in its implementation + setSupportProgressBarIndeterminateVisibility(mSyncInProgress); // always AFTER setContentView(...) ; to work around bug in its implementation @@@ -229,27 -233,20 +234,20 @@@ } if (file == null) { // fall back to root folder - file = mStorageManager.getFileByPath(OCFile.PATH_SEPARATOR); // never returns null + file = mStorageManager.getFileByPath(OCFile.ROOT_PATH); // never returns null } setFile(file); - mDirectories.clear(); - OCFile fileIt = file; - while(fileIt != null && fileIt.getFileName() != OCFile.PATH_SEPARATOR) { - if (fileIt.isDirectory()) { - mDirectories.add(fileIt.getFileName()); - } - // get parent from path - parentPath = fileIt.getRemotePath().substring(0, fileIt.getRemotePath().lastIndexOf(fileIt.getFileName())); - fileIt = mStorageManager.getFileByPath(parentPath); - } - mDirectories.add(OCFile.PATH_SEPARATOR); + setNavigationListWithFolder(file); if (!stateWasRecovered) { Log_OC.e(TAG, "Initializing Fragments in onAccountChanged.."); initFragmentsWithFile(); + if (file.isFolder()) { + startSyncFolderOperation(file); + } } else { - updateFragmentsVisibility(!file.isDirectory()); - updateNavigationElementsInActionBar(file.isDirectory() ? null : file); + updateFragmentsVisibility(!file.isFolder()); + updateNavigationElementsInActionBar(file.isFolder() ? null : file); } @@@ -259,6 -256,23 +257,23 @@@ } + private void setNavigationListWithFolder(OCFile file) { + mDirectories.clear(); + OCFile fileIt = file; + String parentPath; + while(fileIt != null && fileIt.getFileName() != OCFile.ROOT_PATH) { + if (fileIt.isFolder()) { + mDirectories.add(fileIt.getFileName()); + } + //fileIt = mStorageManager.getFileById(fileIt.getParentId()); + // get parent from path + parentPath = fileIt.getRemotePath().substring(0, fileIt.getRemotePath().lastIndexOf(fileIt.getFileName())); + fileIt = mStorageManager.getFileByPath(parentPath); + } + mDirectories.add(OCFile.PATH_SEPARATOR); + } + + private void createMinFragments() { OCFileListFragment listOfFiles = new OCFileListFragment(); FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); @@@ -301,7 -315,7 +316,7 @@@ private Fragment chooseInitialSecondFragment(OCFile file) { Fragment secondFragment = null; - if (file != null && !file.isDirectory()) { + if (file != null && !file.isFolder()) { if (file.isDown() && PreviewMediaFragment.canBePreviewed(file) && file.getLastSyncDateForProperties() > 0 // temporal fix ) { @@@ -466,8 -480,9 +481,9 @@@ FileFragment second = getSecondFragment(); OCFile currentDir = getCurrentDir(); if((currentDir != null && currentDir.getParentId() != 0) || - (second != null && second.getFile() != null)) { + (second != null && second.getFile() != null)) { onBackPressed(); + } break; } @@@ -489,15 -504,22 +505,22 @@@ @Override public boolean onNavigationItemSelected(int itemPosition, long itemId) { - int i = itemPosition; - while (i-- != 0) { - onBackPressed(); - } - // the next operation triggers a new call to this method, but it's necessary to - // ensure that the name exposed in the action bar is the current directory when the - // user selected it in the navigation list - if (itemPosition != 0) + if (itemPosition != 0) { + String targetPath = ""; + for (int i=itemPosition; i < mDirectories.getCount() - 1; i++) { + targetPath = mDirectories.getItem(i) + OCFile.PATH_SEPARATOR + targetPath; + } + targetPath = OCFile.PATH_SEPARATOR + targetPath; + OCFile targetFolder = mStorageManager.getFileByPath(targetPath); + if (targetFolder != null) { + browseTo(targetFolder); + } + + // the next operation triggers a new call to this method, but it's necessary to + // ensure that the name exposed in the action bar is the current directory when the + // user selected it in the navigation list getSupportActionBar().setSelectedNavigationItem(0); + } return true; } @@@ -602,14 -624,17 +625,17 @@@ finish(); return; } - popDirname(); - listOfFiles.onBrowseUp(); + int levelsUp = listOfFiles.onBrowseUp(); + for (int i=0; i < levelsUp && mDirectories.getCount() > 1 ; i++) { + popDirname(); + } } } if (listOfFiles != null) { // should never be null, indeed setFile(listOfFiles.getCurrentFile()); } cleanSecondFragment(); + } @Override @@@ -618,6 -643,8 +644,8 @@@ Log_OC.e(TAG, "onSaveInstanceState() start"); super.onSaveInstanceState(outState); outState.putParcelable(FileDisplayActivity.KEY_WAITING_TO_PREVIEW, mWaitingToPreview); + outState.putBoolean(FileDisplayActivity.KEY_SYNC_IN_PROGRESS, mSyncInProgress); + Log_OC.d(TAG, "onSaveInstanceState() end"); } @@@ -805,10 -832,10 +833,10 @@@ /** * Pushes a directory to the drop down list * @param directory to push - * @throws IllegalArgumentException If the {@link OCFile#isDirectory()} returns false. + * @throws IllegalArgumentException If the {@link OCFile#isFolder()} returns false. */ public void pushDirname(OCFile directory) { - if(!directory.isDirectory()){ + if(!directory.isFolder()){ throw new IllegalArgumentException("Only directories may be pushed!"); } mDirectories.insert(directory.getFileName(), 0); @@@ -860,8 -887,7 +888,7 @@@ public void onReceive(Context context, Intent intent) { boolean inProgress = intent.getBooleanExtra(FileSyncService.IN_PROGRESS, false); String accountName = intent.getStringExtra(FileSyncService.ACCOUNT_NAME); - - Log_OC.d(TAG, "sync of account " + accountName + " is in_progress: " + inProgress); + RemoteOperationResult synchResult = (RemoteOperationResult)intent.getSerializableExtra(FileSyncService.SYNC_RESULT); if (getAccount() != null && accountName.equals(getAccount().name) && mStorageManager != null @@@ -869,31 -895,39 +896,39 @@@ String synchFolderRemotePath = intent.getStringExtra(FileSyncService.SYNC_FOLDER_REMOTE_PATH); - boolean fillBlankRoot = false; - OCFile currentDir = getCurrentDir(); - if (currentDir == null) { - currentDir = mStorageManager.getFileByPath(OCFile.PATH_SEPARATOR); - fillBlankRoot = (currentDir != null); - } + OCFile currentFile = (getFile() == null) ? null : mStorageManager.getFileByPath(getFile().getRemotePath()); + OCFile currentDir = (getCurrentDir() == null) ? null : mStorageManager.getFileByPath(getCurrentDir().getRemotePath()); - if ((synchFolderRemotePath != null && currentDir != null && (currentDir.getRemotePath().equals(synchFolderRemotePath))) - || fillBlankRoot ) { - if (!fillBlankRoot) - currentDir = mStorageManager.getFileByPath(synchFolderRemotePath); - OCFileListFragment fileListFragment = getListOfFilesFragment(); - if (fileListFragment != null) { - fileListFragment.listDirectory(currentDir); + if (currentDir == null) { + // current folder was removed from the server + Toast.makeText( FileDisplayActivity.this, + String.format(getString(R.string.sync_current_folder_was_removed), mDirectories.getItem(0)), + Toast.LENGTH_LONG) + .show(); + browseToRoot(); + + } else { + if (currentFile == null && !getFile().isFolder()) { + // currently selected file was removed in the server, and now we know it + cleanSecondFragment(); + currentFile = currentDir; } - if (getSecondFragment() == null) - setFile(currentDir); + + if (synchFolderRemotePath != null && currentDir.getRemotePath().equals(synchFolderRemotePath)) { + OCFileListFragment fileListFragment = getListOfFilesFragment(); + if (fileListFragment != null) { + fileListFragment.listDirectory(currentDir); + } + } + setFile(currentFile); } setSupportProgressBarIndeterminateVisibility(inProgress); removeStickyBroadcast(intent); + mSyncInProgress = inProgress; } - - RemoteOperationResult synchResult = (RemoteOperationResult)intent.getSerializableExtra(FileSyncService.SYNC_RESULT); + if (synchResult != null) { if (synchResult.getCode().equals(RemoteOperationResult.ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED)) { mLastSslUntrustedServerResult = synchResult; @@@ -902,7 -936,7 +937,7 @@@ } } } - + private class UploadFinishReceiver extends BroadcastReceiver { /** @@@ -962,11 -996,43 +997,43 @@@ * {@inheritDoc} */ @Override - public DataStorageManager getStorageManager() { + public FileDataStorageManager getStorageManager() { return mStorageManager; } + public void browseToRoot() { + OCFileListFragment listOfFiles = getListOfFilesFragment(); + if (listOfFiles != null) { // should never be null, indeed + while (mDirectories.getCount() > 1) { + popDirname(); + } + OCFile root = mStorageManager.getFileByPath(OCFile.ROOT_PATH); + listOfFiles.listDirectory(root); + setFile(listOfFiles.getCurrentFile()); + startSyncFolderOperation(root); + } + cleanSecondFragment(); + } + + + public void browseTo(OCFile folder) { + if (folder == null || !folder.isFolder()) { + throw new IllegalArgumentException("Trying to browse to invalid folder " + folder); + } + OCFileListFragment listOfFiles = getListOfFilesFragment(); + if (listOfFiles != null) { + setNavigationListWithFolder(folder); + listOfFiles.listDirectory(folder); + setFile(listOfFiles.getCurrentFile()); + startSyncFolderOperation(folder); + } else { + Log_OC.e(TAG, "Unexpected null when accessing list fragment"); + } + cleanSecondFragment(); + } + + /** * {@inheritDoc} * @@@ -976,6 -1042,10 +1043,10 @@@ public void onBrowsedDownTo(OCFile directory) { pushDirname(directory); cleanSecondFragment(); + + // Sync Folder + startSyncFolderOperation(directory); + } /** @@@ -1064,6 -1134,19 +1135,19 @@@ } + // private void updateDisplayHomeAtSync(){ + // ActionBar actionBar = getSupportActionBar(); + // OCFile currentDir = getCurrentDir(); + // if (currentDir.getParentId() != DataStorageManager.ROOT_PARENT_ID) { + // actionBar.setHomeButtonEnabled(!mSyncInProgress); + // actionBar.setDisplayHomeAsUpEnabled(!mSyncInProgress); + // } + // else { + // actionBar.setHomeButtonEnabled(true); + // actionBar.setDisplayHomeAsUpEnabled(false); + // } + // } + // /** * {@inheritDoc} */ @@@ -1184,7 -1267,8 +1268,8 @@@ } else if (operation instanceof CreateFolderOperation) { onCreateFolderOperationFinish((CreateFolderOperation)operation, result); - } + + } } @@@ -1232,7 -1316,6 +1317,6 @@@ refeshListOfFilesFragment(); } else { - //dismissDialog(DIALOG_SHORT_WAIT); dismissLoadingDialog(); try { Toast msg = Toast.makeText(FileDisplayActivity.this, R.string.create_dir_fail_msg, Toast.LENGTH_LONG); @@@ -1293,10 -1376,7 +1377,7 @@@ i.putExtra(ConflictsResolveActivity.EXTRA_ACCOUNT, getAccount()); startActivity(i); - } else { - Toast msg = Toast.makeText(this, R.string.sync_file_fail_msg, Toast.LENGTH_LONG); - msg.show(); - } + } } else { if (operation.transferWasRequested()) { @@@ -1365,7 -1445,7 +1446,7 @@@ private OCFile getCurrentDir() { OCFile file = getFile(); if (file != null) { - if (file.isDirectory()) { + if (file.isFolder()) { return file; } else if (mStorageManager != null) { String parentPath = file.getRemotePath().substring(0, file.getRemotePath().lastIndexOf(file.getFileName())); @@@ -1374,5 -1454,35 +1455,35 @@@ } return null; } + + public void startSyncFolderOperation(OCFile folder) { + long currentSyncTime = System.currentTimeMillis(); + + mSyncInProgress = true; + + // perform folder synchronization + RemoteOperation synchFolderOp = new SynchronizeFolderOperation( folder, + currentSyncTime, + false, + getStorageManager(), + getAccount(), + getApplicationContext() + ); + synchFolderOp.execute(getAccount(), this, null, null, this); + + setSupportProgressBarIndeterminateVisibility(true); + } + + // public void enableDisableViewGroup(ViewGroup viewGroup, boolean enabled) { + // int childCount = viewGroup.getChildCount(); + // for (int i = 0; i < childCount; i++) { + // View view = viewGroup.getChildAt(i); + // view.setEnabled(enabled); + // view.setClickable(!enabled); + // if (view instanceof ViewGroup) { + // enableDisableViewGroup((ViewGroup) view, enabled); + // } + // } + // } } diff --combined src/com/owncloud/android/ui/activity/Preferences.java index 7196b138,0d96becb..e94942e2 --- a/src/com/owncloud/android/ui/activity/Preferences.java +++ b/src/com/owncloud/android/ui/activity/Preferences.java @@@ -26,7 -26,6 +26,6 @@@ import android.content.pm.PackageManage import android.net.Uri; import android.os.Bundle; import android.preference.CheckBoxPreference; - import android.preference.ListPreference; import android.preference.Preference; import android.preference.Preference.OnPreferenceChangeListener; import android.preference.Preference.OnPreferenceClickListener; @@@ -42,22 -41,19 +41,20 @@@ import com.owncloud.android.OwnCloudSes import com.owncloud.android.R; import com.owncloud.android.db.DbHandler; + /** * An Activity that allows the user to change the application's settings. * * @author Bartek Przybylski - * + * @author David A. Velasco */ - public class Preferences extends SherlockPreferenceActivity implements OnPreferenceChangeListener { + public class Preferences extends SherlockPreferenceActivity { private static final String TAG = "OwnCloudPreferences"; private final int mNewSession = 47; private final int mEditSession = 48; private DbHandler mDbHandler; private Vector mSessions; - private ListPreference mTrackingUpdateInterval; - private CheckBoxPreference mDeviceTracking; private CheckBoxPreference pCode; //private CheckBoxPreference pLogging; //private Preference pLoggingHistory; @@@ -285,21 -281,6 +282,6 @@@ Intent intent; switch (item.getItemId()) { - //case R.id.addSessionItem: - case 1: - intent = new Intent(this, PreferencesNewSession.class); - startActivityForResult(intent, mNewSession); - break; - case R.id.SessionContextEdit: - intent = new Intent(this, PreferencesNewSession.class); - intent.putExtra("sessionId", mSessions.get(mSelectedMenuItem) - .getEntryId()); - intent.putExtra("sessionName", mSessions.get(mSelectedMenuItem) - .getName()); - intent.putExtra("sessionURL", mSessions.get(mSelectedMenuItem) - .getUrl()); - startActivityForResult(intent, mEditSession); - break; case android.R.id.home: intent = new Intent(getBaseContext(), FileDisplayActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); @@@ -323,35 -304,4 +305,4 @@@ super.onDestroy(); } - @Override - /** - * Updates various summaries after updates. Also starts and stops - * the - */ - public boolean onPreferenceChange(Preference preference, Object newValue) { - // Update current account summary - /*if (preference.equals(mAccountList)) { - mAccountList.setSummary(newValue.toString()); - } - - // Update tracking interval summary - else*/ if (preference.equals(mTrackingUpdateInterval)) { - String trackingSummary = getResources().getString( - R.string.prefs_trackmydevice_interval_summary); - trackingSummary = String.format(trackingSummary, - newValue.toString()); - mTrackingUpdateInterval.setSummary(trackingSummary); - } - - // Start or stop tracking service - else if (preference.equals(mDeviceTracking)) { - Intent locationServiceIntent = new Intent(); - locationServiceIntent - .setAction("com.owncloud.android.location.LocationLauncher"); - locationServiceIntent.putExtra("TRACKING_SETTING", - (Boolean) newValue); - sendBroadcast(locationServiceIntent); - } - return true; - } } diff --combined src/com/owncloud/android/ui/adapter/FileListListAdapter.java index a2651fac,5e6d01b2..a864f7a5 --- a/src/com/owncloud/android/ui/adapter/FileListListAdapter.java +++ b/src/com/owncloud/android/ui/adapter/FileListListAdapter.java @@@ -28,18 -28,17 +28,18 @@@ import android.widget.ListAdapter import android.widget.ListView; import android.widget.TextView; + +import java.util.Vector; + import com.owncloud.android.DisplayUtils; import com.owncloud.android.R; import com.owncloud.android.authentication.AccountUtils; - import com.owncloud.android.datamodel.DataStorageManager; + import com.owncloud.android.datamodel.FileDataStorageManager; 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.ui.activity.TransferServiceGetter; -import java.util.Vector; - /** * This Adapter populates a ListView with all files and folders in an ownCloud @@@ -52,9 -51,12 +52,9 @@@ public class FileListListAdapter extend private Context mContext; private OCFile mFile = null; private Vector mFiles = null; - private DataStorageManager mStorageManager; + private FileDataStorageManager mStorageManager; private Account mAccount; private TransferServiceGetter mTransferServiceGetter; - //total size of a directory (recursive) - private Long totalSizeOfDirectoriesRecursive = null; - private Long lastModifiedOfAllSubdirectories = null; public FileListListAdapter(Context context, TransferServiceGetter transferServiceGetter) { mContext = context; @@@ -133,7 -135,7 +133,7 @@@ TextView lastModV = (TextView) view.findViewById(R.id.last_mod); ImageView checkBoxV = (ImageView) view.findViewById(R.id.custom_checkbox); - if (!file.isDirectory()) { + if (!file.isFolder()) { fileSizeV.setVisibility(View.VISIBLE); fileSizeV.setText(DisplayUtils.bytesToHumanReadable(file.getFileLength())); lastModV.setVisibility(View.VISIBLE); @@@ -193,14 -195,14 +193,14 @@@ * @param directory New file to adapt. Can be NULL, meaning "no content to adapt". * @param updatedStorageManager Optional updated storage manager; used to replace mStorageManager if is different (and not NULL) */ - public void swapDirectory(OCFile directory, DataStorageManager updatedStorageManager) { + public void swapDirectory(OCFile directory, FileDataStorageManager updatedStorageManager) { mFile = directory; if (updatedStorageManager != null && updatedStorageManager != mStorageManager) { mStorageManager = updatedStorageManager; mAccount = AccountUtils.getCurrentOwnCloudAccount(mContext); } if (mStorageManager != null) { - mFiles = mStorageManager.getDirectoryContent(mFile); + mFiles = mStorageManager.getFolderContent(mFile); } else { mFiles = null; } diff --combined src/com/owncloud/android/ui/fragment/FileDetailFragment.java index 317b98c5,75bd4a99..af737662 --- a/src/com/owncloud/android/ui/fragment/FileDetailFragment.java +++ b/src/com/owncloud/android/ui/fragment/FileDetailFragment.java @@@ -48,17 -48,17 +48,17 @@@ import com.owncloud.android.Log_OC import com.owncloud.android.R; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder; import com.owncloud.android.files.services.FileObserverService; import com.owncloud.android.files.services.FileUploader; +import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder; import com.owncloud.android.files.services.FileUploader.FileUploaderBinder; import com.owncloud.android.operations.OnRemoteOperationListener; import com.owncloud.android.operations.RemoteOperation; import com.owncloud.android.operations.RemoteOperationResult; -import com.owncloud.android.operations.RemoteOperationResult.ResultCode; import com.owncloud.android.operations.RemoveFileOperation; import com.owncloud.android.operations.RenameFileOperation; import com.owncloud.android.operations.SynchronizeFileOperation; +import com.owncloud.android.operations.RemoteOperationResult.ResultCode; import com.owncloud.android.ui.activity.ConflictsResolveActivity; import com.owncloud.android.ui.activity.FileActivity; import com.owncloud.android.ui.activity.FileDisplayActivity; @@@ -66,7 -66,6 +66,7 @@@ import com.owncloud.android.ui.dialog.E import com.owncloud.android.ui.dialog.EditNameDialog.EditNameDialogListener; import com.owncloud.android.ui.preview.PreviewImageFragment; + import eu.alefzero.webdav.OnDatatransferProgressListener; /** @@@ -417,7 -416,7 +417,7 @@@ public class FileDetailFragment extend private void renameFile() { OCFile file = getFile(); String fileName = file.getFileName(); - int extensionStart = file.isDirectory() ? -1 : fileName.lastIndexOf("."); + int extensionStart = file.isFolder() ? -1 : fileName.lastIndexOf("."); int selectionEnd = (extensionStart >= 0) ? extensionStart : fileName.length(); EditNameDialog dialog = EditNameDialog.newInstance(getString(R.string.rename_dialog_title), fileName, 0, selectionEnd, this); dialog.show(getFragmentManager(), "nameeditdialog"); @@@ -448,7 -447,7 +448,7 @@@ } } else { - mLastRemoteOperation = new SynchronizeFileOperation(file, null, mStorageManager, mAccount, true, false, getActivity()); + mLastRemoteOperation = new SynchronizeFileOperation(file, null, mStorageManager, mAccount, true, getActivity()); mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity()); // update ui @@@ -837,10 -836,7 +837,7 @@@ i.putExtra(ConflictsResolveActivity.EXTRA_ACCOUNT, mAccount); startActivity(i); - } else { - Toast msg = Toast.makeText(getActivity(), R.string.sync_file_fail_msg, Toast.LENGTH_LONG); - msg.show(); - } + } if (file.isDown()) { setButtonsForDown(); diff --combined src/com/owncloud/android/ui/fragment/OCFileListFragment.java index 2c3f939f,2627f93a..a3c6d3be --- a/src/com/owncloud/android/ui/fragment/OCFileListFragment.java +++ b/src/com/owncloud/android/ui/fragment/OCFileListFragment.java @@@ -24,7 -24,7 +24,7 @@@ import java.util.List import com.owncloud.android.Log_OC; import com.owncloud.android.R; import com.owncloud.android.authentication.AccountUtils; - import com.owncloud.android.datamodel.DataStorageManager; + import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.files.FileHandler; import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder; @@@ -43,7 -43,6 +43,7 @@@ import com.owncloud.android.ui.fragment import com.owncloud.android.ui.preview.PreviewImageFragment; import com.owncloud.android.ui.preview.PreviewMediaFragment; + import android.accounts.Account; import android.app.Activity; import android.os.Bundle; @@@ -122,24 -121,52 +122,52 @@@ public class OCFileListFragment extend /** - * Call this, when the user presses the up button + * Call this, when the user presses the up button. + * + * Tries to move up the current folder one level. If the parent folder was removed from the database, + * it continues browsing up until finding an existing folders. + * + * return Count of folder levels browsed up. */ - public void onBrowseUp() { + public int onBrowseUp() { OCFile parentDir = null; + int moveCount = 0; if(mFile != null){ - DataStorageManager storageManager = mContainerActivity.getStorageManager(); - parentDir = storageManager.getFileById(mFile.getParentId()); - mFile = parentDir; + FileDataStorageManager storageManager = mContainerActivity.getStorageManager(); + + String parentPath = null; + if (mFile.getParentId() != FileDataStorageManager.ROOT_PARENT_ID) { + parentPath = new File(mFile.getRemotePath()).getParent(); + parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : parentPath + OCFile.PATH_SEPARATOR; + parentDir = storageManager.getFileByPath(parentPath); + moveCount++; + } else { + parentDir = storageManager.getFileByPath(OCFile.ROOT_PATH); // never returns null; keep the path in root folder + } + while (parentDir == null) { + parentPath = new File(parentPath).getParent(); + parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : parentPath + OCFile.PATH_SEPARATOR; + parentDir = storageManager.getFileByPath(parentPath); + moveCount++; + } // exit is granted because storageManager.getFileByPath("/") never returns null + mFile = parentDir; } - listDirectory(parentDir); + + if (mFile != null) { + listDirectory(mFile); + + mContainerActivity.startSyncFolderOperation(mFile); + } // else - should never happen now + + return moveCount; } @Override public void onItemClick(AdapterView l, View v, int position, long id) { OCFile file = (OCFile) mAdapter.getItem(position); if (file != null) { - if (file.isDirectory()) { + if (file.isFolder()) { // update state and view of this fragment listDirectory(file); // then, notify parent activity to let it update its state and view, and other fragments @@@ -186,7 -213,7 +214,7 @@@ List toDisable = new ArrayList(); MenuItem item = null; - if (targetFile.isDirectory()) { + if (targetFile.isFolder()) { // contextual menu for folders toHide.add(R.id.action_open_file_with); toHide.add(R.id.action_download_file); @@@ -258,7 -285,7 +286,7 @@@ switch (item.getItemId()) { case R.id.action_rename_file: { String fileName = mTargetFile.getFileName(); - int extensionStart = mTargetFile.isDirectory() ? -1 : fileName.lastIndexOf("."); + int extensionStart = mTargetFile.isFolder() ? -1 : fileName.lastIndexOf("."); int selectionEnd = (extensionStart >= 0) ? extensionStart : fileName.length(); EditNameDialog dialog = EditNameDialog.newInstance(getString(R.string.rename_dialog_title), fileName, 0, selectionEnd, this); dialog.show(getFragmentManager(), EditNameDialog.TAG); @@@ -268,7 -295,7 +296,7 @@@ int messageStringId = R.string.confirmation_remove_alert; int posBtnStringId = R.string.confirmation_remove_remote; int neuBtnStringId = -1; - if (mTargetFile.isDirectory()) { + if (mTargetFile.isFolder()) { messageStringId = R.string.confirmation_remove_folder_alert; posBtnStringId = R.string.confirmation_remove_remote_and_local; neuBtnStringId = R.string.confirmation_remove_folder_local; @@@ -288,7 -315,7 +316,7 @@@ } case R.id.action_sync_file: { Account account = AccountUtils.getCurrentOwnCloudAccount(getSherlockActivity()); - RemoteOperation operation = new SynchronizeFileOperation(mTargetFile, null, mContainerActivity.getStorageManager(), account, true, false, getSherlockActivity()); + RemoteOperation operation = new SynchronizeFileOperation(mTargetFile, null, mContainerActivity.getStorageManager(), account, true, getSherlockActivity()); operation.execute(account, getSherlockActivity(), mContainerActivity, mHandler, getSherlockActivity()); ((FileDisplayActivity) getSherlockActivity()).showLoadingDialog(); return true; @@@ -347,7 -374,7 +375,7 @@@ * @param directory File to be listed */ public void listDirectory(OCFile directory) { - DataStorageManager storageManager = mContainerActivity.getStorageManager(); + FileDataStorageManager storageManager = mContainerActivity.getStorageManager(); if (storageManager != null) { // Check input parameters for null @@@ -362,7 -389,7 +390,7 @@@ // If that's not a directory -> List its parent - if(!directory.isDirectory()){ + if(!directory.isFolder()){ Log_OC.w(TAG, "You see, that is not a directory -> " + directory.toString()); directory = storageManager.getFileById(directory.getParentId()); } @@@ -372,7 -399,6 +400,6 @@@ mList.setSelectionFromTop(0, 0); } mFile = directory; - } } @@@ -397,11 -423,13 +424,13 @@@ public void startMediaPreview(OCFile file, int i, boolean b); public void startImagePreview(OCFile file); + + public void startSyncFolderOperation(OCFile folder); /** * Getter for the current DataStorageManager in the container activity */ - public DataStorageManager getStorageManager(); + public FileDataStorageManager getStorageManager(); /** @@@ -455,16 -483,7 +484,7 @@@ @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); - } + mContainerActivity.getStorageManager().removeFile(mTargetFile, false, true); // TODO perform in background task / new thread listDirectory(); mContainerActivity.onTransferStateChanged(mTargetFile, false, false); } diff --combined src/com/owncloud/android/ui/preview/PreviewImageActivity.java index 87ddd562,9c48cc18..ef48d123 --- a/src/com/owncloud/android/ui/preview/PreviewImageActivity.java +++ b/src/com/owncloud/android/ui/preview/PreviewImageActivity.java @@@ -35,21 -35,20 +35,20 @@@ import android.view.View.OnTouchListene import com.actionbarsherlock.app.ActionBar; import com.actionbarsherlock.view.MenuItem; import com.actionbarsherlock.view.Window; +import com.owncloud.android.Log_OC; +import com.owncloud.android.R; import com.owncloud.android.authentication.AccountUtils; - import com.owncloud.android.datamodel.DataStorageManager; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.files.services.FileDownloader; -import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder; import com.owncloud.android.files.services.FileUploader; +import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder; import com.owncloud.android.files.services.FileUploader.FileUploaderBinder; import com.owncloud.android.ui.activity.FileActivity; import com.owncloud.android.ui.activity.FileDisplayActivity; import com.owncloud.android.ui.dialog.LoadingDialog; import com.owncloud.android.ui.fragment.FileFragment; -import com.owncloud.android.Log_OC; -import com.owncloud.android.R; /** * Holds a swiping galley where image files contained in an ownCloud directory are shown @@@ -67,7 -66,7 +66,7 @@@ public class PreviewImageActivity exten private static final String DIALOG_WAIT_TAG = "DIALOG_WAIT"; - private DataStorageManager mStorageManager; + private FileDataStorageManager mStorageManager; private ViewPager mViewPager; private PreviewImagePagerAdapter mPreviewImagePagerAdapter; @@@ -110,7 -109,7 +109,7 @@@ //OCFile parentFolder = mStorageManager.getFileById(getFile().getParentId()); if (parentFolder == null) { // should not be necessary - parentFolder = mStorageManager.getFileByPath(OCFile.PATH_SEPARATOR); + parentFolder = mStorageManager.getFileByPath(OCFile.ROOT_PATH); } mPreviewImagePagerAdapter = new PreviewImagePagerAdapter(getSupportFragmentManager(), parentFolder, getAccount(), mStorageManager); mViewPager = (ViewPager) findViewById(R.id.fragmentPager); @@@ -436,7 -435,7 +435,7 @@@ mStorageManager = new FileDataStorageManager(getAccount(), getContentResolver()); // Update file according to DB file, if it is possible - if (file.getFileId() > DataStorageManager.ROOT_PARENT_ID) + if (file.getFileId() > FileDataStorageManager.ROOT_PARENT_ID) file = mStorageManager.getFileById(file.getFileId()); if (file != null) { diff --combined src/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.java index d17d35f7,6ef06189..de90b4bd --- a/src/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.java +++ b/src/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.java @@@ -22,16 -22,15 +22,16 @@@ import java.util.Map import java.util.Set; import java.util.Vector; - import com.owncloud.android.datamodel.DataStorageManager; +import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.ui.fragment.FileFragment; + import android.accounts.Account; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentStatePagerAdapter; import android.view.ViewGroup; + import com.owncloud.android.datamodel.FileDataStorageManager; -import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.ui.fragment.FileFragment; /** * Adapter class that provides Fragment instances @@@ -46,7 -45,7 +46,7 @@@ public class PreviewImagePagerAdapter e private Set mObsoleteFragments; private Set mObsoletePositions; private Set mDownloadErrors; - private DataStorageManager mStorageManager; + private FileDataStorageManager mStorageManager; private Map mCachedFragments; @@@ -57,7 -56,7 +57,7 @@@ * @param parentFolder Folder where images will be searched for. * @param storageManager Bridge to database. */ - public PreviewImagePagerAdapter(FragmentManager fragmentManager, OCFile parentFolder, Account account, DataStorageManager storageManager) { + public PreviewImagePagerAdapter(FragmentManager fragmentManager, OCFile parentFolder, Account account, FileDataStorageManager storageManager) { super(fragmentManager); if (fragmentManager == null) { @@@ -72,7 -71,7 +72,7 @@@ mAccount = account; mStorageManager = storageManager; - mImageFiles = mStorageManager.getDirectoryImages(parentFolder); + mImageFiles = mStorageManager.getFolderImages(parentFolder); mObsoleteFragments = new HashSet(); mObsoletePositions = new HashSet(); mDownloadErrors = new HashSet(); diff --combined src/com/owncloud/android/ui/preview/PreviewVideoActivity.java index 56972c65,47441ffb..7db07b35 --- a/src/com/owncloud/android/ui/preview/PreviewVideoActivity.java +++ b/src/com/owncloud/android/ui/preview/PreviewVideoActivity.java @@@ -17,16 -17,6 +17,15 @@@ package com.owncloud.android.ui.preview; +import com.owncloud.android.Log_OC; +import com.owncloud.android.R; +import com.owncloud.android.authentication.AccountUtils; +import com.owncloud.android.authentication.AccountUtils.AccountNotFoundException; - import com.owncloud.android.datamodel.DataStorageManager; +import com.owncloud.android.datamodel.FileDataStorageManager; +import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.media.MediaService; +import com.owncloud.android.ui.activity.FileActivity; + import android.accounts.Account; import android.app.AlertDialog; import android.content.DialogInterface; @@@ -40,7 -30,15 +39,6 @@@ import android.os.Bundle import android.widget.MediaController; import android.widget.VideoView; -import com.owncloud.android.Log_OC; -import com.owncloud.android.R; -import com.owncloud.android.datamodel.FileDataStorageManager; -import com.owncloud.android.authentication.AccountUtils; -import com.owncloud.android.authentication.AccountUtils.AccountNotFoundException; -import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.media.MediaService; -import com.owncloud.android.ui.activity.FileActivity; -- /** * Activity implementing a basic video player. * @@@ -61,7 -59,7 +59,7 @@@ public class PreviewVideoActivity exten private static final String TAG = PreviewVideoActivity.class.getSimpleName(); - private DataStorageManager mStorageManager; + private FileDataStorageManager mStorageManager; private int mSavedPlaybackPosition; // in the unit time handled by MediaPlayer.getCurrentPosition() private boolean mAutoplay; // when 'true', the playback starts immediately with the activity diff --combined src/eu/alefzero/webdav/WebdavClient.java index 6cb4b83e,65a8f00a..51209280 --- a/src/eu/alefzero/webdav/WebdavClient.java +++ b/src/eu/alefzero/webdav/WebdavClient.java @@@ -43,11 -43,10 +43,11 @@@ import org.apache.http.params.CoreProto import com.owncloud.android.Log_OC; import com.owncloud.android.MainApp; - import com.owncloud.android.network.BearerAuthScheme; import com.owncloud.android.network.BearerCredentials; + + import android.net.Uri; public class WebdavClient extends HttpClient { @@@ -169,7 -168,7 +169,7 @@@ try { method.setFollowRedirects(mFollowRedirects); } catch (Exception e) { - if (mFollowRedirects) Log_OC.d(TAG, "setFollowRedirects failed for " + method.getName() + " method, custom redirection will be used"); + //if (mFollowRedirects) Log_OC.d(TAG, "setFollowRedirects failed for " + method.getName() + " method, custom redirection will be used if needed"); customRedirectionNeeded = mFollowRedirects; } if (mSsoSessionCookie != null && mSsoSessionCookie.length() > 0) { diff --combined src/eu/alefzero/webdav/WebdavEntry.java index bdf1b35a,665a850c..1c6ba695 --- a/src/eu/alefzero/webdav/WebdavEntry.java +++ b/src/eu/alefzero/webdav/WebdavEntry.java @@@ -25,11 -25,10 +25,11 @@@ import org.apache.jackrabbit.webdav.pro import com.owncloud.android.Log_OC; + import android.net.Uri; public class WebdavEntry { - private String mName, mPath, mUri, mContentType; + private String mName, mPath, mUri, mContentType, mEtag; private long mContentLength, mCreateTimestamp, mModifiedTimestamp; public WebdavEntry(MultiStatusResponse ms, String splitElement) { @@@ -43,8 -42,10 +43,10 @@@ DavPropertySet propSet = ms.getProperties(status); @SuppressWarnings("rawtypes") DavProperty prop = propSet.get(DavPropertyName.DISPLAYNAME); - if (prop != null) + if (prop != null) { mName = (String) prop.getName().toString(); + mName = mName.substring(1, mName.length()-1); + } else { String[] tmp = mPath.split("/"); if (tmp.length > 0) @@@ -88,6 -89,12 +90,12 @@@ .parseResponseDate((String) prop.getValue()); mCreateTimestamp = (d != null) ? d.getTime() : 0; } + + prop = propSet.get(DavPropertyName.GETETAG); + if (prop != null) { + mEtag = (String) prop.getValue(); + mEtag = mEtag.substring(1, mEtag.length()-1); + } } else { Log_OC.e("WebdavEntry", @@@ -130,6 -137,10 +138,10 @@@ public long modifiedTimestamp() { return mModifiedTimestamp; } + + public String etag() { + return mEtag; + } private void resetData() { mName = mUri = mContentType = null;