diff --git a/build.gradle b/build.gradle index 0e155a0e04..d528855513 100644 --- a/build.gradle +++ b/build.gradle @@ -35,7 +35,6 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-kapt' - apply plugin: 'checkstyle' apply plugin: 'pmd' apply plugin: 'jacoco-android' @@ -263,7 +262,6 @@ dependencies { implementation 'com.github.tobiaskaminsky:android-job:v1.2.6.1' // 'com.github.evernote:android-job:v1.2.5' implementation 'com.jakewharton:butterknife:10.1.0' kapt 'com.jakewharton:butterknife-compiler:10.1.0' - implementation 'org.greenrobot:eventbus:3.1.1' implementation 'com.googlecode.ez-vcard:ez-vcard:0.10.5' implementation 'org.lukhnos:nnio:0.2' diff --git a/src/androidTest/java/com/owncloud/android/UploadIT.java b/src/androidTest/java/com/owncloud/android/UploadIT.java index 9e5eb6db20..cc901f551a 100644 --- a/src/androidTest/java/com/owncloud/android/UploadIT.java +++ b/src/androidTest/java/com/owncloud/android/UploadIT.java @@ -4,6 +4,7 @@ import android.content.ContentResolver; import com.evernote.android.job.JobRequest; import com.nextcloud.client.account.CurrentAccountProvider; +import com.nextcloud.client.device.PowerManagementService; import com.nextcloud.client.network.ConnectivityService; import com.owncloud.android.authentication.AccountUtils; import com.owncloud.android.datamodel.UploadsStorageManager; @@ -48,6 +49,23 @@ public class UploadIT extends AbstractIT { } }; + private PowerManagementService powerManagementServiceMock = new PowerManagementService() { + @Override + public boolean isPowerSavingEnabled() { + return false; + } + + @Override + public boolean isPowerSavingExclusionAvailable() { + return false; + } + + @Override + public boolean isBatteryCharging() { + return false; + } + }; + @Before public void setUp() { final ContentResolver contentResolver = targetContext.getContentResolver(); @@ -98,6 +116,7 @@ public class UploadIT extends AbstractIT { UploadFileOperation newUpload = new UploadFileOperation( storageManager, connectivityServiceMock, + powerManagementServiceMock, account, null, ocUpload, @@ -123,6 +142,7 @@ public class UploadIT extends AbstractIT { UploadFileOperation newUpload = new UploadFileOperation( storageManager, connectivityServiceMock, + powerManagementServiceMock, account, null, ocUpload, diff --git a/src/main/java/com/nextcloud/client/di/ComponentsModule.java b/src/main/java/com/nextcloud/client/di/ComponentsModule.java index ab5828a415..b7ec94797f 100644 --- a/src/main/java/com/nextcloud/client/di/ComponentsModule.java +++ b/src/main/java/com/nextcloud/client/di/ComponentsModule.java @@ -26,6 +26,7 @@ import com.owncloud.android.authentication.DeepLinkLoginActivity; import com.owncloud.android.files.BootupBroadcastReceiver; import com.owncloud.android.files.services.FileDownloader; import com.owncloud.android.files.services.FileUploader; +import com.owncloud.android.jobs.NContentObserverJob; import com.owncloud.android.jobs.NotificationJob; import com.owncloud.android.providers.DiskLruImageCacheFileProvider; import com.owncloud.android.providers.DocumentsStorageProvider; @@ -147,4 +148,5 @@ abstract class ComponentsModule { @ContributesAndroidInjector abstract AccountManagerService accountManagerService(); @ContributesAndroidInjector abstract OperationsService operationsService(); + @ContributesAndroidInjector abstract NContentObserverJob contentObserverJob(); } diff --git a/src/main/java/com/owncloud/android/MainApp.java b/src/main/java/com/owncloud/android/MainApp.java index 7cee61103b..75d40de8bc 100644 --- a/src/main/java/com/owncloud/android/MainApp.java +++ b/src/main/java/com/owncloud/android/MainApp.java @@ -46,6 +46,7 @@ import com.evernote.android.job.JobManager; import com.evernote.android.job.JobRequest; import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.appinfo.AppInfo; +import com.nextcloud.client.device.PowerManagementService; import com.nextcloud.client.di.ActivityInjector; import com.nextcloud.client.di.DaggerAppComponent; import com.nextcloud.client.network.ConnectivityService; @@ -106,9 +107,8 @@ import static com.owncloud.android.ui.activity.ContactsPreferenceActivity.PREFER /** * Main Application of the project - * - * Contains methods to build the "static" strings. These strings were before constants in different - * classes + *

