From: masensio Date: Tue, 27 Oct 2015 07:52:17 +0000 (+0100) Subject: Merge branch 'master' into sync_full_folder X-Git-Tag: oc-android-1.9^2~37^2~2 X-Git-Url: http://git.linex4red.de/pub/Android/ownCloud.git/commitdiff_plain/b9ef747a0be9e50d50b709474daf9dca47d60f5f?ds=inline;hp=-c Merge branch 'master' into sync_full_folder Conflicts: owncloud-android-library src/com/owncloud/android/files/services/FileUploader.java --- b9ef747a0be9e50d50b709474daf9dca47d60f5f diff --combined res/values/strings.xml index c1bf7893,267d713e..305a5404 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@@ -74,6 -74,7 +74,7 @@@ seconds ago Nothing in here. Upload something! Loading… + No App found for file type! There are no files in this folder. Tap on a file to display additional information. Size: @@@ -81,7 -82,7 +82,7 @@@ Created: Modified: Download - Refresh file + Synchronize File was renamed to %1$s during upload List Layout Share link @@@ -89,7 -90,8 +90,7 @@@ Yes No OK - Cancel download - Cancel upload + Cancel synchronization Cancel Save & Exit Error @@@ -341,7 -343,7 +342,7 @@@ Security Upload Video Path - Download of %1$s folder could not be completed + Synchronization of %1$s folder could not be completed shared with you diff --combined src/com/owncloud/android/files/FileOperationsHelper.java index 848f8cf4,fba62091..cf0f50f0 --- a/src/com/owncloud/android/files/FileOperationsHelper.java +++ b/src/com/owncloud/android/files/FileOperationsHelper.java @@@ -22,7 -22,11 +22,11 @@@ package com.owncloud.android.files; import android.accounts.Account; + import android.content.ActivityNotFoundException; + import android.content.Context; import android.content.Intent; + import android.content.pm.PackageManager; + import android.content.pm.ResolveInfo; import android.net.Uri; import android.support.v4.app.DialogFragment; import android.webkit.MimeTypeMap; @@@ -43,6 -47,8 +47,8 @@@ import com.owncloud.android.ui.dialog.S import org.apache.http.protocol.HTTP; + import java.util.List; + /** * */ @@@ -86,21 -92,46 +92,46 @@@ public class FileOperationsHelper ); } } - - Intent chooserIntent; + + Intent openFileWithIntent; if (intentForGuessedMimeType != null) { - chooserIntent = Intent.createChooser(intentForGuessedMimeType, mFileActivity.getString(R.string.actionbar_open_with)); + openFileWithIntent = intentForGuessedMimeType; } else { - chooserIntent = Intent.createChooser(intentForSavedMimeType, mFileActivity.getString(R.string.actionbar_open_with)); + openFileWithIntent = intentForSavedMimeType; } - mFileActivity.startActivity(chooserIntent); + List launchables = mFileActivity.getPackageManager(). + queryIntentActivities(openFileWithIntent, PackageManager.GET_INTENT_FILTERS); + + if(launchables != null && launchables.size() > 0) { + try { + mFileActivity.startActivity( + Intent.createChooser( + openFileWithIntent, mFileActivity.getString(R.string.actionbar_open_with) + ) + ); + } catch (ActivityNotFoundException anfe) { + showNoAppForFileTypeToast(mFileActivity.getApplicationContext()); + } + } else { + showNoAppForFileTypeToast(mFileActivity.getApplicationContext()); + } } else { Log_OC.wtf(TAG, "Trying to open a NULL OCFile"); } } + /** + * Displays a toast stating that no application could be found to open the file. + * + * @param context the context to be able to show a toast. + */ + private void showNoAppForFileTypeToast(Context context) { + Toast.makeText(context, + R.string.file_list_no_app_for_file_type, Toast.LENGTH_SHORT) + .show(); + } public void shareFileWithLink(OCFile file) { @@@ -205,12 -236,9 +236,12 @@@ } } - + /** + * Request the synchronization of a file or folder with the OC server, including its contents. + * + * @param file The file or folder to synchronize + */ public void syncFile(OCFile file) { - if (!file.isFolder()){ Intent intent = new Intent(mFileActivity, OperationsService.class); intent.setAction(OperationsService.ACTION_SYNC_FILE); @@@ -226,7 -254,6 +257,7 @@@ intent.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount()); intent.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath()); mFileActivity.startService(intent); + } } @@@ -301,11 -328,19 +332,11 @@@ // for both files and folders FileDownloaderBinder downloaderBinder = mFileActivity.getFileDownloaderBinder(); - FileUploaderBinder uploaderBinder = mFileActivity.getFileUploaderBinder(); if (downloaderBinder != null && downloaderBinder.isDownloading(account, file)) { downloaderBinder.cancel(account, file); - - // TODO - review why is this here, and solve in a better way - // Remove etag for parent, if file is a favorite - if (file.isFavorite()) { - OCFile parent = mFileActivity.getStorageManager().getFileById(file.getParentId()); - parent.setEtag(""); - mFileActivity.getStorageManager().saveFile(parent); - } - - } else if (uploaderBinder != null && uploaderBinder.isUploading(account, file)) { + } + FileUploaderBinder uploaderBinder = mFileActivity.getFileUploaderBinder(); + if (uploaderBinder != null && uploaderBinder.isUploading(account, file)) { uploaderBinder.cancel(account, file); } } diff --combined src/com/owncloud/android/files/services/FileUploader.java index d6f7127d,669ff135..5024da75 --- a/src/com/owncloud/android/files/services/FileUploader.java +++ b/src/com/owncloud/android/files/services/FileUploader.java @@@ -21,14 -21,18 +21,14 @@@ package com.owncloud.android.files.services; import java.io.File; -import java.io.IOException; import java.util.AbstractList; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Vector; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; import android.accounts.Account; import android.accounts.AccountManager; -import android.accounts.AccountsException; import android.accounts.OnAccountsUpdateListener; import android.app.NotificationManager; import android.app.PendingIntent; @@@ -42,7 -46,6 +42,7 @@@ import android.os.Looper import android.os.Message; import android.os.Process; import android.support.v4.app.NotificationCompat; +import android.util.Pair; import android.webkit.MimeTypeMap; import com.owncloud.android.R; @@@ -83,7 -86,6 +83,7 @@@ public class FileUploader extends Servi public static final String EXTRA_REMOTE_PATH = "REMOTE_PATH"; public static final String EXTRA_OLD_REMOTE_PATH = "OLD_REMOTE_PATH"; public static final String EXTRA_OLD_FILE_PATH = "OLD_FILE_PATH"; + public static final String EXTRA_LINKED_TO_PATH = "LINKED_TO"; public static final String ACCOUNT_NAME = "ACCOUNT_NAME"; public static final String KEY_FILE = "FILE"; @@@ -111,10 -113,11 +111,10 @@@ private ServiceHandler mServiceHandler; private IBinder mBinder; private OwnCloudClient mUploadClient = null; - private Account mLastAccount = null; + private Account mCurrentAccount = null; private FileDataStorageManager mStorageManager; - private ConcurrentMap mPendingUploads = - new ConcurrentHashMap(); + private IndexedForest mPendingUploads = new IndexedForest(); private UploadFileOperation mCurrentUpload = null; private NotificationManager mNotificationManager; @@@ -130,14 -133,26 +130,14 @@@ } /** - * Builds a key for mPendingUploads from the account and file to upload - * - * @param account Account where the file to upload is stored - * @param file File to upload - */ - private String buildRemoteName(Account account, OCFile file) { - return account.name + file.getRemotePath(); - } - - private String buildRemoteName(Account account, String remotePath) { - return account.name + remotePath; - } - - /** * Checks if an ownCloud server version should support chunked uploads. * * @param version OwnCloud version instance corresponding to an ownCloud * server. * @return 'True' if the ownCloud server with version supports chunked * uploads. + * + * TODO - move to OCClient */ private static boolean chunkedUploadIsSupported(OwnCloudVersion version) { return (version != null && version.compareTo(OwnCloudVersion.owncloud_v4_5) >= 0); @@@ -267,7 -282,7 +267,7 @@@ files = new OCFile[localPaths.length]; for (int i = 0; i < localPaths.length; i++) { files[i] = obtainNewOCFileToUpload(remotePaths[i], localPaths[i], - ((mimeTypes != null) ? mimeTypes[i] : null), storageManager); + ((mimeTypes != null) ? mimeTypes[i] : null)); if (files[i] == null) { // TODO @andomaex add failure Notification return Service.START_NOT_STICKY; @@@ -283,23 -298,18 +283,23 @@@ UploadFileOperation newUpload = null; try { for (int i = 0; i < files.length; i++) { - uploadKey = buildRemoteName(account, files[i].getRemotePath()); - newUpload = new UploadFileOperation(account, files[i], chunked, isInstant, + newUpload = new UploadFileOperation( + account, + files[i], + chunked, + isInstant, forceOverwrite, localAction, - getApplicationContext()); + getApplicationContext() + ); if (isInstant) { newUpload.setRemoteFolderToBeCreated(); } - // Grants that the file only upload once time - mPendingUploads.putIfAbsent(uploadKey, newUpload); - newUpload.addDatatransferProgressListener(this); - newUpload.addDatatransferProgressListener((FileUploaderBinder)mBinder); + newUpload.addDatatransferProgressListener((FileUploaderBinder) mBinder); + Pair putResult = mPendingUploads.putIfAbsent( + account, files[i].getRemotePath(), newUpload + ); + uploadKey = putResult.first; requestedUploads.add(uploadKey); } @@@ -323,6 -333,7 +323,6 @@@ msg.obj = requestedUploads; mServiceHandler.sendMessage(msg); } - Log_OC.i(TAG, "mPendingUploads size:" + mPendingUploads.size()); return Service.START_NOT_STICKY; } @@@ -375,27 -386,23 +375,27 @@@ /** * Cancels a pending or current upload of a remote file. * - * @param account Owncloud account where the remote file will be stored. - * @param file A file in the queue of pending uploads + * @param account ownCloud account where the remote file will be stored. + * @param file A file in the queue of pending uploads */ public void cancel(Account account, OCFile file) { - UploadFileOperation upload; - synchronized (mPendingUploads) { - upload = mPendingUploads.remove(buildRemoteName(account, file)); - } + Pair removeResult = mPendingUploads.remove(account, file.getRemotePath()); + UploadFileOperation upload = removeResult.first; if (upload != null) { upload.cancel(); + } else { + if (mCurrentUpload != null && mCurrentAccount != null && + mCurrentUpload.getRemotePath().startsWith(file.getRemotePath()) && + account.name.equals(mCurrentAccount.name)) { + mCurrentUpload.cancel(); + } } } /** - * Cancels a pending or current upload for an account + * Cancels all the uploads for an account * - * @param account Owncloud accountName where the remote file will be stored. + * @param account ownCloud account. */ public void cancel(Account account) { Log_OC.d(TAG, "Account= " + account.name); @@@ -407,14 -414,13 +407,14 @@@ } } // Cancel pending uploads - cancelUploadForAccount(account.name); + cancelUploadsForAccount(account); } public void clearListeners() { mBoundListeners.clear(); } + /** * Returns True when the file described by 'file' is being uploaded to * the ownCloud account 'account' or waiting for it @@@ -426,8 -432,22 +426,8 @@@ * @param file A file that could be in the queue of pending uploads */ public boolean isUploading(Account account, OCFile file) { - if (account == null || file == null) - return false; - String targetKey = buildRemoteName(account, file); - synchronized (mPendingUploads) { - if (file.isFolder()) { - // this can be slow if there are many uploads :( - Iterator it = mPendingUploads.keySet().iterator(); - boolean found = false; - while (it.hasNext() && !found) { - found = it.next().startsWith(targetKey); - } - return found; - } else { - return (mPendingUploads.containsKey(targetKey)); - } - } + if (account == null || file == null) return false; + return (mPendingUploads.contains(account, file.getRemotePath())); } @@@ -476,19 -496,15 +476,19 @@@ } /** - * Review uploads and cancel it if its account doesn't exist + * Builds a key for the map of listeners. + * + * TODO remove and replace key with file.getFileId() after changing current policy (upload file, then + * add to local database) to better policy (add to local database, then upload) + * + * @param account ownCloud account where the file to upload belongs. + * @param file File to upload + * @return Key */ - public void checkAccountOfCurrentUpload() { - if (mCurrentUpload != null && - !AccountUtils.exists(mCurrentUpload.getAccount(), getApplicationContext())) { - mCurrentUpload.cancel(); - } - // The rest of uploads are cancelled when they try to start + private String buildRemoteName(Account account, OCFile file) { + return account.name + file.getRemotePath(); } + } /** @@@ -528,13 -544,17 +528,13 @@@ /** * Core upload method: sends the file(s) to upload * - * @param uploadKey Key to access the upload to perform, contained in - * mPendingUploads + * @param uploadKey Key to access the upload to perform, contained in mPendingUploads */ public void uploadFile(String uploadKey) { - synchronized (mPendingUploads) { - mCurrentUpload = mPendingUploads.get(uploadKey); - } + mCurrentUpload = mPendingUploads.get(uploadKey); if (mCurrentUpload != null) { - // Detect if the account exists if (AccountUtils.exists(mCurrentUpload.getAccount(), getApplicationContext())) { Log_OC.d(TAG, "Account " + mCurrentUpload.getAccount().name + " exists"); @@@ -544,20 -564,16 +544,20 @@@ RemoteOperationResult uploadResult = null, grantResult; try { - /// prepare client object to send requests to the ownCloud server - if (mUploadClient == null || - !mLastAccount.equals(mCurrentUpload.getAccount())) { - mLastAccount = mCurrentUpload.getAccount(); - mStorageManager = - new FileDataStorageManager(mLastAccount, getContentResolver()); - OwnCloudAccount ocAccount = new OwnCloudAccount(mLastAccount, this); - mUploadClient = OwnCloudClientManagerFactory.getDefaultSingleton(). - getClientFor(ocAccount, this); - } + /// prepare client object to send the request to the ownCloud server + if (mCurrentAccount == null || !mCurrentAccount.equals(mCurrentUpload.getAccount())) { + mCurrentAccount = mCurrentUpload.getAccount(); + mStorageManager = new FileDataStorageManager( + mCurrentAccount, + getContentResolver() + ); + } // else, reuse storage manager from previous operation + + // always get client from client manager, to get fresh credentials in case of update + OwnCloudAccount ocAccount = new OwnCloudAccount(mCurrentAccount, this); + mUploadClient = OwnCloudClientManagerFactory.getDefaultSingleton(). + getClientFor(ocAccount, this); + /// check the existence of the parent folder for the file to upload String remoteParentPath = new File(mCurrentUpload.getRemotePath()).getParent(); @@@ -572,43 -588,43 +572,43 @@@ uploadResult = mCurrentUpload.execute(mUploadClient); if (uploadResult.isSuccess()) { saveUploadedFile(); + + } else if (uploadResult.getCode() == ResultCode.SYNC_CONFLICT) { + mStorageManager.saveConflict(mCurrentUpload.getFile(), true); } } else { uploadResult = grantResult; } - } catch (AccountsException e) { - Log_OC.e(TAG, "Error while trying to get autorization for " + - mLastAccount.name, e); - uploadResult = new RemoteOperationResult(e); - - } catch (IOException e) { - Log_OC.e(TAG, "Error while trying to get autorization for " + - mLastAccount.name, e); + } catch (Exception e) { + Log_OC.e(TAG, "Error uploading", e); uploadResult = new RemoteOperationResult(e); } finally { - synchronized (mPendingUploads) { - mPendingUploads.remove(uploadKey); - Log_OC.i(TAG, "Remove CurrentUploadItem from pending upload Item Map."); - } - if (uploadResult != null && uploadResult.isException()) { - // enforce the creation of a new client object for next uploads; - // this grant that a new socket will be created in the future if - // the current exception is due to an abrupt lose of network connection - mUploadClient = null; + Pair removeResult; + if (mCurrentUpload.wasRenamed()) { + removeResult = mPendingUploads.removePayload( + mCurrentAccount, + mCurrentUpload.getOldFile().getRemotePath() + ); + } else { + removeResult = mPendingUploads.removePayload( + mCurrentAccount, + mCurrentUpload.getRemotePath() + ); } - } - /// notify result - notifyUploadResult(uploadResult, mCurrentUpload); - sendFinalBroadcast(mCurrentUpload, uploadResult); + /// notify result + notifyUploadResult(mCurrentUpload, uploadResult); + + sendBroadcastUploadFinished(mCurrentUpload, uploadResult, removeResult.second); + } } else { // Cancel the transfer Log_OC.d(TAG, "Account " + mCurrentUpload.getAccount().toString() + " doesn't exist"); - cancelUploadForAccount(mCurrentUpload.getAccount().name); + cancelUploadsForAccount(mCurrentUpload.getAccount()); } } @@@ -675,7 -691,7 +675,7 @@@ * synchronized with the server, specially the modification time and Etag * (where available) * - * TODO refactor this ugly thing + * TODO move into UploadFileOperation */ private void saveUploadedFile() { OCFile file = mCurrentUpload.getFile(); @@@ -693,8 -709,6 +693,8 @@@ if (result.isSuccess()) { updateOCFile(file, (RemoteFile) result.getData().get(0)); file.setLastSyncDateForProperties(syncDate); + } else { + Log_OC.e(TAG, "Error reading properties of file after successful upload; this is gonna hurt..."); } // / maybe this would be better as part of UploadFileOperation... or @@@ -704,7 -718,6 +704,7 @@@ if (oldFile.fileExists()) { oldFile.setStoragePath(null); mStorageManager.saveFile(oldFile); + mStorageManager.saveConflict(oldFile, false); } // else: it was just an automatic renaming due to a name // coincidence; nothing else is needed, the storagePath is right @@@ -712,7 -725,7 +712,10 @@@ } file.setNeedsUpdateThumbnail(true); mStorageManager.saveFile(file); + mStorageManager.saveConflict(file, false); ++ + mStorageManager.triggerMediaScan(file.getStoragePath()); ++ } private void updateOCFile(OCFile file, RemoteFile remoteFile) { @@@ -721,11 -734,12 +724,11 @@@ file.setMimetype(remoteFile.getMimeType()); file.setModificationTimestamp(remoteFile.getModifiedTimestamp()); file.setModificationTimestampAtLastSyncForData(remoteFile.getModifiedTimestamp()); - // file.setEtag(remoteFile.getEtag()); // TODO Etag, where available + file.setEtag(remoteFile.getEtag()); file.setRemoteId(remoteFile.getRemoteId()); } - private OCFile obtainNewOCFileToUpload(String remotePath, String localPath, String mimeType, - FileDataStorageManager storageManager) { + private OCFile obtainNewOCFileToUpload(String remotePath, String localPath, String mimeType) { // MIME type if (mimeType == null || mimeType.length() <= 0) { @@@ -816,11 -830,11 +819,11 @@@ /** * Updates the status notification with the result of an upload operation. * - * @param uploadResult Result of the upload operation. - * @param upload Finished upload operation + * @param uploadResult Result of the upload operation. + * @param upload Finished upload operation */ - private void notifyUploadResult( - RemoteOperationResult uploadResult, UploadFileOperation upload) { + private void notifyUploadResult(UploadFileOperation upload, + RemoteOperationResult uploadResult) { Log_OC.d(TAG, "NotifyUploadResult with resultCode: " + uploadResult.getCode()); // / cancelled operation or success -> silent removal of progress notification mNotificationManager.cancel(R.string.uploader_upload_in_progress_ticker); @@@ -927,15 -941,10 +930,15 @@@ * Sends a broadcast in order to the interested activities can update their * view * - * @param upload Finished upload operation - * @param uploadResult Result of the upload operation + * @param upload Finished upload operation + * @param uploadResult Result of the upload operation + * @param unlinkedFromRemotePath Path in the uploads tree where the upload was unlinked from */ - private void sendFinalBroadcast(UploadFileOperation upload, RemoteOperationResult uploadResult) { + private void sendBroadcastUploadFinished( + UploadFileOperation upload, + RemoteOperationResult uploadResult, + String unlinkedFromRemotePath) { + Intent end = new Intent(getUploadFinishMessage()); end.putExtra(EXTRA_REMOTE_PATH, upload.getRemotePath()); // real remote // path, after @@@ -948,10 -957,6 +951,10 @@@ end.putExtra(EXTRA_OLD_FILE_PATH, upload.getOriginalStoragePath()); end.putExtra(ACCOUNT_NAME, upload.getAccount().name); end.putExtra(EXTRA_UPLOAD_RESULT, uploadResult.isSuccess()); + if (unlinkedFromRemotePath != null) { + end.putExtra(EXTRA_LINKED_TO_PATH, unlinkedFromRemotePath); + } + sendStickyBroadcast(end); } @@@ -961,8 -966,6 +964,8 @@@ * @param localPath Full path to a file in the local file system. * @param mimeType MIME type of the file. * @return true if is needed to add the pdf file extension to the file + * + * TODO - move to OCFile or Utils class */ private boolean isPdfFileFromContentProviderWithoutExtension(String localPath, String mimeType) { @@@ -973,11 -976,20 +976,11 @@@ /** * Remove uploads of an account - * @param accountName Name of an OC account + * + * @param account Downloads account to remove */ - private void cancelUploadForAccount(String accountName){ - // this can be slow if there are many uploads :( - Iterator it = mPendingUploads.keySet().iterator(); - Log_OC.d(TAG, "Number of pending updloads= " + mPendingUploads.size()); - while (it.hasNext()) { - String key = it.next(); - Log_OC.d(TAG, "mPendingUploads CANCELLED " + key); - if (key.startsWith(accountName)) { - synchronized (mPendingUploads) { - mPendingUploads.remove(key); - } - } - } + private void cancelUploadsForAccount(Account account){ + // Cancel pending uploads + mPendingUploads.remove(account); } } diff --combined src/com/owncloud/android/syncadapter/FileSyncAdapter.java index b0d2f222,7f8b0db1..7dfe5201 --- a/src/com/owncloud/android/syncadapter/FileSyncAdapter.java +++ b/src/com/owncloud/android/syncadapter/FileSyncAdapter.java @@@ -30,6 -30,7 +30,6 @@@ import java.util.Map import org.apache.jackrabbit.webdav.DavException; -import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.authentication.AuthenticatorActivity; import com.owncloud.android.datamodel.FileDataStorageManager; @@@ -58,7 -59,7 +58,7 @@@ import android.support.v4.app.Notificat * Implementation of {@link AbstractThreadedSyncAdapter} responsible for synchronizing * ownCloud files. * - * Performs a full synchronization of the account recieved in {@link #onPerformSync(Account, Bundle, + * Performs a full synchronization of the account received in {@link #onPerformSync(Account, Bundle, * String, ContentProviderClient, SyncResult)}. */ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter { @@@ -76,7 -77,9 +76,7 @@@ ".EVENT_FULL_SYNC_END"; public static final String EVENT_FULL_SYNC_FOLDER_CONTENTS_SYNCED = FileSyncAdapter.class.getName() + ".EVENT_FULL_SYNC_FOLDER_CONTENTS_SYNCED"; - //public static final String EVENT_FULL_SYNC_FOLDER_SIZE_SYNCED = - // FileSyncAdapter.class.getName() + ".EVENT_FULL_SYNC_FOLDER_SIZE_SYNCED"; - + public static final String EXTRA_ACCOUNT_NAME = FileSyncAdapter.class.getName() + ".EXTRA_ACCOUNT_NAME"; public static final String EXTRA_FOLDER_PATH = FileSyncAdapter.class.getName() + @@@ -265,6 -268,16 +265,6 @@@ if (mFailedResultsCounter > MAX_FAILED_RESULTS || isFinisher(mLastFailedResult)) return; - /* - OCFile folder, - long currentSyncTime, - boolean updateFolderProperties, - boolean syncFullAccount, - DataStorageManager dataStorageManager, - Account account, - Context context ) { - } - */ // folder synchronization RefreshFolderOperation synchFolderOp = new RefreshFolderOperation( folder, mCurrentSyncTime, @@@ -295,7 -308,7 +295,7 @@@ // synchronize children folders List children = synchFolderOp.getChildren(); // beware of the 'hidden' recursion here! - fetchChildren(folder, children, synchFolderOp.getRemoteFolderChanged()); + syncChildren(children); } } else { @@@ -338,19 -351,25 +338,19 @@@ /** * Triggers the synchronization of any folder contained in the list of received files. + * + * No consideration of etag here because it MUST walk down anyway, in case that kept-in-sync files + * have local changes. * * @param files Files to recursively synchronize. */ - private void fetchChildren(OCFile parent, List files, boolean parentEtagChanged) { + private void syncChildren(List files) { int i; - OCFile newFile = null; - //String etag = null; - //boolean syncDown = false; + OCFile newFile; for (i=0; i < files.size() && !mCancellation; i++) { newFile = files.get(i); if (newFile.isFolder()) { - /* - etag = newFile.getEtag(); - syncDown = (parentEtagChanged || etag == null || etag.length() == 0); - if(syncDown) { */ - synchronizeFolder(newFile); - //sendLocalBroadcast(EVENT_FULL_SYNC_FOLDER_SIZE_SYNCED, parent.getRemotePath(), - // null); - //} + synchronizeFolder(newFile); } } @@@ -507,6 -526,7 +507,7 @@@ private NotificationCompat.Builder createNotificationBuilder() { NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(getContext()); notificationBuilder.setSmallIcon(R.drawable.notification_icon).setAutoCancel(true); + notificationBuilder.setColor(getContext().getResources().getColor(R.color.primary)); return notificationBuilder; } diff --combined src/com/owncloud/android/ui/activity/FileDisplayActivity.java index f4eda3f3,240d146f..644cf014 --- a/src/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/com/owncloud/android/ui/activity/FileDisplayActivity.java @@@ -1048,11 -1048,7 +1048,11 @@@ public class FileDisplayActivity extend (uploadedRemotePath.startsWith(currentDir.getRemotePath())); if (sameAccount && isDescendant) { - refreshListOfFilesFragment(); + String linkedToRemotePath = + intent.getStringExtra(FileDownloader.EXTRA_LINKED_TO_PATH); + if (linkedToRemotePath == null || isAscendant(linkedToRemotePath)) { + refreshListOfFilesFragment(); + } } boolean uploadWasFine = intent.getBooleanExtra(FileUploader.EXTRA_UPLOAD_RESULT, @@@ -1105,16 -1101,6 +1105,16 @@@ } + // TODO refactor this receiver, and maybe DownloadFinishReceiver; this method is duplicated :S + private boolean isAscendant(String linkedToRemotePath) { + OCFile currentDir = getCurrentDir(); + return ( + currentDir != null && + currentDir.getRemotePath().startsWith(linkedToRemotePath) + ); + } + + } @@@ -1126,10 -1112,11 +1126,10 @@@ */ private class DownloadFinishReceiver extends BroadcastReceiver { - //int refreshCounter = 0; @Override public void onReceive(Context context, Intent intent) { try { - boolean sameAccount = isSameAccount(context, intent); + boolean sameAccount = isSameAccount(intent); String downloadedRemotePath = intent.getStringExtra(FileDownloader.EXTRA_REMOTE_PATH); boolean isDescendant = isDescendant(downloadedRemotePath); @@@ -1138,6 -1125,7 +1138,6 @@@ String linkedToRemotePath = intent.getStringExtra(FileDownloader.EXTRA_LINKED_TO_PATH); if (linkedToRemotePath == null || isAscendant(linkedToRemotePath)) { - //Log_OC.v(TAG, "refresh #" + ++refreshCounter); refreshListOfFilesFragment(); } refreshSecondFragment( @@@ -1179,7 -1167,7 +1179,7 @@@ ); } - private boolean isSameAccount(Context context, Intent intent) { + private boolean isSameAccount(Intent intent) { String accountName = intent.getStringExtra(FileDownloader.ACCOUNT_NAME); return (accountName != null && getAccount() != null && accountName.equals(getAccount().name)); @@@ -1553,6 -1541,7 +1553,7 @@@ OCFile syncedFile = operation.getLocalFile(); onTransferStateChanged(syncedFile, true, true); invalidateOptionsMenu(); + refreshShowDetails(); } } } diff --combined src/com/owncloud/android/ui/fragment/FileDetailFragment.java index e58b4d93,b2e6464c..6aafbf76 --- a/src/com/owncloud/android/ui/fragment/FileDetailFragment.java +++ b/src/com/owncloud/android/ui/fragment/FileDetailFragment.java @@@ -22,6 -22,7 +22,7 @@@ package com.owncloud.android.ui.fragment; import android.accounts.Account; + import android.graphics.Bitmap; import android.os.Bundle; import android.view.LayoutInflater; import android.view.Menu; @@@ -35,9 -36,11 +36,11 @@@ import android.widget.ImageView import android.widget.ProgressBar; import android.widget.TextView; + import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; + import com.owncloud.android.datamodel.ThumbnailsCacheManager; import com.owncloud.android.files.FileMenuFilter; import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder; import com.owncloud.android.files.services.FileUploader.FileUploaderBinder; @@@ -245,8 -248,9 +248,8 @@@ public class FileDetailFragment extend dialog.show(getFragmentManager(), FTAG_RENAME_FILE); return true; } - case R.id.action_cancel_download: - case R.id.action_cancel_upload: { - ((FileDisplayActivity) mContainerActivity).cancelTransference(getFile()); + case R.id.action_cancel_sync: { + ((FileDisplayActivity)mContainerActivity).cancelTransference(getFile()); return true; } case R.id.action_download_file: @@@ -296,6 -300,7 +299,6 @@@ } } - /** * Check if the fragment was created with an empty layout. An empty fragment can't show file details, must be replaced. * @@@ -337,7 -342,7 +340,7 @@@ // set file details setFilename(file.getFileName()); - setFiletype(file.getMimetype(), file.getFileName()); + setFiletype(file); setFilesize(file.getFileLength()); setTimeModified(file.getModificationTimestamp()); @@@ -391,18 -396,54 +394,54 @@@ /** * Updates the MIME type in view - * @param mimetype MIME type to set - * @param filename Name of the file, to deduce the icon to use in case the MIME type is not precise enough + * @param file : An {@link OCFile} */ - private void setFiletype(String mimetype, String filename) { + private void setFiletype(OCFile file) { + String mimetype = file.getMimetype(); TextView tv = (TextView) getView().findViewById(R.id.fdType); if (tv != null) { + // mimetype MIME type to set String printableMimetype = DisplayUtils.convertMIMEtoPrettyPrint(mimetype); tv.setText(printableMimetype); } + ImageView iv = (ImageView) getView().findViewById(R.id.fdIcon); + if (iv != null) { - iv.setImageResource(MimetypeIconUtil.getFileTypeIconId(mimetype, filename)); + Bitmap thumbnail; + iv.setTag(file.getFileId()); + + if (file.isImage()) { + String tagId = String.valueOf(file.getRemoteId()); + thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(tagId); + + if (thumbnail != null && !file.needsUpdateThumbnail()) { + iv.setImageBitmap(thumbnail); + } else { + // generate new Thumbnail + if (ThumbnailsCacheManager.cancelPotentialWork(file, iv)) { + final ThumbnailsCacheManager.ThumbnailGenerationTask task = + new ThumbnailsCacheManager.ThumbnailGenerationTask( + iv, mContainerActivity.getStorageManager(), mAccount + ); + if (thumbnail == null) { + thumbnail = ThumbnailsCacheManager.mDefaultImg; + } + final ThumbnailsCacheManager.AsyncDrawable asyncDrawable = + new ThumbnailsCacheManager.AsyncDrawable( + MainApp.getAppContext().getResources(), + thumbnail, + task + ); + iv.setImageDrawable(asyncDrawable); + task.execute(file); + } + } + } else { + // Name of the file, to deduce the icon to use in case the MIME type is not precise enough + String filename = file.getFileName(); + iv.setImageResource(MimetypeIconUtil.getFileTypeIconId(mimetype, filename)); + } } } diff --combined src/com/owncloud/android/utils/FileStorageUtils.java index da1b69e7,39837717..1740ca58 --- a/src/com/owncloud/android/utils/FileStorageUtils.java +++ b/src/com/owncloud/android/utils/FileStorageUtils.java @@@ -32,7 -32,6 +32,7 @@@ import com.owncloud.android.R import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.lib.resources.files.RemoteFile; +import android.accounts.Account; import android.annotation.SuppressLint; import android.content.Context; import android.content.SharedPreferences; @@@ -54,6 -53,8 +54,6 @@@ public class FileStorageUtils public static Boolean mSortAscending = true; - //private static final String LOG_TAG = "FileStorageUtils"; - public static final String getSavePath(String accountName) { File sdCard = Environment.getExternalStorageDirectory(); return sdCard.getAbsolutePath() + "/" + MainApp.getDataFolder() + "/" + Uri.encode(accountName, "@"); @@@ -119,7 -120,7 +119,7 @@@ * Creates and populates a new {@link OCFile} object with the data read from the server. * * @param remote remote file read from the server (remote file or folder). - * @return New OCFile instance representing the remote resource described by we. + * @return New OCFile instance representing the remote resource described by remote. */ public static OCFile fillOCFile(RemoteFile remote) { OCFile file = new OCFile(remote.getRemotePath()); @@@ -253,7 -254,7 +253,7 @@@ Collections.sort(files, new Comparator() { public int compare(OCFile o1, OCFile o2) { if (o1.isFolder() && o2.isFolder()) { - return val * o1.getRemotePath().toLowerCase().compareTo(o2.getRemotePath().toLowerCase()); + return val * new AlphanumComparator().compare(o1, o2); } else if (o1.isFolder()) { return -1; } else if (o2.isFolder()) { @@@ -301,33 -302,5 +301,33 @@@ String result = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension.toLowerCase()); return (result != null) ? result : ""; } - + + /** + * Scans the default location for saving local copies of files searching for + * a 'lost' file with the same full name as the {@link OCFile} received as + * parameter. + * + * This method helps to keep linked local copies of the files when the app is uninstalled, and then + * reinstalled in the device. OR after the cache of the app was deleted in system settings. + * + * The method is assuming that all the local changes in the file where synchronized in the past. This is dangerous, + * but assuming the contrary could lead to massive unnecessary synchronizations of downloaded file after deleting + * the app cache. + * + * This should be changed in the near future to avoid any chance of data loss, but we need to add some options + * to limit hard automatic synchronizations to wifi, unless the user wants otherwise. + * + * @param file File to associate a possible 'lost' local file. + * @param account Account holding file. + */ + public static void searchForLocalFileInDefaultPath(OCFile file, Account account) { + if (file.getStoragePath() == null && !file.isFolder()) { + File f = new File(FileStorageUtils.getDefaultSavePathFor(account.name, file)); + if (f.exists()) { + file.setStoragePath(f.getAbsolutePath()); + file.setLastSyncDateForData(f.lastModified()); + } + } + } + }