restructuring

added for uploading KEY_WHILE_CHARGING_ONLY
This commit is contained in:
Luke Owncloud 2014-11-27 08:58:53 +01:00
parent d08e4369f7
commit 949c2ae3c0
3 changed files with 174 additions and 64 deletions

View file

@ -113,6 +113,10 @@ public class UploadDbObject implements Serializable {
* Upload only via wifi? * Upload only via wifi?
*/ */
boolean isUseWifiOnly; boolean isUseWifiOnly;
/**
* Upload only if phone being charged?
*/
boolean isWhileChargingOnly;
/** /**
* Name of Owncloud account to upload file to. * Name of Owncloud account to upload file to.
*/ */
@ -270,4 +274,12 @@ public class UploadDbObject implements Serializable {
return AccountUtils.getOwnCloudAccountByName(context, getAccountName()); return AccountUtils.getOwnCloudAccountByName(context, getAccountName());
} }
public void setWhileChargingOnly(boolean isWhileChargingOnly) {
this.isWhileChargingOnly = isWhileChargingOnly;
}
public boolean isWhileChargingOnly() {
return isWhileChargingOnly;
}
} }

View file

@ -78,6 +78,7 @@ import com.owncloud.android.ui.activity.FileActivity;
import com.owncloud.android.ui.activity.FileDisplayActivity; import com.owncloud.android.ui.activity.FileDisplayActivity;
import com.owncloud.android.ui.activity.UploadListActivity; import com.owncloud.android.ui.activity.UploadListActivity;
import com.owncloud.android.utils.ErrorMessageAdapter; import com.owncloud.android.utils.ErrorMessageAdapter;
import com.owncloud.android.utils.UploadUtils;
import com.owncloud.android.utils.UriUtils; import com.owncloud.android.utils.UriUtils;
/** /**
@ -122,6 +123,7 @@ public class FileUploadService extends IntentService {
public static final String KEY_FORCE_OVERWRITE = "KEY_FORCE_OVERWRITE"; public static final String KEY_FORCE_OVERWRITE = "KEY_FORCE_OVERWRITE";
public static final String KEY_CREATE_REMOTE_FOLDER = "CREATE_REMOTE_FOLDER"; public static final String KEY_CREATE_REMOTE_FOLDER = "CREATE_REMOTE_FOLDER";
public static final String KEY_WIFI_ONLY = "WIFI_ONLY"; public static final String KEY_WIFI_ONLY = "WIFI_ONLY";
public static final String KEY_WHILE_CHARGING_ONLY = "KEY_WHILE_CHARGING_ONLY";
public static final String KEY_LOCAL_BEHAVIOUR = "BEHAVIOUR"; public static final String KEY_LOCAL_BEHAVIOUR = "BEHAVIOUR";
/** /**
@ -255,7 +257,7 @@ public class FileUploadService extends IntentService {
mDb.updateUpload(uploadDbObject); mDb.updateUpload(uploadDbObject);
} }
//TODO This service can be instantiated at any time. Move this retry call to start of app. //TODO This service can be instantiated at any time. Better move this retry call to start of app.
if(InstantUploadBroadcastReceiver.isOnline(getApplicationContext())) { if(InstantUploadBroadcastReceiver.isOnline(getApplicationContext())) {
Log_OC.d(TAG, "FileUploadService.retry() called by onCreate()"); Log_OC.d(TAG, "FileUploadService.retry() called by onCreate()");
FileUploadService.retry(getApplicationContext()); FileUploadService.retry(getApplicationContext());
@ -289,24 +291,26 @@ public class FileUploadService extends IntentService {
Log_OC.i(TAG, "mPendingUploads size:" + mPendingUploads.size() + " - before adding new uploads."); Log_OC.i(TAG, "mPendingUploads size:" + mPendingUploads.size() + " - before adding new uploads.");
if (intent == null || intent.hasExtra(KEY_RETRY)) { if (intent == null || intent.hasExtra(KEY_RETRY)) {
Log_OC.d(TAG, "Receive null intent."); Log_OC.d(TAG, "Receive null intent.");
// service was restarted by OS (after return START_STICKY and kill // service was restarted by OS or connectivity change was detected or
// service) or connectivity change was detected. ==> check persistent upload // retry of pending upload was requested.
// list. // ==> First check persistent uploads, then perform upload.
// int countAddedEntries = 0;
List<UploadDbObject> list = mDb.getAllPendingUploads(); List<UploadDbObject> list = mDb.getAllPendingUploads();
for (UploadDbObject uploadDbObject : list) { for (UploadDbObject uploadDbObject : list) {
// store locally. // store locally.
String uploadKey = buildRemoteName(uploadDbObject); String uploadKey = buildRemoteName(uploadDbObject);
UploadDbObject previous = mPendingUploads.putIfAbsent(uploadKey, uploadDbObject); UploadDbObject previous = mPendingUploads.putIfAbsent(uploadKey, uploadDbObject);
if(previous == null) { if(previous == null) {
// and store persistently. // and store persistently.
uploadDbObject.setUploadStatus(UploadStatus.UPLOAD_LATER); uploadDbObject.setUploadStatus(UploadStatus.UPLOAD_LATER);
mDb.updateUpload(uploadDbObject); mDb.updateUpload(uploadDbObject);
countAddedEntries++;
} else { } else {
//upload already pending. ignore. //upload already pending. ignore.
} }
} }
Log_OC.d(TAG, "added " + countAddedEntries
+ " entrie(s) to mPendingUploads (this should be 0 except for the first time).");
} else { } else {
Log_OC.d(TAG, "Receive upload intent."); Log_OC.d(TAG, "Receive upload intent.");
UploadSingleMulti uploadType = (UploadSingleMulti) intent.getSerializableExtra(KEY_UPLOAD_TYPE); UploadSingleMulti uploadType = (UploadSingleMulti) intent.getSerializableExtra(KEY_UPLOAD_TYPE);
@ -372,6 +376,7 @@ public class FileUploadService extends IntentService {
boolean forceOverwrite = intent.getBooleanExtra(KEY_FORCE_OVERWRITE, false); boolean forceOverwrite = intent.getBooleanExtra(KEY_FORCE_OVERWRITE, false);
boolean isCreateRemoteFolder = intent.getBooleanExtra(KEY_CREATE_REMOTE_FOLDER, false); boolean isCreateRemoteFolder = intent.getBooleanExtra(KEY_CREATE_REMOTE_FOLDER, false);
boolean isUseWifiOnly = intent.getBooleanExtra(KEY_WIFI_ONLY, true); boolean isUseWifiOnly = intent.getBooleanExtra(KEY_WIFI_ONLY, true);
boolean isWhileChargingOnly = intent.getBooleanExtra(KEY_WHILE_CHARGING_ONLY, true);
LocalBehaviour localAction = (LocalBehaviour) intent.getSerializableExtra(KEY_LOCAL_BEHAVIOUR); LocalBehaviour localAction = (LocalBehaviour) intent.getSerializableExtra(KEY_LOCAL_BEHAVIOUR);
if (localAction == null) if (localAction == null)
localAction = LocalBehaviour.LOCAL_BEHAVIOUR_COPY; localAction = LocalBehaviour.LOCAL_BEHAVIOUR_COPY;
@ -385,6 +390,7 @@ public class FileUploadService extends IntentService {
uploadObject.setCreateRemoteFolder(isCreateRemoteFolder); uploadObject.setCreateRemoteFolder(isCreateRemoteFolder);
uploadObject.setLocalAction(localAction); uploadObject.setLocalAction(localAction);
uploadObject.setUseWifiOnly(isUseWifiOnly); uploadObject.setUseWifiOnly(isUseWifiOnly);
uploadObject.setWhileChargingOnly(isWhileChargingOnly);
uploadObject.setUploadStatus(UploadStatus.UPLOAD_LATER); uploadObject.setUploadStatus(UploadStatus.UPLOAD_LATER);
String uploadKey = buildRemoteName(uploadObject); String uploadKey = buildRemoteName(uploadObject);
@ -402,27 +408,46 @@ public class FileUploadService extends IntentService {
// upload already pending. ignore. // upload already pending. ignore.
} }
} }
// TODO check if would be clever to read entries from
// UploadDbHandler and add to requestedUploads at this point
} }
// at this point mPendingUploads is filled. // at this point mPendingUploads is filled.
Log_OC.i(TAG, "mPendingUploads size:" + mPendingUploads.size() + " - before uploading."); Log_OC.i(TAG, "mPendingUploads size:" + mPendingUploads.size() + " - before uploading.");
try {
Iterator<String> it = mPendingUploads.keySet().iterator(); Iterator<String> it = mPendingUploads.keySet().iterator();
while (it.hasNext()) { while (it.hasNext()) {
String up = it.next(); String upload = it.next();
Log_OC.d(TAG, "Calling uploadFile for " + up); UploadDbObject uploadDbObject = mPendingUploads.get(upload);
UploadDbObject uploadDbObject = mPendingUploads.get(up);
boolean uploadSuccessful = uploadFile(uploadDbObject); switch (canUploadFileNow(uploadDbObject)) {
case NOW:
Log_OC.d(TAG, "Calling uploadFile for " + upload);
RemoteOperationResult uploadResult = uploadFile(uploadDbObject);
updateDataseUploadResult(uploadResult, mCurrentUpload);
notifyUploadResult(uploadResult, mCurrentUpload);
sendFinalBroadcast(uploadResult, mCurrentUpload);
if (!shouldRetryFailedUpload(uploadResult)) {
Log_OC.d(TAG, "Upload with result " + uploadResult.getCode() + ": " + uploadResult.getLogMessage()
+ " will be abandoned.");
mPendingUploads.remove(buildRemoteName(uploadDbObject));
} }
} catch (ConcurrentModificationException e) { mCurrentUpload = null;
// for now: ignore. TODO: fix this. break;
case LATER:
//TODO schedule retry for later upload.
break;
case FILE_GONE:
mDb.updateUpload(uploadDbObject.getLocalPath(), UploadStatus.UPLOAD_FAILED_GIVE_UP,
new RemoteOperationResult(ResultCode.FILE_NOT_FOUND));
it.remove();
break;
case ERROR:
Log_OC.e(TAG, "canUploadFileNow() returned ERROR. Fix that!");
break;
} }
}
Log_OC.i(TAG, "mPendingUploads size:" + mPendingUploads.size() + " - after uploading."); Log_OC.i(TAG, "mPendingUploads size:" + mPendingUploads.size() + " - after uploading.");
Log_OC.i(TAG, "onHandleIntent end"); Log_OC.i(TAG, "onHandleIntent end");
} }
@ -564,38 +589,67 @@ public class FileUploadService extends IntentService {
} }
} }
enum CanUploadFileNowStatus {NOW, LATER, FILE_GONE, ERROR};
/** /**
* Core upload method: sends the file(s) to upload. This function blocks * Returns true when the file may be uploaded now. This methods checks all
* until upload succeeded or failed. * restraints of the passed {@link UploadDbObject}, these include
* isUseWifiOnly(), check if local file exists, check if file was already
* uploaded...
*
* If return value is CanUploadFileNowStatus.NOW, uploadFile() may be
* called.
*
* @return CanUploadFileNowStatus.NOW is upload may proceed, <br>
* CanUploadFileNowStatus.LATER if upload should be performed at a
* later time, <br>
* CanUploadFileNowStatus.ERROR if a severe error happened, calling
* entity should remove upload from queue.
* *
* @param uploadDbObject Key to access the upload to perform, contained in
* mPendingUploads
* @return true on success.
*/ */
private boolean uploadFile(UploadDbObject uploadDbObject) { private CanUploadFileNowStatus canUploadFileNow(UploadDbObject uploadDbObject) {
if (uploadDbObject.getUploadStatus() == UploadStatus.UPLOAD_SUCCEEDED) { if (uploadDbObject.getUploadStatus() == UploadStatus.UPLOAD_SUCCEEDED) {
Log_OC.w(TAG, "Already succeeded uploadObject was again scheduled for upload. Fix that!"); Log_OC.w(TAG, "Already succeeded uploadObject was again scheduled for upload. Fix that!");
return false; return CanUploadFileNowStatus.ERROR;
}
if (mCurrentUpload != null) {
Log_OC.e(TAG,
"mCurrentUpload != null. Meaning there is another upload in progress, cannot start a new one. Fix that!");
return false;
} }
if (uploadDbObject.isUseWifiOnly() if (uploadDbObject.isUseWifiOnly()
&& !InstantUploadBroadcastReceiver.isConnectedViaWiFi(getApplicationContext())) { && !InstantUploadBroadcastReceiver.isConnectedViaWiFi(getApplicationContext())) {
Log_OC.d(TAG, "Do not start upload because it is wifi-only."); Log_OC.d(TAG, "Do not start upload because it is wifi-only.");
return false; return CanUploadFileNowStatus.LATER;
}
if(uploadDbObject.isWhileChargingOnly() && !UploadUtils.isCharging(getApplicationContext())) {
Log_OC.d(TAG, "Do not start upload because it is while charging only.");
return CanUploadFileNowStatus.LATER;
} }
if (!new File(uploadDbObject.getLocalPath()).exists()) { if (!new File(uploadDbObject.getLocalPath()).exists()) {
mDb.updateUpload(uploadDbObject.getLocalPath(), UploadStatus.UPLOAD_FAILED_GIVE_UP,
new RemoteOperationResult(ResultCode.FILE_NOT_FOUND));
Log_OC.d(TAG, "Do not start upload because local file does not exist."); Log_OC.d(TAG, "Do not start upload because local file does not exist.");
return false; return CanUploadFileNowStatus.FILE_GONE;
}
return CanUploadFileNowStatus.NOW;
}
/**
* Core upload method: sends the file(s) to upload. This function blocks
* until upload succeeded or failed.
*
* Before uploading starts mCurrentUpload is set.
*
* @param uploadDbObject Key to access the upload to perform, contained in
* mPendingUploads
* @return RemoteOperationResult of upload operation.
*/
private RemoteOperationResult uploadFile(UploadDbObject uploadDbObject) {
if (mCurrentUpload != null) {
Log_OC.e(TAG,
"mCurrentUpload != null. Meaning there is another upload in progress, cannot start a new one. Fix that!");
return new RemoteOperationResult(new IllegalStateException("mCurrentUpload != null when calling uploadFile()"));
} }
AccountManager aMgr = AccountManager.get(this); AccountManager aMgr = AccountManager.get(this);
@ -656,7 +710,6 @@ public class FileUploadService extends IntentService {
uploadResult = new RemoteOperationResult(e); uploadResult = new RemoteOperationResult(e);
} finally { } finally {
mPendingUploads.remove(buildRemoteName(uploadDbObject));
Log_OC.i(TAG, "Remove CurrentUploadItem from pending upload Item Map."); Log_OC.i(TAG, "Remove CurrentUploadItem from pending upload Item Map.");
if (uploadResult.isException()) { if (uploadResult.isException()) {
// enforce the creation of a new client object for next // enforce the creation of a new client object for next
@ -666,14 +719,7 @@ public class FileUploadService extends IntentService {
mUploadClient = null; mUploadClient = null;
} }
} }
return uploadResult;
// notify result
notifyUploadResult(uploadResult, mCurrentUpload);
sendFinalBroadcast(mCurrentUpload, uploadResult);
mCurrentUpload = null;
return uploadResult.isSuccess();
} }
@ -846,6 +892,13 @@ public class FileUploadService extends IntentService {
mNotificationManager.notify(R.string.uploader_upload_in_progress_ticker, mNotificationBuilder.build()); mNotificationManager.notify(R.string.uploader_upload_in_progress_ticker, mNotificationBuilder.build());
updateDatabaseUploadStart(mCurrentUpload);
}
/**
* Updates the persistent upload database that upload is in progress.
*/
private void updateDatabaseUploadStart(UploadFileOperation upload) {
mDb.updateUpload(upload.getOriginalStoragePath(), UploadStatus.UPLOAD_IN_PROGRESS, null); mDb.updateUpload(upload.getOriginalStoragePath(), UploadStatus.UPLOAD_IN_PROGRESS, null);
} }
@ -910,34 +963,61 @@ public class FileUploadService extends IntentService {
if (uploadResult.isSuccess()) { if (uploadResult.isSuccess()) {
mDb.updateUpload(upload.getOriginalStoragePath(), UploadStatus.UPLOAD_SUCCEEDED, uploadResult);
// remove success notification, with a delay of 2 seconds // remove success notification, with a delay of 2 seconds
NotificationDelayer.cancelWithDelay(mNotificationManager, R.string.uploader_upload_succeeded_ticker, NotificationDelayer.cancelWithDelay(mNotificationManager, R.string.uploader_upload_succeeded_ticker,
2000); 2000);
}
}
}
/**
* Updates the persistent upload database with upload result.
*/
private void updateDataseUploadResult(RemoteOperationResult uploadResult, UploadFileOperation upload) {
// result: success or fail notification
if (uploadResult.isCancelled()) {
mDb.updateUpload(upload.getOriginalStoragePath(), UploadStatus.UPLOAD_CANCELLED, uploadResult);
} else {
if (uploadResult.isSuccess()) {
mDb.updateUpload(upload.getOriginalStoragePath(), UploadStatus.UPLOAD_SUCCEEDED, uploadResult);
} else {
if (shouldRetryFailedUpload(uploadResult)) {
mDb.updateUpload(upload.getOriginalStoragePath(), UploadStatus.UPLOAD_FAILED_RETRY, uploadResult);
} else { } else {
// TODO: add other cases in which upload attempt is to be
// abandoned.
if (uploadResult.getCode() == ResultCode.QUOTA_EXCEEDED) {
mDb.updateUpload(upload.getOriginalStoragePath(), mDb.updateUpload(upload.getOriginalStoragePath(),
UploadDbHandler.UploadStatus.UPLOAD_FAILED_GIVE_UP, uploadResult); UploadDbHandler.UploadStatus.UPLOAD_FAILED_GIVE_UP, uploadResult);
} else {
mDb.updateUpload(upload.getOriginalStoragePath(), UploadStatus.UPLOAD_FAILED_RETRY, uploadResult);
} }
} }
} else { }
mDb.updateUpload(upload.getOriginalStoragePath(), UploadStatus.UPLOAD_FAILED_RETRY, uploadResult); }
/**
* Determines whether with given uploadResult the upload should be retried later.
* @param uploadResult
* @return true if upload should be retried later, false if is should be abandoned.
*/
private boolean shouldRetryFailedUpload(RemoteOperationResult uploadResult) {
if (uploadResult.isSuccess()) {
return false;
}
switch (uploadResult.getCode()) {
case HOST_NOT_AVAILABLE:
case NO_NETWORK_CONNECTION:
case TIMEOUT:
return true;
default:
return false;
} }
} }
/** /**
* Sends a broadcast in order to the interested activities can update their * Sends a broadcast in order to the interested activities can update their
* view * view
*
* @param upload Finished upload operation
* @param uploadResult Result of the upload operation * @param uploadResult Result of the upload operation
* @param upload Finished upload operation
*/ */
private void sendFinalBroadcast(UploadFileOperation upload, RemoteOperationResult uploadResult) { private void sendFinalBroadcast(RemoteOperationResult uploadResult, UploadFileOperation upload) {
Intent end = new Intent(getUploadFinishMessage()); Intent end = new Intent(getUploadFinishMessage());
end.putExtra(EXTRA_REMOTE_PATH, upload.getRemotePath()); // real remote end.putExtra(EXTRA_REMOTE_PATH, upload.getRemotePath()); // real remote
// path, after // path, after

View file

@ -0,0 +1,18 @@
package com.owncloud.android.utils;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
public class UploadUtils {
public static boolean isCharging(Context context) {
IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus = context.registerReceiver(null, ifilter);
int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
return status == BatteryManager.BATTERY_STATUS_CHARGING || status == BatteryManager.BATTERY_STATUS_FULL;
}
}