+ * Contains methods to build the "static" strings. These strings were before constants in different classes */ public class MainApp extends MultiDexApplication implements HasActivityInjector, @@ -161,6 +161,8 @@ public class MainApp extends MultiDexApplication implements @Inject ConnectivityService connectivityService; + @Inject PowerManagementService powerManagementService; + private PassCodeManager passCodeManager; @SuppressWarnings("unused") @@ -202,7 +204,8 @@ public class MainApp extends MultiDexApplication implements accountManager, preferences, uploadsStorageManager, - connectivityService + connectivityService, + powerManagementService ) ); @@ -235,22 +238,22 @@ public class MainApp extends MultiDexApplication implements } } - initSyncOperations(uploadsStorageManager, accountManager, connectivityService); + initSyncOperations(uploadsStorageManager, accountManager, connectivityService, powerManagementService); initContactsBackup(accountManager); notificationChannels(); new JobRequest.Builder(MediaFoldersDetectionJob.TAG) - .setPeriodic(TimeUnit.MINUTES.toMillis(15), TimeUnit.MINUTES.toMillis(5)) - .setUpdateCurrent(true) - .build() - .schedule(); + .setPeriodic(TimeUnit.MINUTES.toMillis(15), TimeUnit.MINUTES.toMillis(5)) + .setUpdateCurrent(true) + .build() + .schedule(); new JobRequest.Builder(MediaFoldersDetectionJob.TAG) - .startNow() - .setUpdateCurrent(false) - .build() - .schedule(); + .startNow() + .setUpdateCurrent(false) + .build() + .schedule(); // register global protection with pass code registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { @@ -324,7 +327,7 @@ public class MainApp extends MultiDexApplication implements boolean set = false; for (StoragePoint storagePoint : storagePoints) { if (storagePoint.getStorageType() == StoragePoint.StorageType.INTERNAL && - storagePoint.getPrivacyType() == StoragePoint.PrivacyType.PUBLIC) { + storagePoint.getPrivacyType() == StoragePoint.PrivacyType.PUBLIC) { preferences.setStoragePath(storagePoint.getPath()); preferences.removeKeysMigrationPreference(); set = true; @@ -362,7 +365,8 @@ public class MainApp extends MultiDexApplication implements public static void initSyncOperations( final UploadsStorageManager uploadsStorageManager, final UserAccountManager accountManager, - final ConnectivityService connectivityService + final ConnectivityService connectivityService, + final PowerManagementService powerManagementService ) { updateToAutoUpload(); cleanOldEntries(); @@ -370,7 +374,7 @@ public class MainApp extends MultiDexApplication implements if (getAppContext() != null) { if (PermissionUtil.checkSelfPermission(getAppContext(), - Manifest.permission.WRITE_EXTERNAL_STORAGE)) { + Manifest.permission.WRITE_EXTERNAL_STORAGE)) { splitOutAutoUploadEntries(); } else { AppPreferences preferences = AppPreferencesImpl.fromContext(getAppContext()); @@ -381,17 +385,30 @@ public class MainApp extends MultiDexApplication implements initiateExistingAutoUploadEntries(); FilesSyncHelper.scheduleFilesSyncIfNeeded(mContext); - FilesSyncHelper.restartJobsIfNeeded(uploadsStorageManager, accountManager, connectivityService); + FilesSyncHelper.restartJobsIfNeeded( + uploadsStorageManager, + accountManager, + connectivityService, + powerManagementService); FilesSyncHelper.scheduleOfflineSyncIfNeeded(); - ReceiversHelper.registerNetworkChangeReceiver(uploadsStorageManager, accountManager, connectivityService); + ReceiversHelper.registerNetworkChangeReceiver(uploadsStorageManager, + accountManager, + connectivityService, + powerManagementService); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - ReceiversHelper.registerPowerChangeReceiver(uploadsStorageManager, accountManager, connectivityService); + ReceiversHelper.registerPowerChangeReceiver(uploadsStorageManager, + accountManager, + connectivityService, + powerManagementService); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - ReceiversHelper.registerPowerSaveReceiver(uploadsStorageManager, accountManager, connectivityService); + ReceiversHelper.registerPowerSaveReceiver(uploadsStorageManager, + accountManager, + connectivityService, + powerManagementService); } } @@ -399,36 +416,36 @@ public class MainApp extends MultiDexApplication implements if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O && getAppContext() != null) { Context context = getAppContext(); NotificationManager notificationManager = (NotificationManager) - context.getSystemService(Context.NOTIFICATION_SERVICE); + context.getSystemService(Context.NOTIFICATION_SERVICE); if (notificationManager != null) { createChannel(notificationManager, NotificationUtils.NOTIFICATION_CHANNEL_DOWNLOAD, - R.string.notification_channel_download_name, - R.string.notification_channel_download_description, context); + R.string.notification_channel_download_name, + R.string.notification_channel_download_description, context); createChannel(notificationManager, NotificationUtils.NOTIFICATION_CHANNEL_UPLOAD, - R.string.notification_channel_upload_name, - R.string.notification_channel_upload_description, context); + R.string.notification_channel_upload_name, + R.string.notification_channel_upload_description, context); createChannel(notificationManager, NotificationUtils.NOTIFICATION_CHANNEL_MEDIA, - R.string.notification_channel_media_name, - R.string.notification_channel_media_description, context); + R.string.notification_channel_media_name, + R.string.notification_channel_media_description, context); createChannel(notificationManager, NotificationUtils.NOTIFICATION_CHANNEL_FILE_SYNC, - R.string.notification_channel_file_sync_name, - R.string.notification_channel_file_sync_description, context); + R.string.notification_channel_file_sync_name, + R.string.notification_channel_file_sync_description, context); createChannel(notificationManager, NotificationUtils.NOTIFICATION_CHANNEL_FILE_OBSERVER, - R.string.notification_channel_file_observer_name, R.string - .notification_channel_file_observer_description, context); + R.string.notification_channel_file_observer_name, R.string + .notification_channel_file_observer_description, context); createChannel(notificationManager, NotificationUtils.NOTIFICATION_CHANNEL_PUSH, - R.string.notification_channel_push_name, R.string - .notification_channel_push_description, context, NotificationManager.IMPORTANCE_DEFAULT); + R.string.notification_channel_push_name, R.string + .notification_channel_push_description, context, NotificationManager.IMPORTANCE_DEFAULT); createChannel(notificationManager, NotificationUtils.NOTIFICATION_CHANNEL_GENERAL, R.string - .notification_channel_general_name, R.string.notification_channel_general_description, - context, NotificationManager.IMPORTANCE_DEFAULT); + .notification_channel_general_name, R.string.notification_channel_general_description, + context, NotificationManager.IMPORTANCE_DEFAULT); } else { Log_OC.e(TAG, "Notification manager is null"); } @@ -440,15 +457,15 @@ public class MainApp extends MultiDexApplication implements String channelId, int channelName, int channelDescription, Context context) { createChannel(notificationManager, channelId, channelName, channelDescription, context, - NotificationManager.IMPORTANCE_LOW); + NotificationManager.IMPORTANCE_LOW); } private static void createChannel(NotificationManager notificationManager, String channelId, int channelName, int channelDescription, Context context, int importance) { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O - && getAppContext() != null - && notificationManager.getNotificationChannel(channelId) == null) { + && getAppContext() != null + && notificationManager.getNotificationChannel(channelId) == null) { CharSequence name = context.getString(channelName); String description = context.getString(channelDescription); NotificationChannel channel = new NotificationChannel(channelId, name, importance); @@ -547,29 +564,29 @@ public class MainApp extends MultiDexApplication implements } private static void updateToAutoUpload() { - Context context = getAppContext(); - AppPreferences preferences = AppPreferencesImpl.fromContext(context); - if (preferences.instantPictureUploadEnabled() || preferences.instantVideoUploadEnabled()){ - preferences.removeLegacyPreferences(); + Context context = getAppContext(); + AppPreferences preferences = AppPreferencesImpl.fromContext(context); + if (preferences.instantPictureUploadEnabled() || preferences.instantVideoUploadEnabled()) { + preferences.removeLegacyPreferences(); - // show info pop-up - try { - new AlertDialog.Builder(context, R.style.Theme_ownCloud_Dialog) - .setTitle(R.string.drawer_synced_folders) - .setMessage(R.string.synced_folders_new_info) - .setPositiveButton(R.string.drawer_open, (dialog, which) -> { - // show Auto Upload - Intent folderSyncIntent = new Intent(context, SyncedFoldersActivity.class); - dialog.dismiss(); - context.startActivity(folderSyncIntent); - }) - .setNegativeButton(R.string.drawer_close, (dialog, which) -> dialog.dismiss()) - .setIcon(R.drawable.nav_synced_folders) - .show(); - } catch (WindowManager.BadTokenException e) { - Log_OC.i(TAG, "Error showing Auto Upload Update dialog, so skipping it: " + e.getMessage()); - } + // show info pop-up + try { + new AlertDialog.Builder(context, R.style.Theme_ownCloud_Dialog) + .setTitle(R.string.drawer_synced_folders) + .setMessage(R.string.synced_folders_new_info) + .setPositiveButton(R.string.drawer_open, (dialog, which) -> { + // show Auto Upload + Intent folderSyncIntent = new Intent(context, SyncedFoldersActivity.class); + dialog.dismiss(); + context.startActivity(folderSyncIntent); + }) + .setNegativeButton(R.string.drawer_close, (dialog, which) -> dialog.dismiss()) + .setIcon(R.drawable.nav_synced_folders) + .show(); + } catch (WindowManager.BadTokenException e) { + Log_OC.i(TAG, "Error showing Auto Upload Update dialog, so skipping it: " + e.getMessage()); } + } } private static void updateAutoUploadEntries() { @@ -578,7 +595,7 @@ public class MainApp extends MultiDexApplication implements AppPreferences preferences = AppPreferencesImpl.fromContext(context); if (!preferences.isAutoUploadPathsUpdateEnabled()) { SyncedFolderProvider syncedFolderProvider = - new SyncedFolderProvider(MainApp.getAppContext().getContentResolver(), preferences); + new SyncedFolderProvider(MainApp.getAppContext().getContentResolver(), preferences); syncedFolderProvider.updateAutoUploadPaths(mContext); } } @@ -604,7 +621,7 @@ public class MainApp extends MultiDexApplication implements for (SyncedFolder syncedFolder : syncedFolders) { idsToDelete.add(syncedFolder.getId()); Log_OC.i(TAG, "Migration check for synced_folders record: " - + syncedFolder.getId() + " - " + syncedFolder.getLocalPath()); + + syncedFolder.getId() + " - " + syncedFolder.getLocalPath()); for (MediaFolder imageMediaFolder : imageMediaFolders) { if (imageMediaFolder.absolutePath.equals(syncedFolder.getLocalPath())) { @@ -612,7 +629,7 @@ public class MainApp extends MultiDexApplication implements newSyncedFolder.setType(MediaFolderType.IMAGE); primaryKey = syncedFolderProvider.storeSyncedFolder(newSyncedFolder); Log_OC.i(TAG, "Migrated image synced_folders record: " - + primaryKey + " - " + newSyncedFolder.getLocalPath()); + + primaryKey + " - " + newSyncedFolder.getLocalPath()); break; } } @@ -623,7 +640,7 @@ public class MainApp extends MultiDexApplication implements newSyncedFolder.setType(MediaFolderType.VIDEO); primaryKey = syncedFolderProvider.storeSyncedFolder(newSyncedFolder); Log_OC.i(TAG, "Migrated video synced_folders record: " - + primaryKey + " - " + newSyncedFolder.getLocalPath()); + + primaryKey + " - " + newSyncedFolder.getLocalPath()); break; } } @@ -643,7 +660,7 @@ public class MainApp extends MultiDexApplication implements AppPreferences preferences = AppPreferencesImpl.fromContext(getAppContext()); if (!preferences.isAutoUploadInitialized()) { SyncedFolderProvider syncedFolderProvider = - new SyncedFolderProvider(MainApp.getAppContext().getContentResolver(), preferences); + new SyncedFolderProvider(MainApp.getAppContext().getContentResolver(), preferences); for (SyncedFolder syncedFolder : syncedFolderProvider.getSyncedFolders()) { if (syncedFolder.isEnabled()) { @@ -666,7 +683,7 @@ public class MainApp extends MultiDexApplication implements if (!preferences.isLegacyClean()) { SyncedFolderProvider syncedFolderProvider = - new SyncedFolderProvider(context.getContentResolver(), preferences); + new SyncedFolderProvider(context.getContentResolver(), preferences); List syncedFolderList = syncedFolderProvider.getSyncedFolders(); Map, Long> syncedFolders = new HashMap<>(); @@ -686,7 +703,7 @@ public class MainApp extends MultiDexApplication implements if (ids.size() > 0) { int deletedCount = syncedFolderProvider.deleteSyncedFoldersNotInList(ids); - if(deletedCount > 0) { + if (deletedCount > 0) { preferences.setLegacyClean(true); } } else { diff --git a/src/main/java/com/owncloud/android/files/BootupBroadcastReceiver.java b/src/main/java/com/owncloud/android/files/BootupBroadcastReceiver.java index d44f13fe73..42998ed986 100644 --- a/src/main/java/com/owncloud/android/files/BootupBroadcastReceiver.java +++ b/src/main/java/com/owncloud/android/files/BootupBroadcastReceiver.java @@ -28,6 +28,7 @@ import android.content.Context; import android.content.Intent; import com.nextcloud.client.account.UserAccountManager; +import com.nextcloud.client.device.PowerManagementService; import com.nextcloud.client.network.ConnectivityService; import com.owncloud.android.MainApp; import com.owncloud.android.datamodel.UploadsStorageManager; @@ -49,6 +50,7 @@ public class BootupBroadcastReceiver extends BroadcastReceiver { @Inject UserAccountManager accountManager; @Inject UploadsStorageManager uploadsStorageManager; @Inject ConnectivityService connectivityService; + @Inject PowerManagementService powerManagementService; /** * Receives broadcast intent reporting that the system was just boot up. @@ -61,7 +63,10 @@ public class BootupBroadcastReceiver extends BroadcastReceiver { AndroidInjection.inject(this, context); if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) { - MainApp.initSyncOperations(uploadsStorageManager, accountManager, connectivityService); + MainApp.initSyncOperations(uploadsStorageManager, + accountManager, + connectivityService, + powerManagementService); MainApp.initContactsBackup(accountManager); } else { Log_OC.d(TAG, "Getting wrong intent: " + intent.getAction()); diff --git a/src/main/java/com/owncloud/android/files/services/FileUploader.java b/src/main/java/com/owncloud/android/files/services/FileUploader.java index 7ae1dd1ca3..a3528a8170 100644 --- a/src/main/java/com/owncloud/android/files/services/FileUploader.java +++ b/src/main/java/com/owncloud/android/files/services/FileUploader.java @@ -49,10 +49,10 @@ import android.util.Pair; import com.evernote.android.job.JobRequest; import com.evernote.android.job.util.Device; import com.nextcloud.client.account.UserAccountManager; +import com.nextcloud.client.device.PowerManagementService; import com.nextcloud.client.network.ConnectivityService; import com.owncloud.android.MainApp; import com.owncloud.android.R; -import com.owncloud.android.authentication.AccountUtils; import com.owncloud.android.authentication.AuthenticatorActivity; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; @@ -74,7 +74,6 @@ import com.owncloud.android.ui.activity.FileActivity; 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.PowerUtils; import com.owncloud.android.utils.ThemeUtils; import org.jetbrains.annotations.NotNull; @@ -186,6 +185,7 @@ public class FileUploader extends Service //since there can be only one instance of an Android service, there also just one db connection. @Inject UploadsStorageManager mUploadsStorageManager; @Inject ConnectivityService connectivityService; + @Inject PowerManagementService powerManagementService; private IndexedForest mPendingUploads = new IndexedForest<>(); @@ -399,6 +399,7 @@ public class FileUploader extends Service @NotNull final UploadsStorageManager uploadsStorageManager, @NotNull final ConnectivityService connectivityService, @NotNull final UserAccountManager accountManager, + @NotNull final PowerManagementService powerManagementService, @Nullable final UploadResult uploadResult ) { OCUpload[] failedUploads = uploadsStorageManager.getFailedUploads(); @@ -410,7 +411,7 @@ public class FileUploader extends Service !connectivityService.isInternetWalled(); boolean gotWifi = gotNetwork && Device.getNetworkType(context).equals(JobRequest.NetworkType.UNMETERED); boolean charging = Device.getBatteryStatus(context).isCharging(); - boolean isPowerSaving = PowerUtils.isPowerSaveMode(context); + boolean isPowerSaving = powerManagementService.isPowerSavingEnabled(); for ( OCUpload failedUpload: failedUploads) { accountMatch = account == null || account.name.equals(failedUpload.getAccountName()); @@ -641,6 +642,7 @@ public class FileUploader extends Service newUpload = new UploadFileOperation( mUploadsStorageManager, connectivityService, + powerManagementService, account, file, ocUpload, @@ -701,6 +703,7 @@ public class FileUploader extends Service UploadFileOperation newUpload = new UploadFileOperation( mUploadsStorageManager, connectivityService, + powerManagementService, account, null, upload, @@ -986,7 +989,7 @@ public class FileUploader extends Service cancel(mCurrentUpload.getAccount().name, mCurrentUpload.getFile().getRemotePath() , ResultCode.DELAYED_FOR_CHARGING); } else if (!mCurrentUpload.isIgnoringPowerSaveMode() && - PowerUtils.isPowerSaveMode(MainApp.getAppContext())) { + powerManagementService.isPowerSavingEnabled()) { cancel(mCurrentUpload.getAccount().name, mCurrentUpload.getFile().getRemotePath() , ResultCode.DELAYED_IN_POWER_SAVE_MODE); } diff --git a/src/main/java/com/owncloud/android/jobs/FilesSyncJob.java b/src/main/java/com/owncloud/android/jobs/FilesSyncJob.java index 1584de75ca..97b5b1716c 100644 --- a/src/main/java/com/owncloud/android/jobs/FilesSyncJob.java +++ b/src/main/java/com/owncloud/android/jobs/FilesSyncJob.java @@ -34,6 +34,7 @@ import android.text.TextUtils; import com.evernote.android.job.Job; import com.evernote.android.job.util.support.PersistableBundleCompat; import com.nextcloud.client.account.UserAccountManager; +import com.nextcloud.client.device.PowerManagementService; import com.nextcloud.client.network.ConnectivityService; import com.nextcloud.client.preferences.AppPreferences; import com.owncloud.android.MainApp; @@ -52,7 +53,6 @@ import com.owncloud.android.utils.FileStorageUtils; import com.owncloud.android.utils.FilesSyncHelper; import com.owncloud.android.utils.MimeType; import com.owncloud.android.utils.MimeTypeUtil; -import com.owncloud.android.utils.PowerUtils; import java.io.File; import java.text.ParsePosition; @@ -74,7 +74,7 @@ import static com.owncloud.android.datamodel.OCFile.PATH_SEPARATOR; */ public class FilesSyncJob extends Job { public static final String TAG = "FilesSyncJob"; - public static final String SKIP_CUSTOM = "skipCustom"; + static final String SKIP_CUSTOM = "skipCustom"; public static final String OVERRIDE_POWER_SAVING = "overridePowerSaving"; private static final String WAKELOCK_TAG_SEPARATION = ":"; @@ -82,17 +82,18 @@ public class FilesSyncJob extends Job { private AppPreferences preferences; private UploadsStorageManager uploadsStorageManager; private ConnectivityService connectivityService; + private PowerManagementService powerManagementService; - public FilesSyncJob( - final UserAccountManager userAccountManager, - final AppPreferences preferences, - final UploadsStorageManager uploadsStorageManager, - final ConnectivityService connectivityService - ) { + FilesSyncJob(final UserAccountManager userAccountManager, + final AppPreferences preferences, + final UploadsStorageManager uploadsStorageManager, + final ConnectivityService connectivityService, + final PowerManagementService powerManagementService) { this.userAccountManager = userAccountManager; this.preferences = preferences; this.uploadsStorageManager = uploadsStorageManager; this.connectivityService = connectivityService; + this.powerManagementService = powerManagementService; } @NonNull @@ -112,8 +113,10 @@ public class FilesSyncJob extends Job { final boolean overridePowerSaving = bundle.getBoolean(OVERRIDE_POWER_SAVING, false); // If we are in power save mode, better to postpone upload - if (PowerUtils.isPowerSaveMode(context) && !overridePowerSaving) { - wakeLock.release(); + if (powerManagementService.isPowerSavingEnabled() && !overridePowerSaving) { + if (wakeLock != null) { + wakeLock.release(); + } return Result.SUCCESS; } @@ -121,7 +124,10 @@ public class FilesSyncJob extends Job { boolean lightVersion = resources.getBoolean(R.bool.syncedFolder_light); final boolean skipCustom = bundle.getBoolean(SKIP_CUSTOM, false); - FilesSyncHelper.restartJobsIfNeeded(uploadsStorageManager, userAccountManager, connectivityService); + FilesSyncHelper.restartJobsIfNeeded(uploadsStorageManager, + userAccountManager, + connectivityService, + powerManagementService); FilesSyncHelper.insertAllDBEntries(preferences, skipCustom); // Create all the providers we'll need diff --git a/src/main/java/com/owncloud/android/jobs/NCJobCreator.java b/src/main/java/com/owncloud/android/jobs/NCJobCreator.java index f670b3c001..c0894a59e1 100644 --- a/src/main/java/com/owncloud/android/jobs/NCJobCreator.java +++ b/src/main/java/com/owncloud/android/jobs/NCJobCreator.java @@ -29,6 +29,7 @@ import android.content.Context; import com.evernote.android.job.Job; import com.evernote.android.job.JobCreator; import com.nextcloud.client.account.UserAccountManager; +import com.nextcloud.client.device.PowerManagementService; import com.nextcloud.client.network.ConnectivityService; import com.nextcloud.client.preferences.AppPreferences; import com.owncloud.android.datamodel.UploadsStorageManager; @@ -46,19 +47,22 @@ public class NCJobCreator implements JobCreator { private final AppPreferences preferences; private final UploadsStorageManager uploadsStorageManager; private final ConnectivityService connectivityService; + private final PowerManagementService powerManagementService; public NCJobCreator( Context context, UserAccountManager accountManager, AppPreferences preferences, UploadsStorageManager uploadsStorageManager, - ConnectivityService connectivityServices + ConnectivityService connectivityServices, + PowerManagementService powerManagementService ) { this.context = context; this.accountManager = accountManager; this.preferences = preferences; this.uploadsStorageManager = uploadsStorageManager; this.connectivityService = connectivityServices; + this.powerManagementService = powerManagementService; } @Override @@ -71,9 +75,13 @@ public class NCJobCreator implements JobCreator { case AccountRemovalJob.TAG: return new AccountRemovalJob(uploadsStorageManager, accountManager); case FilesSyncJob.TAG: - return new FilesSyncJob(accountManager, preferences, uploadsStorageManager, connectivityService); + return new FilesSyncJob(accountManager, + preferences, + uploadsStorageManager, + connectivityService, + powerManagementService); case OfflineSyncJob.TAG: - return new OfflineSyncJob(accountManager, connectivityService); + return new OfflineSyncJob(accountManager, connectivityService, powerManagementService); case NotificationJob.TAG: return new NotificationJob(context, accountManager); case MediaFoldersDetectionJob.TAG: diff --git a/src/main/java/com/owncloud/android/jobs/NContentObserverJob.java b/src/main/java/com/owncloud/android/jobs/NContentObserverJob.java index cc3a59eb1d..fde69fb6e9 100644 --- a/src/main/java/com/owncloud/android/jobs/NContentObserverJob.java +++ b/src/main/java/com/owncloud/android/jobs/NContentObserverJob.java @@ -27,12 +27,15 @@ import android.os.Build; import com.evernote.android.job.JobRequest; import com.evernote.android.job.util.support.PersistableBundleCompat; -import com.nextcloud.client.preferences.AppPreferencesImpl; +import com.nextcloud.client.device.PowerManagementService; +import com.nextcloud.client.preferences.AppPreferences; import com.owncloud.android.datamodel.SyncedFolderProvider; import com.owncloud.android.utils.FilesSyncHelper; -import com.owncloud.android.utils.PowerUtils; + +import javax.inject.Inject; import androidx.annotation.RequiresApi; +import dagger.android.AndroidInjection; /* Job that triggers new FilesSyncJob in case new photo or video were detected @@ -40,6 +43,16 @@ import androidx.annotation.RequiresApi; */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public class NContentObserverJob extends JobService { + + @Inject PowerManagementService powerManagementService; + @Inject AppPreferences preferences; + + @Override + public void onCreate() { + super.onCreate(); + AndroidInjection.inject(this); + } + @Override public boolean onStartJob(JobParameters params) { @@ -65,9 +78,8 @@ public class NContentObserverJob extends JobService { } private void checkAndStartFileSyncJob() { - if (!PowerUtils.isPowerSaveMode(getApplicationContext()) && - new SyncedFolderProvider(getContentResolver(), - AppPreferencesImpl.fromContext(getApplicationContext())).countEnabledSyncedFolders() > 0) { + if (!powerManagementService.isPowerSavingEnabled() && + new SyncedFolderProvider(getContentResolver(), preferences).countEnabledSyncedFolders() > 0) { PersistableBundleCompat persistableBundleCompat = new PersistableBundleCompat(); persistableBundleCompat.putBoolean(FilesSyncJob.SKIP_CUSTOM, true); diff --git a/src/main/java/com/owncloud/android/jobs/OfflineSyncJob.java b/src/main/java/com/owncloud/android/jobs/OfflineSyncJob.java index 058ff6a24b..6030fad70d 100644 --- a/src/main/java/com/owncloud/android/jobs/OfflineSyncJob.java +++ b/src/main/java/com/owncloud/android/jobs/OfflineSyncJob.java @@ -30,6 +30,7 @@ import com.evernote.android.job.Job; import com.evernote.android.job.JobManager; import com.evernote.android.job.JobRequest; import com.nextcloud.client.account.UserAccountManager; +import com.nextcloud.client.device.PowerManagementService; import com.nextcloud.client.network.ConnectivityService; import com.owncloud.android.MainApp; import com.owncloud.android.datamodel.FileDataStorageManager; @@ -39,7 +40,6 @@ import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.resources.files.CheckEtagRemoteOperation; import com.owncloud.android.operations.SynchronizeFileOperation; import com.owncloud.android.utils.FileStorageUtils; -import com.owncloud.android.utils.PowerUtils; import java.io.File; import java.util.Set; @@ -55,10 +55,12 @@ public class OfflineSyncJob extends Job { private static final String WAKELOCK_TAG_SEPARATION = ":"; private final UserAccountManager userAccountManager; private final ConnectivityService connectivityService; + private final PowerManagementService powerManagementService; - public OfflineSyncJob(UserAccountManager userAccountManager, ConnectivityService connectivityService) { + OfflineSyncJob(UserAccountManager userAccountManager, ConnectivityService connectivityService, PowerManagementService powerManagementService) { this.userAccountManager = userAccountManager; this.connectivityService = connectivityService; + this.powerManagementService = powerManagementService; } @NonNull @@ -67,7 +69,7 @@ public class OfflineSyncJob extends Job { final Context context = getContext(); PowerManager.WakeLock wakeLock = null; - if (!PowerUtils.isPowerSaveMode(context) && + if (!powerManagementService.isPowerSavingEnabled() && connectivityService.getActiveNetworkType() == JobRequest.NetworkType.UNMETERED && !connectivityService.isInternetWalled()) { Set jobs = JobManager.instance().getAllJobsForTag(TAG); diff --git a/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index 639b9dfc14..1065837b54 100644 --- a/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -32,6 +32,7 @@ import android.util.Log; import com.evernote.android.job.JobRequest; import com.evernote.android.job.util.Device; import com.google.gson.reflect.TypeToken; +import com.nextcloud.client.device.PowerManagementService; import com.nextcloud.client.network.ConnectivityService; import com.owncloud.android.datamodel.ArbitraryDataProvider; import com.owncloud.android.datamodel.DecryptedFolderMetadata; @@ -65,7 +66,6 @@ import com.owncloud.android.utils.EncryptionUtils; import com.owncloud.android.utils.FileStorageUtils; import com.owncloud.android.utils.MimeType; import com.owncloud.android.utils.MimeTypeUtil; -import com.owncloud.android.utils.PowerUtils; import com.owncloud.android.utils.UriUtils; import org.apache.commons.httpclient.HttpStatus; @@ -147,6 +147,7 @@ public class UploadFileOperation extends SyncOperation { final private OCUpload mUpload; final private UploadsStorageManager uploadsStorageManager; final private ConnectivityService connectivityService; + final private PowerManagementService powerManagementService; private boolean encryptedAncestor; @@ -178,6 +179,7 @@ public class UploadFileOperation extends SyncOperation { public UploadFileOperation(UploadsStorageManager uploadsStorageManager, ConnectivityService connectivityService, + PowerManagementService powerManagementService, Account account, OCFile file, OCUpload upload, @@ -201,6 +203,7 @@ public class UploadFileOperation extends SyncOperation { this.uploadsStorageManager = uploadsStorageManager; this.connectivityService = connectivityService; + this.powerManagementService = powerManagementService; mAccount = account; mUpload = upload; if (file == null) { @@ -742,7 +745,7 @@ public class UploadFileOperation extends SyncOperation { } // check that device is not in power save mode - if (!mIgnoringPowerSaveMode && PowerUtils.isPowerSaveMode(mContext)) { + if (!mIgnoringPowerSaveMode && powerManagementService.isPowerSavingEnabled()) { Log_OC.d(TAG, "Upload delayed because device is in power save mode: " + getRemotePath()); remoteOperationResult = new RemoteOperationResult(ResultCode.DELAYED_IN_POWER_SAVE_MODE); } diff --git a/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java b/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java index 79e40a599a..e38ce14a5a 100755 --- a/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java @@ -45,6 +45,7 @@ import com.evernote.android.job.JobRequest; import com.evernote.android.job.util.support.PersistableBundleCompat; import com.google.android.material.bottomnavigation.BottomNavigationView; import com.nextcloud.client.account.UserAccountManager; +import com.nextcloud.client.device.PowerManagementService; import com.nextcloud.client.network.ConnectivityService; import com.owncloud.android.R; import com.owncloud.android.datamodel.UploadsStorageManager; @@ -117,6 +118,9 @@ public class UploadListActivity extends FileActivity { @Inject ConnectivityService connectivityService; + @Inject + PowerManagementService powerManagementService; + @Override public void showFiles(boolean onDeviceOnly) { super.showFiles(onDeviceOnly); @@ -180,7 +184,8 @@ public class UploadListActivity extends FileActivity { uploadListAdapter = new UploadListAdapter(this, uploadsStorageManager, userAccountManager, - connectivityService); + connectivityService, + powerManagementService); final GridLayoutManager lm = new GridLayoutManager(this, 1); uploadListAdapter.setLayoutManager(lm); @@ -229,6 +234,7 @@ public class UploadListActivity extends FileActivity { uploadsStorageManager, connectivityService, userAccountManager, + powerManagementService, null)).start(); // update UI @@ -301,7 +307,10 @@ public class UploadListActivity extends FileActivity { protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == FileActivity.REQUEST_CODE__UPDATE_CREDENTIALS && resultCode == RESULT_OK) { - FilesSyncHelper.restartJobsIfNeeded(uploadsStorageManager, userAccountManager, connectivityService); + FilesSyncHelper.restartJobsIfNeeded(uploadsStorageManager, + userAccountManager, + connectivityService, + powerManagementService); } } @@ -321,7 +330,10 @@ public class UploadListActivity extends FileActivity { } else { // already updated -> just retry! - FilesSyncHelper.restartJobsIfNeeded(uploadsStorageManager, userAccountManager, connectivityService); + FilesSyncHelper.restartJobsIfNeeded(uploadsStorageManager, + userAccountManager, + connectivityService, + powerManagementService); } } else { diff --git a/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java index 556d9575ed..409437867a 100755 --- a/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java @@ -41,6 +41,7 @@ import android.widget.TextView; import com.afollestad.sectionedrecyclerview.SectionedRecyclerViewAdapter; import com.afollestad.sectionedrecyclerview.SectionedViewHolder; import com.nextcloud.client.account.UserAccountManager; +import com.nextcloud.client.device.PowerManagementService; import com.nextcloud.client.network.ConnectivityService; import com.owncloud.android.R; import com.owncloud.android.datamodel.OCFile; @@ -74,6 +75,7 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter private constructor - } - - /** - * 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 != null && powerManager.isPowerSaveMode(); - } - - // For older versions, we just say that device is not in power save mode - return false; - } -} diff --git a/src/main/java/com/owncloud/android/utils/ReceiversHelper.java b/src/main/java/com/owncloud/android/utils/ReceiversHelper.java index 30ed3704ea..81eb82a821 100644 --- a/src/main/java/com/owncloud/android/utils/ReceiversHelper.java +++ b/src/main/java/com/owncloud/android/utils/ReceiversHelper.java @@ -30,6 +30,7 @@ import android.content.IntentFilter; import com.evernote.android.job.JobRequest; import com.evernote.android.job.util.Device; import com.nextcloud.client.account.UserAccountManager; +import com.nextcloud.client.device.PowerManagementService; import com.nextcloud.client.network.ConnectivityService; import com.owncloud.android.MainApp; import com.owncloud.android.datamodel.UploadsStorageManager; @@ -43,11 +44,10 @@ public final class ReceiversHelper { // utility class -> private constructor } - public static void registerNetworkChangeReceiver( - final UploadsStorageManager uploadsStorageManager, - final UserAccountManager accountManager, - final ConnectivityService connectivityService - ) { + public static void registerNetworkChangeReceiver(final UploadsStorageManager uploadsStorageManager, + final UserAccountManager accountManager, + final ConnectivityService connectivityService, + final PowerManagementService powerManagementService) { Context context = MainApp.getAppContext(); IntentFilter intentFilter = new IntentFilter(); @@ -58,7 +58,10 @@ public final class ReceiversHelper { @Override public void onReceive(Context context, Intent intent) { if (!Device.getNetworkType(context).equals(JobRequest.NetworkType.ANY)) { - FilesSyncHelper.restartJobsIfNeeded(uploadsStorageManager, accountManager, connectivityService); + FilesSyncHelper.restartJobsIfNeeded(uploadsStorageManager, + accountManager, + connectivityService, + powerManagementService); } } }; @@ -69,7 +72,8 @@ public final class ReceiversHelper { public static void registerPowerChangeReceiver( final UploadsStorageManager uploadsStorageManager, final UserAccountManager accountManager, - final ConnectivityService connectivityService + final ConnectivityService connectivityService, + final PowerManagementService powerManagementService ) { Context context = MainApp.getAppContext(); @@ -81,7 +85,10 @@ public final class ReceiversHelper { @Override public void onReceive(Context context, Intent intent) { if (Intent.ACTION_POWER_CONNECTED.equals(intent.getAction())) { - FilesSyncHelper.restartJobsIfNeeded(uploadsStorageManager, accountManager, connectivityService); + FilesSyncHelper.restartJobsIfNeeded(uploadsStorageManager, + accountManager, + connectivityService, + powerManagementService); } } }; @@ -92,8 +99,9 @@ public final class ReceiversHelper { public static void registerPowerSaveReceiver( final UploadsStorageManager uploadsStorageManager, final UserAccountManager accountManager, - final ConnectivityService connectivityService - ) { + final ConnectivityService connectivityService, + final PowerManagementService powerManagementService + ) { Context context = MainApp.getAppContext(); IntentFilter intentFilter = new IntentFilter(); @@ -102,8 +110,11 @@ public final class ReceiversHelper { BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - if (!PowerUtils.isPowerSaveMode(context)) { - FilesSyncHelper.restartJobsIfNeeded(uploadsStorageManager, accountManager, connectivityService); + if (!powerManagementService.isPowerSavingEnabled()) { + FilesSyncHelper.restartJobsIfNeeded(uploadsStorageManager, + accountManager, + connectivityService, + powerManagementService); } } }; diff --git a/src/test/java/com/owncloud/android/utils/PowerUtilsTest.java b/src/test/java/com/owncloud/android/utils/PowerUtilsTest.java deleted file mode 100644 index 505766899c..0000000000 --- a/src/test/java/com/owncloud/android/utils/PowerUtilsTest.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Nextcloud Android client application - * - * @author Edvard Holst - * Copyright (C) 2019 Edvard Holst - * Copyright (C) 2019 Nextcloud GmbH - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.owncloud.android.utils; - -import android.content.Context; -import android.os.Build; -import android.os.PowerManager; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; - -import static org.junit.Assert.*; -import static org.mockito.Mockito.when; - -public class PowerUtilsTest { - - @Mock - private Context mContext; - - @Mock - private PowerManager mPowerManager; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - } - - private static void setFinalStatic(Field field, Object newValue) throws Exception { - field.setAccessible(true); - - Field modifiersField = Field.class.getDeclaredField("modifiers"); - modifiersField.setAccessible(true); - modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); - - field.set(null, newValue); - } - - @Test - public void isPowerSaveMode_assertCorrectlyReportsTrue() throws Exception { - setFinalStatic(Build.VERSION.class.getField("SDK_INT"), Build.VERSION_CODES.O); - when(mContext.getSystemService(Context.POWER_SERVICE)).thenReturn(mPowerManager); - when(mPowerManager.isPowerSaveMode()).thenReturn(true); - assertTrue("Incorrectly reported power saving mode on", - PowerUtils.isPowerSaveMode(mContext)); - } - - @Test - public void isPowerSaveMode_assertCorrectlyReportsFalse() throws Exception { - setFinalStatic(Build.VERSION.class.getField("SDK_INT"), Build.VERSION_CODES.O); - when(mContext.getSystemService(Context.POWER_SERVICE)).thenReturn(mPowerManager); - when(mPowerManager.isPowerSaveMode()).thenReturn(false); - assertFalse("Incorrectly reported power saving mode off", - PowerUtils.isPowerSaveMode(mContext)); - } -}