X-Git-Url: http://git.linex4red.de/pub/Android/ownCloud.git/blobdiff_plain/ab8bf56a15193f69169d284876ac6bbd80ff2150..ff82b51e49f40155e7c340090f5ee759af2bf3ad:/src/com/owncloud/android/files/services/FileUploader.java diff --git a/src/com/owncloud/android/files/services/FileUploader.java b/src/com/owncloud/android/files/services/FileUploader.java index f9c57ccf..98ad87e2 100644 --- a/src/com/owncloud/android/files/services/FileUploader.java +++ b/src/com/owncloud/android/files/services/FileUploader.java @@ -1,6 +1,8 @@ -/* ownCloud Android client application +/** + * ownCloud Android client application + * * Copyright (C) 2012 Bartek Przybylski - * Copyright (C) 2012-2013 ownCloud Inc. + * Copyright (C) 2012-2015 ownCloud Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -31,6 +33,7 @@ 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; import android.app.Service; @@ -54,7 +57,6 @@ import com.owncloud.android.db.DbHandler; import com.owncloud.android.lib.common.OwnCloudAccount; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.OwnCloudClientManagerFactory; -import com.owncloud.android.lib.common.accounts.AccountUtils.Constants; import com.owncloud.android.lib.common.network.OnDatatransferProgressListener; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; @@ -76,8 +78,8 @@ import com.owncloud.android.utils.ErrorMessageAdapter; import com.owncloud.android.utils.UriUtils; - -public class FileUploader extends Service implements OnDatatransferProgressListener { +public class FileUploader extends Service + implements OnDatatransferProgressListener, OnAccountsUpdateListener { private static final String UPLOAD_FINISH_MESSAGE = "UPLOAD_FINISH"; public static final String EXTRA_UPLOAD_RESULT = "RESULT"; @@ -114,7 +116,8 @@ public class FileUploader extends Service implements OnDatatransferProgressListe private Account mLastAccount = null; private FileDataStorageManager mStorageManager; - private ConcurrentMap mPendingUploads = new ConcurrentHashMap(); + private ConcurrentMap mPendingUploads = + new ConcurrentHashMap(); private UploadFileOperation mCurrentUpload = null; private NotificationManager mNotificationManager; @@ -126,7 +129,7 @@ public class FileUploader extends Service implements OnDatatransferProgressListe public static String getUploadFinishMessage() { - return FileUploader.class.getName().toString() + UPLOAD_FINISH_MESSAGE; + return FileUploader.class.getName() + UPLOAD_FINISH_MESSAGE; } /** @@ -161,16 +164,41 @@ public class FileUploader extends Service implements OnDatatransferProgressListe @Override public void onCreate() { super.onCreate(); - Log_OC.i(TAG, "mPendingUploads size:" + mPendingUploads.size()); + Log_OC.d(TAG, "Creating service"); mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); - HandlerThread thread = new HandlerThread("FileUploaderThread", Process.THREAD_PRIORITY_BACKGROUND); + HandlerThread thread = new HandlerThread("FileUploaderThread", + Process.THREAD_PRIORITY_BACKGROUND); thread.start(); mServiceLooper = thread.getLooper(); mServiceHandler = new ServiceHandler(mServiceLooper, this); mBinder = new FileUploaderBinder(); + + // add AccountsUpdatedListener + AccountManager am = AccountManager.get(getApplicationContext()); + am.addOnAccountsUpdatedListener(this, null, false); } /** + * Service clean up + */ + @Override + public void onDestroy() { + Log_OC.v(TAG, "Destroying service" ); + mBinder = null; + mServiceHandler = null; + mServiceLooper.quit(); + mServiceLooper = null; + mNotificationManager = null; + + // remove AccountsUpdatedListener + AccountManager am = AccountManager.get(getApplicationContext()); + am.removeOnAccountsUpdatedListener(this); + + super.onDestroy(); + } + + + /** * Entry point to add one or several files to the queue of uploads. * * New uploads are added calling to startService(), resulting in a call to @@ -179,6 +207,8 @@ public class FileUploader extends Service implements OnDatatransferProgressListe */ @Override public int onStartCommand(Intent intent, int flags, int startId) { + Log_OC.d(TAG, "Starting command with id " + startId); + if (!intent.hasExtra(KEY_ACCOUNT) || !intent.hasExtra(KEY_UPLOAD_TYPE) || !(intent.hasExtra(KEY_LOCAL_FILE) || intent.hasExtra(KEY_FILE))) { Log_OC.e(TAG, "Not enough information provided in intent"); @@ -199,7 +229,7 @@ public class FileUploader extends Service implements OnDatatransferProgressListe if (uploadType == UPLOAD_SINGLE_FILE) { if (intent.hasExtra(KEY_FILE)) { - files = new OCFile[] { (OCFile) intent.getParcelableExtra(KEY_FILE) }; + files = new OCFile[] { intent.getParcelableExtra(KEY_FILE) }; } else { localPaths = new String[] { intent.getStringExtra(KEY_LOCAL_FILE) }; @@ -211,11 +241,11 @@ public class FileUploader extends Service implements OnDatatransferProgressListe if (intent.hasExtra(KEY_FILE)) { files = (OCFile[]) intent.getParcelableArrayExtra(KEY_FILE); // TODO - // will - // this - // casting - // work - // fine? + // will + // this + // casting + // work + // fine? } else { localPaths = intent.getStringArrayExtra(KEY_LOCAL_FILE); @@ -224,7 +254,8 @@ public class FileUploader extends Service implements OnDatatransferProgressListe } } - FileDataStorageManager storageManager = new FileDataStorageManager(account, getContentResolver()); + FileDataStorageManager storageManager = new FileDataStorageManager(account, + getContentResolver()); boolean forceOverwrite = intent.getBooleanExtra(KEY_FORCE_OVERWRITE, false); boolean isInstant = intent.getBooleanExtra(KEY_INSTANT_UPLOAD, false); @@ -250,8 +281,8 @@ public class FileUploader extends Service implements OnDatatransferProgressListe files = new OCFile[localPaths.length]; for (int i = 0; i < localPaths.length; i++) { - files[i] = obtainNewOCFileToUpload(remotePaths[i], localPaths[i], ((mimeTypes != null) ? mimeTypes[i] - : (String) null), storageManager); + files[i] = obtainNewOCFileToUpload(remotePaths[i], localPaths[i], + ((mimeTypes != null) ? mimeTypes[i] : null), storageManager); if (files[i] == null) { // TODO @andomaex add failure Notification return Service.START_NOT_STICKY; @@ -259,9 +290,7 @@ public class FileUploader extends Service implements OnDatatransferProgressListe } } - AccountManager aMgr = AccountManager.get(this); - String version = aMgr.getUserData(account, Constants.KEY_OC_VERSION); - OwnCloudVersion ocv = new OwnCloudVersion(version); + OwnCloudVersion ocv = AccountUtils.getServerVersion(account); boolean chunked = FileUploader.chunkedUploadIsSupported(ocv); AbstractList requestedUploads = new Vector(); @@ -270,12 +299,14 @@ public class FileUploader extends Service implements OnDatatransferProgressListe try { for (int i = 0; i < files.length; i++) { uploadKey = buildRemoteName(account, files[i].getRemotePath()); - newUpload = new UploadFileOperation(account, files[i], chunked, isInstant, forceOverwrite, localAction, + newUpload = new UploadFileOperation(account, files[i], chunked, isInstant, + forceOverwrite, localAction, getApplicationContext()); if (isInstant) { newUpload.setRemoteFolderToBeCreated(); } - mPendingUploads.putIfAbsent(uploadKey, newUpload); // Grants that the file only upload once time + // Grants that the file only upload once time + mPendingUploads.putIfAbsent(uploadKey, newUpload); newUpload.addDatatransferProgressListener(this); newUpload.addDatatransferProgressListener((FileUploaderBinder)mBinder); @@ -327,6 +358,15 @@ public class FileUploader extends Service implements OnDatatransferProgressListe return false; // not accepting rebinding (default behaviour) } + @Override + public void onAccountsUpdated(Account[] accounts) { + // Review current upload, and cancel it if its account doen't exist + if (mCurrentUpload != null && + !AccountUtils.exists(mCurrentUpload.getAccount(), getApplicationContext())) { + mCurrentUpload.cancel(); + } + // The rest of uploads are cancelled when they try to start + } /** * Binder to let client components to perform operations on the queue of @@ -337,9 +377,11 @@ public class FileUploader extends Service implements OnDatatransferProgressListe public class FileUploaderBinder extends Binder implements OnDatatransferProgressListener { /** - * Map of listeners that will be reported about progress of uploads from a {@link FileUploaderBinder} instance + * Map of listeners that will be reported about progress of uploads from a + * {@link FileUploaderBinder} instance */ - private Map mBoundListeners = new HashMap(); + private Map mBoundListeners = + new HashMap(); /** * Cancels a pending or current upload of a remote file. @@ -348,7 +390,7 @@ public class FileUploader extends Service implements OnDatatransferProgressListe * @param file A file in the queue of pending uploads */ public void cancel(Account account, OCFile file) { - UploadFileOperation upload = null; + UploadFileOperation upload; synchronized (mPendingUploads) { upload = mPendingUploads.remove(buildRemoteName(account, file)); } @@ -357,20 +399,34 @@ public class FileUploader extends Service implements OnDatatransferProgressListe } } + /** + * Cancels a pending or current upload for an account + * + * @param account Owncloud accountName where the remote file will be stored. + */ + public void cancel(Account account) { + Log_OC.d(TAG, "Account= " + account.name); + if (mCurrentUpload != null) { + Log_OC.d(TAG, "Current Upload Account= " + mCurrentUpload.getAccount().name); + if (mCurrentUpload.getAccount().name.equals(account.name)) { + mCurrentUpload.cancel(); + } + } + // Cancel pending uploads + cancelUploadForAccount(account.name); + } 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 * - * If 'file' is a directory, returns 'true' if some of its descendant files is uploading or waiting to upload. + * If 'file' is a directory, returns 'true' if some of its descendant files + * is uploading or waiting to upload. * * @param account ownCloud account where the remote file will be stored. * @param file A file that could be in the queue of pending uploads @@ -402,7 +458,8 @@ public class FileUploader extends Service implements OnDatatransferProgressListe * @param account ownCloud account holding the file of interest. * @param file {@link OCFile} of interest for listener. */ - public void addDatatransferProgressListener (OnDatatransferProgressListener listener, Account account, OCFile file) { + public void addDatatransferProgressListener (OnDatatransferProgressListener listener, + Account account, OCFile file) { if (account == null || file == null || listener == null) return; String targetKey = buildRemoteName(account, file); mBoundListeners.put(targetKey, listener); @@ -417,7 +474,8 @@ public class FileUploader extends Service implements OnDatatransferProgressListe * @param account ownCloud account holding the file of interest. * @param file {@link OCFile} of interest for listener. */ - public void removeDatatransferProgressListener (OnDatatransferProgressListener listener, Account account, OCFile file) { + public void removeDatatransferProgressListener (OnDatatransferProgressListener listener, + Account account, OCFile file) { if (account == null || file == null || listener == null) return; String targetKey = buildRemoteName(account, file); if (mBoundListeners.get(targetKey) == listener) { @@ -427,15 +485,26 @@ public class FileUploader extends Service implements OnDatatransferProgressListe @Override - public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, - String fileName) { + public void onTransferProgress(long progressRate, long totalTransferredSoFar, + long totalToTransfer, String fileName) { String key = buildRemoteName(mCurrentUpload.getAccount(), mCurrentUpload.getFile()); OnDatatransferProgressListener boundListener = mBoundListeners.get(key); if (boundListener != null) { - boundListener.onTransferProgress(progressRate, totalTransferredSoFar, totalToTransfer, fileName); + boundListener.onTransferProgress(progressRate, totalTransferredSoFar, + totalToTransfer, fileName); } } + /** + * Review uploads and cancel it if its account doesn't exist + */ + public void checkAccountOfCurrentUpload() { + if (mCurrentUpload != null && + !AccountUtils.exists(mCurrentUpload.getAccount(), getApplicationContext())) { + mCurrentUpload.cancel(); + } + // The rest of uploads are cancelled when they try to start + } } /** @@ -467,6 +536,7 @@ public class FileUploader extends Service implements OnDatatransferProgressListe mService.uploadFile(it.next()); } } + Log_OC.d(TAG, "Stopping command after id " + msg.arg1); mService.stopSelf(msg.arg1); } } @@ -487,15 +557,16 @@ public class FileUploader extends Service implements OnDatatransferProgressListe // Detect if the account exists if (AccountUtils.exists(mCurrentUpload.getAccount(), getApplicationContext())) { - Log_OC.d(TAG, "Account " + mCurrentUpload.getAccount().toString() + " exists"); + Log_OC.d(TAG, "Account " + mCurrentUpload.getAccount().name + " exists"); notifyUploadStart(mCurrentUpload); - RemoteOperationResult uploadResult = null, grantResult = null; + RemoteOperationResult uploadResult = null, grantResult; try { /// prepare client object to send requests to the ownCloud server - if (mUploadClient == null || !mLastAccount.equals(mCurrentUpload.getAccount())) { + if (mUploadClient == null || + !mLastAccount.equals(mCurrentUpload.getAccount())) { mLastAccount = mCurrentUpload.getAccount(); mStorageManager = new FileDataStorageManager(mLastAccount, getContentResolver()); @@ -506,7 +577,8 @@ public class FileUploader extends Service implements OnDatatransferProgressListe /// check the existence of the parent folder for the file to upload String remoteParentPath = new File(mCurrentUpload.getRemotePath()).getParent(); - remoteParentPath = remoteParentPath.endsWith(OCFile.PATH_SEPARATOR) ? remoteParentPath : remoteParentPath + OCFile.PATH_SEPARATOR; + remoteParentPath = remoteParentPath.endsWith(OCFile.PATH_SEPARATOR) ? + remoteParentPath : remoteParentPath + OCFile.PATH_SEPARATOR; grantResult = grantFolderExistence(remoteParentPath); /// perform the upload @@ -522,11 +594,13 @@ public class FileUploader extends Service implements OnDatatransferProgressListe } } catch (AccountsException e) { - Log_OC.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, 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); + Log_OC.e(TAG, "Error while trying to get autorization for " + + mLastAccount.name, e); uploadResult = new RemoteOperationResult(e); } finally { @@ -534,9 +608,10 @@ public class FileUploader extends Service implements OnDatatransferProgressListe mPendingUploads.remove(uploadKey); Log_OC.i(TAG, "Remove CurrentUploadItem from pending upload Item Map."); } - if (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 + 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; } } @@ -547,26 +622,31 @@ public class FileUploader extends Service implements OnDatatransferProgressListe } else { // Cancel the transfer - Log_OC.d(TAG, "Account " + mCurrentUpload.getAccount().toString() + " doesn't exist"); + Log_OC.d(TAG, "Account " + mCurrentUpload.getAccount().toString() + + " doesn't exist"); + cancelUploadForAccount(mCurrentUpload.getAccount().name); + } } } /** - * Checks the existence of the folder where the current file will be uploaded both in the remote server - * and in the local database. + * Checks the existence of the folder where the current file will be uploaded both + * in the remote server and in the local database. * - * If the upload is set to enforce the creation of the folder, the method tries to create it both remote - * and locally. + * If the upload is set to enforce the creation of the folder, the method tries to + * create it both remote and locally. * * @param pathToGrant Full remote path whose existence will be granted. - * @return An {@link OCFile} instance corresponding to the folder where the file will be uploaded. + * @return An {@link OCFile} instance corresponding to the folder where the file + * will be uploaded. */ private RemoteOperationResult grantFolderExistence(String pathToGrant) { RemoteOperation operation = new ExistenceCheckRemoteOperation(pathToGrant, this, false); RemoteOperationResult result = operation.execute(mUploadClient); - if (!result.isSuccess() && result.getCode() == ResultCode.FILE_NOT_FOUND && mCurrentUpload.isRemoteFolderToBeCreated()) { + if (!result.isSuccess() && result.getCode() == ResultCode.FILE_NOT_FOUND && + mCurrentUpload.isRemoteFolderToBeCreated()) { SyncOperation syncOp = new CreateFolderOperation( pathToGrant, true); result = syncOp.execute(mUploadClient, mStorageManager); } @@ -587,7 +667,8 @@ public class FileUploader extends Service implements OnDatatransferProgressListe private OCFile createLocalFolder(String remotePath) { String parentPath = new File(remotePath).getParent(); - parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : parentPath + OCFile.PATH_SEPARATOR; + parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? + parentPath : parentPath + OCFile.PATH_SEPARATOR; OCFile parent = mStorageManager.getFileByPath(parentPath); if (parent == null) { parent = createLocalFolder(parentPath); @@ -622,7 +703,8 @@ public class FileUploader extends Service implements OnDatatransferProgressListe // new PROPFIND to keep data consistent with server // in theory, should return the same we already have - ReadRemoteFileOperation operation = new ReadRemoteFileOperation(mCurrentUpload.getRemotePath()); + ReadRemoteFileOperation operation = + new ReadRemoteFileOperation(mCurrentUpload.getRemotePath()); RemoteOperationResult result = operation.execute(mUploadClient); if (result.isSuccess()) { updateOCFile(file, (RemoteFile) result.getData().get(0)); @@ -664,7 +746,8 @@ public class FileUploader extends Service implements OnDatatransferProgressListe mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension( remotePath.substring(remotePath.lastIndexOf('.') + 1)); } catch (IndexOutOfBoundsException e) { - Log_OC.e(TAG, "Trying to find out MIME type of a file without extension: " + remotePath); + Log_OC.e(TAG, "Trying to find out MIME type of a file without extension: " + + remotePath); } } if (mimeType == null) { @@ -729,11 +812,13 @@ public class FileUploader extends Service implements OnDatatransferProgressListe * Callback method to update the progress bar in the status notification */ @Override - public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String filePath) { + public void onTransferProgress(long progressRate, long totalTransferredSoFar, + long totalToTransfer, String filePath) { int percent = (int) (100.0 * ((double) totalTransferredSoFar) / ((double) totalToTransfer)); if (percent != mLastPercent) { mNotificationBuilder.setProgress(100, percent, false); - String fileName = filePath.substring(filePath.lastIndexOf(FileUtils.PATH_SEPARATOR) + 1); + String fileName = filePath.substring( + filePath.lastIndexOf(FileUtils.PATH_SEPARATOR) + 1); String text = String.format(getString(R.string.uploader_upload_in_progress_content), percent, fileName); mNotificationBuilder.setContentText(text); mNotificationManager.notify(R.string.uploader_upload_in_progress_ticker, mNotificationBuilder.build()); @@ -758,7 +843,7 @@ public class FileUploader extends Service implements OnDatatransferProgressListe int tickerId = (uploadResult.isSuccess()) ? R.string.uploader_upload_succeeded_ticker : R.string.uploader_upload_failed_ticker; - String content = null; + String content; // check credentials error boolean needsToUpdateCredentials = ( @@ -877,14 +962,33 @@ public class FileUploader extends Service implements OnDatatransferProgressListe /** * Checks if content provider, using the content:// scheme, returns a file with mime-type * 'application/pdf' but file has not extension - * @param localPath - * @param mimeType + * @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 */ - private boolean isPdfFileFromContentProviderWithoutExtension(String localPath, String mimeType) { + private boolean isPdfFileFromContentProviderWithoutExtension(String localPath, + String mimeType) { return localPath.startsWith(UriUtils.URI_CONTENT_SCHEME) && mimeType.equals(MIME_TYPE_PDF) && !localPath.endsWith(FILE_EXTENSION_PDF); } + /** + * Remove uploads of an account + * @param accountName Name of an OC account + */ + 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); + } + } + } + } }