mirror of
https://github.com/nextcloud/android.git
synced 2024-11-26 15:15:51 +03:00
Delaying upload if device is in power save mode
This change adds handling for uploads from device when it is operating in power save mode and is a fix for #1277. In those case, uploads are behaving similar as if upload requires Wi-Fi (or that device needs to be charging) and those conditions are not met - they are delayed until device exits power save mode. Same thing holds true for contacts backup and explicit uploads - they are not delayed. Difference with Wi-Fi (and charging) conditions is in that 1) this delay cannot be set per synced folder and 2) it is turned on by default. For devices that don't have this capability (API level < 21) - old behavior (backward-compatible) is used; they behave as this check is not present. Jobs for file uploads are skipped if they are executing during power save mode. Only exception to this is when user is doing "force resync" which will continue even in power save mode. Also added is listener for power save mode, albeit it makes sense only if user explicitly turns off power save mode while it is in it, as putting device to be charged will restart all jobs already by design (another listener that already exists).
This commit is contained in:
parent
9e7e2c11c6
commit
8bc432027e
12 changed files with 140 additions and 44 deletions
|
@ -194,6 +194,10 @@ public class MainApp extends MultiDexApplication {
|
|||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
ReceiversHelper.registerPowerChangeReceiver();
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
ReceiversHelper.registerPowerSaveReceiver();
|
||||
}
|
||||
}
|
||||
|
||||
public static Context getAppContext() {
|
||||
|
|
|
@ -383,6 +383,8 @@ public class UploadsStorageManager extends Observable {
|
|||
"==" + UploadResult.LOCK_FAILED.getValue() +
|
||||
" OR " + ProviderTableMeta.UPLOADS_LAST_RESULT +
|
||||
"==" + UploadResult.DELAYED_FOR_CHARGING.getValue() +
|
||||
" OR " + ProviderTableMeta.UPLOADS_LAST_RESULT +
|
||||
"==" + UploadResult.DELAYED_IN_POWER_SAVE_MODE.getValue() +
|
||||
" AND " + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "== ?",
|
||||
new String[]{account.name}
|
||||
);
|
||||
|
@ -424,12 +426,14 @@ public class UploadsStorageManager extends Observable {
|
|||
|
||||
if (account != null) {
|
||||
return getUploads(ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_FAILED.value +
|
||||
AND +
|
||||
ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_WIFI.getValue() +
|
||||
AND +
|
||||
ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.LOCK_FAILED.getValue() +
|
||||
AND +
|
||||
ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_CHARGING.getValue() +
|
||||
AND + ProviderTableMeta.UPLOADS_LAST_RESULT +
|
||||
"<>" + UploadResult.DELAYED_FOR_WIFI.getValue() +
|
||||
AND + ProviderTableMeta.UPLOADS_LAST_RESULT +
|
||||
"<>" + UploadResult.LOCK_FAILED.getValue() +
|
||||
AND + ProviderTableMeta.UPLOADS_LAST_RESULT +
|
||||
"<>" + UploadResult.DELAYED_FOR_CHARGING.getValue() +
|
||||
AND + ProviderTableMeta.UPLOADS_LAST_RESULT +
|
||||
"<>" + UploadResult.DELAYED_IN_POWER_SAVE_MODE.getValue() +
|
||||
AND + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "== ?",
|
||||
new String[]{account.name}
|
||||
);
|
||||
|
@ -446,9 +450,13 @@ public class UploadsStorageManager extends Observable {
|
|||
public OCUpload[] getFailedButNotDelayedUploads() {
|
||||
|
||||
return getUploads(ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_FAILED.value + AND +
|
||||
ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.LOCK_FAILED.getValue() + AND +
|
||||
ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_WIFI.getValue() + AND +
|
||||
ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_CHARGING.getValue(),
|
||||
ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.LOCK_FAILED.getValue() +
|
||||
AND + ProviderTableMeta.UPLOADS_LAST_RESULT +
|
||||
"<>" + UploadResult.DELAYED_FOR_WIFI.getValue() +
|
||||
AND + ProviderTableMeta.UPLOADS_LAST_RESULT +
|
||||
"<>" + UploadResult.DELAYED_FOR_CHARGING.getValue() +
|
||||
AND + ProviderTableMeta.UPLOADS_LAST_RESULT +
|
||||
"<>" + UploadResult.DELAYED_IN_POWER_SAVE_MODE.getValue(),
|
||||
null
|
||||
);
|
||||
}
|
||||
|
@ -465,14 +473,15 @@ public class UploadsStorageManager extends Observable {
|
|||
result = getDB().delete(
|
||||
ProviderTableMeta.CONTENT_URI_UPLOADS,
|
||||
ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_FAILED.value +
|
||||
AND +
|
||||
ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.LOCK_FAILED.getValue() +
|
||||
AND +
|
||||
ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_WIFI.getValue() +
|
||||
AND +
|
||||
ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_CHARGING.getValue() +
|
||||
AND +
|
||||
ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "== ?",
|
||||
AND + ProviderTableMeta.UPLOADS_LAST_RESULT +
|
||||
"<>" + UploadResult.LOCK_FAILED.getValue() +
|
||||
AND + ProviderTableMeta.UPLOADS_LAST_RESULT +
|
||||
"<>" + UploadResult.DELAYED_FOR_WIFI.getValue() +
|
||||
AND + ProviderTableMeta.UPLOADS_LAST_RESULT +
|
||||
"<>" + UploadResult.DELAYED_FOR_CHARGING.getValue() +
|
||||
AND + ProviderTableMeta.UPLOADS_LAST_RESULT +
|
||||
"<>" + UploadResult.DELAYED_IN_POWER_SAVE_MODE.getValue() +
|
||||
AND + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "== ?",
|
||||
new String[]{account.name}
|
||||
);
|
||||
}
|
||||
|
@ -512,17 +521,19 @@ public class UploadsStorageManager extends Observable {
|
|||
whereArgs[0] = String.valueOf(UploadStatus.UPLOAD_SUCCEEDED.value);
|
||||
whereArgs[1] = String.valueOf(UploadStatus.UPLOAD_FAILED.value);
|
||||
whereArgs[2] = account.name;
|
||||
result = getDB().delete(
|
||||
long result = getDB().delete(
|
||||
ProviderTableMeta.CONTENT_URI_UPLOADS,
|
||||
ProviderTableMeta.UPLOADS_STATUS + "=? OR " + ProviderTableMeta.UPLOADS_STATUS + "=?" +
|
||||
AND +
|
||||
ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.LOCK_FAILED.getValue() +
|
||||
AND +
|
||||
ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_WIFI.getValue() +
|
||||
AND +
|
||||
ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_CHARGING.getValue() +
|
||||
AND +
|
||||
ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "== ?", whereArgs
|
||||
AND + ProviderTableMeta.UPLOADS_LAST_RESULT +
|
||||
"<>" + UploadResult.LOCK_FAILED.getValue() +
|
||||
AND + ProviderTableMeta.UPLOADS_LAST_RESULT +
|
||||
"<>" + UploadResult.DELAYED_FOR_WIFI.getValue() +
|
||||
AND + ProviderTableMeta.UPLOADS_LAST_RESULT +
|
||||
"<>" + UploadResult.DELAYED_FOR_CHARGING.getValue() +
|
||||
AND + ProviderTableMeta.UPLOADS_LAST_RESULT +
|
||||
"<>" + UploadResult.DELAYED_IN_POWER_SAVE_MODE.getValue() +
|
||||
AND + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "== ?",
|
||||
whereArgs
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,8 @@ public enum UploadResult {
|
|||
SERVICE_INTERRUPTED(10),
|
||||
DELAYED_FOR_CHARGING(11),
|
||||
MAINTENANCE_MODE(12),
|
||||
LOCK_FAILED(13);
|
||||
LOCK_FAILED(13),
|
||||
DELAYED_IN_POWER_SAVE_MODE(14);
|
||||
|
||||
private final int value;
|
||||
|
||||
|
@ -80,6 +81,8 @@ public enum UploadResult {
|
|||
return MAINTENANCE_MODE;
|
||||
case 13:
|
||||
return LOCK_FAILED;
|
||||
case 14:
|
||||
return DELAYED_IN_POWER_SAVE_MODE;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -116,6 +119,8 @@ public enum UploadResult {
|
|||
return DELAYED_FOR_WIFI;
|
||||
case DELAYED_FOR_CHARGING:
|
||||
return DELAYED_FOR_CHARGING;
|
||||
case DELAYED_IN_POWER_SAVE_MODE:
|
||||
return DELAYED_IN_POWER_SAVE_MODE;
|
||||
case UNKNOWN_ERROR:
|
||||
if (result.getException() instanceof java.io.FileNotFoundException) {
|
||||
return FILE_ERROR;
|
||||
|
|
|
@ -73,6 +73,7 @@ import com.owncloud.android.ui.activity.UploadListActivity;
|
|||
import com.owncloud.android.ui.notifications.NotificationUtils;
|
||||
import com.owncloud.android.utils.ErrorMessageAdapter;
|
||||
import com.owncloud.android.utils.ThemeUtils;
|
||||
import com.owncloud.android.utils.UploadUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.AbstractList;
|
||||
|
@ -924,6 +925,11 @@ public class FileUploader extends Service
|
|||
!Device.isCharging(MainApp.getAppContext())) {
|
||||
cancel(mCurrentUpload.getAccount().name, mCurrentUpload.getFile().getRemotePath()
|
||||
, ResultCode.DELAYED_FOR_CHARGING);
|
||||
} else if (
|
||||
!mCurrentUpload.getIsIgnoringPowerSaveMode() &&
|
||||
UploadUtils.isPowerSaveMode(MainApp.getAppContext())) {
|
||||
cancel(mCurrentUpload.getAccount().name, mCurrentUpload.getFile().getRemotePath()
|
||||
, ResultCode.DELAYED_IN_POWER_SAVE_MODE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1142,6 +1148,7 @@ public class FileUploader extends Service
|
|||
!ResultCode.LOCAL_FILE_NOT_FOUND.equals(uploadResult.getCode()) &&
|
||||
!uploadResult.getCode().equals(ResultCode.DELAYED_FOR_WIFI) &&
|
||||
!uploadResult.getCode().equals(ResultCode.DELAYED_FOR_CHARGING) &&
|
||||
!uploadResult.getCode().equals(ResultCode.DELAYED_IN_POWER_SAVE_MODE) &&
|
||||
!uploadResult.getCode().equals(ResultCode.LOCK_FAILED) ) {
|
||||
|
||||
int tickerId = (uploadResult.isSuccess()) ? R.string.uploader_upload_succeeded_ticker :
|
||||
|
|
|
@ -47,6 +47,7 @@ import com.owncloud.android.ui.activity.Preferences;
|
|||
import com.owncloud.android.utils.FileStorageUtils;
|
||||
import com.owncloud.android.utils.FilesSyncHelper;
|
||||
import com.owncloud.android.utils.MimeTypeUtil;
|
||||
import com.owncloud.android.utils.UploadUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
@ -66,6 +67,7 @@ public class FilesSyncJob extends Job {
|
|||
public static final String TAG = "FilesSyncJob";
|
||||
|
||||
public static final String SKIP_CUSTOM = "skipCustom";
|
||||
public static final String OVERRIDE_POWER_SAVING = "overridePowerSaving";
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
|
@ -81,6 +83,12 @@ public class FilesSyncJob extends Job {
|
|||
|
||||
PersistableBundleCompat bundle = params.getExtras();
|
||||
final boolean skipCustom = bundle.getBoolean(SKIP_CUSTOM, false);
|
||||
final boolean overridePowerSaving = bundle.getBoolean(OVERRIDE_POWER_SAVING, false);
|
||||
|
||||
// If we are in power save mode, better to postpone upload
|
||||
if (UploadUtils.isPowerSaveMode(context) && !overridePowerSaving) {
|
||||
return Result.SUCCESS;
|
||||
}
|
||||
|
||||
Resources resources = MainApp.getAppContext().getResources();
|
||||
boolean lightVersion = resources.getBoolean(R.bool.syncedFolder_light);
|
||||
|
|
|
@ -29,6 +29,7 @@ import android.support.annotation.RequiresApi;
|
|||
import com.evernote.android.job.JobRequest;
|
||||
import com.evernote.android.job.util.support.PersistableBundleCompat;
|
||||
import com.owncloud.android.utils.FilesSyncHelper;
|
||||
import com.owncloud.android.utils.UploadUtils;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
@ -43,7 +44,8 @@ public class NContentObserverJob extends JobService {
|
|||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
if (params.getJobId() == FilesSyncHelper.ContentSyncJobId && params.getTriggeredContentAuthorities()
|
||||
!= null && params.getTriggeredContentUris() != null
|
||||
&& params.getTriggeredContentUris().length > 0) {
|
||||
&& params.getTriggeredContentUris().length > 0
|
||||
&& !UploadUtils.isPowerSaveMode(getApplicationContext())) {
|
||||
|
||||
PersistableBundleCompat persistableBundleCompat = new PersistableBundleCompat();
|
||||
persistableBundleCompat.putBoolean(FilesSyncJob.SKIP_CUSTOM, true);
|
||||
|
@ -51,6 +53,7 @@ public class NContentObserverJob extends JobService {
|
|||
new JobRequest.Builder(FilesSyncJob.TAG)
|
||||
.setExecutionWindow(1, TimeUnit.SECONDS.toMillis(2))
|
||||
.setBackoffCriteria(TimeUnit.SECONDS.toMillis(5), JobRequest.BackoffPolicy.LINEAR)
|
||||
.setExtras(persistableBundleCompat)
|
||||
.setUpdateCurrent(false)
|
||||
.build()
|
||||
.schedule();
|
||||
|
|
|
@ -48,6 +48,7 @@ import com.owncloud.android.operations.common.SyncOperation;
|
|||
import com.owncloud.android.utils.FileStorageUtils;
|
||||
import com.owncloud.android.utils.MimeType;
|
||||
import com.owncloud.android.utils.MimeTypeUtil;
|
||||
import com.owncloud.android.utils.UploadUtils;
|
||||
import com.owncloud.android.utils.UriUtils;
|
||||
|
||||
import org.apache.commons.httpclient.HttpStatus;
|
||||
|
@ -102,6 +103,7 @@ public class UploadFileOperation extends SyncOperation {
|
|||
private int mCreatedBy = CREATED_BY_USER;
|
||||
private boolean mOnWifiOnly = false;
|
||||
private boolean mWhileChargingOnly = false;
|
||||
private boolean mIgnoringPowerSaveMode = false;
|
||||
|
||||
private boolean mWasRenamed = false;
|
||||
private long mOCUploadId = -1;
|
||||
|
@ -192,6 +194,8 @@ public class UploadFileOperation extends SyncOperation {
|
|||
mOCUploadId = upload.getUploadId();
|
||||
mCreatedBy = upload.getCreadtedBy();
|
||||
mRemoteFolderToBeCreated = upload.isCreateRemoteFolder();
|
||||
// Ignore power save mode only if user explicitly created this upload
|
||||
mIgnoringPowerSaveMode = (mCreatedBy == CREATED_BY_USER);
|
||||
}
|
||||
|
||||
public boolean getIsWifiRequired() {
|
||||
|
@ -202,6 +206,8 @@ public class UploadFileOperation extends SyncOperation {
|
|||
return mWhileChargingOnly;
|
||||
}
|
||||
|
||||
public boolean getIsIgnoringPowerSaveMode() { return mIgnoringPowerSaveMode; }
|
||||
|
||||
public Account getAccount() {
|
||||
return mAccount;
|
||||
}
|
||||
|
@ -347,6 +353,12 @@ public class UploadFileOperation extends SyncOperation {
|
|||
return new RemoteOperationResult(ResultCode.DELAYED_FOR_CHARGING);
|
||||
}
|
||||
|
||||
// Check that device is not in power save mode
|
||||
if (!mIgnoringPowerSaveMode && UploadUtils.isPowerSaveMode(mContext)) {
|
||||
Log_OC.d(TAG, "Upload delayed because device is in power save mode: " + getRemotePath());
|
||||
return new RemoteOperationResult(ResultCode.DELAYED_IN_POWER_SAVE_MODE);
|
||||
}
|
||||
|
||||
/// check if the file continues existing before schedule the operation
|
||||
if (!originalFile.exists()) {
|
||||
Log_OC.d(TAG, mOriginalStoragePath + " not exists anymore");
|
||||
|
|
|
@ -43,6 +43,7 @@ import android.view.View;
|
|||
import android.widget.Toast;
|
||||
|
||||
import com.evernote.android.job.JobRequest;
|
||||
import com.evernote.android.job.util.support.PersistableBundleCompat;
|
||||
import com.owncloud.android.R;
|
||||
import com.owncloud.android.datamodel.UploadsStorageManager;
|
||||
import com.owncloud.android.db.OCUpload;
|
||||
|
@ -237,9 +238,12 @@ public class UploadListActivity extends FileActivity implements UploadListFragme
|
|||
break;
|
||||
|
||||
case R.id.action_force_rescan:
|
||||
PersistableBundleCompat persistableBundleCompat = new PersistableBundleCompat();
|
||||
persistableBundleCompat.putBoolean(FilesSyncJob.OVERRIDE_POWER_SAVING, true);
|
||||
new JobRequest.Builder(FilesSyncJob.TAG)
|
||||
.setExact(1_000L)
|
||||
.setUpdateCurrent(false)
|
||||
.setExtras(persistableBundleCompat)
|
||||
.build()
|
||||
.schedule();
|
||||
|
||||
|
|
|
@ -571,6 +571,10 @@ public class ExpandableUploadListAdapter extends BaseExpandableListAdapter imple
|
|||
case LOCK_FAILED:
|
||||
status = mParentActivity.getString(R.string.lock_failed);
|
||||
break;
|
||||
case DELAYED_IN_POWER_SAVE_MODE:
|
||||
status = mParentActivity.getString(
|
||||
R.string.uploads_view_upload_status_waiting_exit_power_save_mode);
|
||||
break;
|
||||
default:
|
||||
status = "Naughty devs added a new fail result but no description for the user";
|
||||
break;
|
||||
|
|
|
@ -71,4 +71,22 @@ public class ReceiversHelper {
|
|||
|
||||
context.registerReceiver(broadcastReceiver, intentFilter);
|
||||
}
|
||||
|
||||
public static void registerPowerSaveReceiver() {
|
||||
Context context = MainApp.getAppContext();
|
||||
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction("android.os.action.POWER_SAVE_MODE_CHANGED");
|
||||
|
||||
BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (!UploadUtils.isPowerSaveMode(context)) {
|
||||
FilesSyncHelper.restartJobsIfNeeded();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
context.registerReceiver(broadcastReceiver, intentFilter);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,12 +19,15 @@
|
|||
|
||||
package com.owncloud.android.utils;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo.State;
|
||||
import android.os.BatteryManager;
|
||||
import android.os.Build;
|
||||
import android.os.PowerManager;
|
||||
|
||||
|
||||
public class UploadUtils {
|
||||
|
@ -49,4 +52,20 @@ public class UploadUtils {
|
|||
&& cm.getActiveNetworkInfo().getState() == State.CONNECTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if device is in power save mode. For older devices that do not support this API, returns false.
|
||||
* @return true if it is, false otherwise.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
public static boolean isPowerSaveMode(Context context)
|
||||
{
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
|
||||
{
|
||||
PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
|
||||
return powerManager.isPowerSaveMode();
|
||||
}
|
||||
|
||||
// For older versions, we just say that device is not in power save mode
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -172,6 +172,7 @@
|
|||
<string name="uploads_view_upload_status_service_interrupted">App terminated</string>
|
||||
<string name="uploads_view_upload_status_unknown_fail">Unknown error</string>
|
||||
<string name="uploads_view_upload_status_waiting_for_wifi">Waiting for Wi-Fi connectivity</string>
|
||||
<string name="uploads_view_upload_status_waiting_exit_power_save_mode">Waiting to exit power save mode</string>
|
||||
<string name="uploads_view_later_waiting_to_upload">Waiting to upload</string>
|
||||
<string name="uploads_view_group_header" translatable="false">%1$s (%2$d)</string>
|
||||
<string name="downloader_download_in_progress_ticker">Downloading …</string>
|
||||
|
|
Loading…
Reference in a new issue