Check if app is excluded from battery optimization (#3589)

Check if app is excluded from battery optimization
This commit is contained in:
Tobias Kaminsky 2019-08-20 12:10:34 +02:00 committed by GitHub
commit 9a023c6496
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 109 additions and 13 deletions

View file

@ -1 +1 @@
413
412

View file

@ -2,8 +2,11 @@
* Nextcloud Android client application
*
* @author Chris Narkiewicz
* @author Tobias Kaminsky
*
* Copyright (C) 2019 Chris Narkiewicz <hello@ezaquarii.com>
* Copyright (C) 2019 Tobias Kaminsky
* 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
@ -23,6 +26,7 @@ package com.nextcloud.client.device
import android.content.Context
import android.os.PowerManager
import com.nextcloud.client.preferences.AppPreferences
import dagger.Module
import dagger.Provides
@ -30,12 +34,13 @@ import dagger.Provides
class DeviceModule {
@Provides
fun powerManagementService(context: Context): PowerManagementService {
fun powerManagementService(context: Context, preferences: AppPreferences): PowerManagementService {
val platformPowerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager
return PowerManagementServiceImpl(
context = context,
powerManager = platformPowerManager,
deviceInfo = DeviceInfo()
deviceInfo = DeviceInfo(),
preferences = preferences
)
}
}

View file

@ -28,10 +28,13 @@ import android.content.IntentFilter
import android.os.BatteryManager
import android.os.Build
import android.os.PowerManager
import com.nextcloud.client.preferences.AppPreferences
import com.nextcloud.client.preferences.AppPreferencesImpl
internal class PowerManagementServiceImpl(
private val context: Context,
private val powerManager: PowerManager,
private val preferences: AppPreferences,
private val deviceInfo: DeviceInfo = DeviceInfo()
) : PowerManagementService {
@ -41,10 +44,22 @@ internal class PowerManagementServiceImpl(
* break application experience.
*/
val OVERLY_AGGRESSIVE_POWER_SAVING_VENDORS = setOf("samsung", "huawei", "xiaomi")
@JvmStatic
fun fromContext(context: Context): PowerManagementServiceImpl {
val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager
val preferences = AppPreferencesImpl.fromContext(context)
return PowerManagementServiceImpl(context, powerManager, preferences, DeviceInfo())
}
}
override val isPowerSavingEnabled: Boolean
get() {
if (preferences.isPowerCheckDisabled) {
return false
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
if (deviceInfo.apiLevel >= Build.VERSION_CODES.LOLLIPOP) {
return powerManager.isPowerSaveMode

View file

@ -29,6 +29,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.UsersAndGroupsSearchProvider;
@ -154,4 +155,6 @@ abstract class ComponentsModule {
@ContributesAndroidInjector abstract AccountManagerService accountManagerService();
@ContributesAndroidInjector abstract OperationsService operationsService();
@ContributesAndroidInjector abstract NContentObserverJob nContentObserverJob();
}

View file

@ -296,4 +296,8 @@ public interface AppPreferences {
void setPhotoSearchTimestamp(long timestamp);
long getPhotoSearchTimestamp();
boolean isPowerCheckDisabled();
void setPowerCheckDisabled(boolean value);
}

View file

@ -73,6 +73,7 @@ public final class AppPreferencesImpl implements AppPreferences {
private static final String PREF__SELECTED_ACCOUNT_NAME = "select_oc_account";
private static final String PREF__MIGRATED_USER_ID = "migrated_user_id";
private static final String PREF__PHOTO_SEARCH_TIMESTAMP = "photo_search_timestamp";
private static final String PREF__POWER_CHECK_DISABLED = "power_check_disabled";
private final Context context;
private final SharedPreferences preferences;
@ -534,4 +535,14 @@ public final class AppPreferencesImpl implements AppPreferences {
return preferenceName + "_" + folderIdString;
}
@Override
public boolean isPowerCheckDisabled() {
return preferences.getBoolean(PREF__POWER_CHECK_DISABLED, false);
}
@Override
public void setPowerCheckDisabled(boolean value) {
preferences.edit().putBoolean(PREF__POWER_CHECK_DISABLED, value).apply();
}
}

View file

@ -41,6 +41,7 @@ import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.nextcloud.client.device.PowerManagementService;
import com.nextcloud.client.di.Injectable;
import com.nextcloud.client.preferences.AppPreferences;
import com.owncloud.android.BuildConfig;
@ -96,7 +97,6 @@ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderA
SyncedFolderPreferencesDialogFragment.OnSyncedFolderPreferenceListener, Injectable {
private static final String[] PRIORITIZED_FOLDERS = new String[]{"Camera", "Screenshots"};
private static final List<String> SPECIAL_MANUFACTURER = Arrays.asList("Samsung", "Huawei", "Xiaomi");
public static final String EXTRA_SHOW_SIDEBAR = "SHOW_SIDEBAR";
private static final String SYNCED_FOLDER_PREFERENCES_DIALOG_TAG = "SYNCED_FOLDER_PREFERENCES_DIALOG";
private static final String TAG = SyncedFoldersActivity.class.getSimpleName();
@ -112,6 +112,7 @@ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderA
private String path;
private int type;
@Inject AppPreferences preferences;
@Inject PowerManagementService powerManagementService;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -175,9 +176,42 @@ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderA
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.synced_folders_menu, menu);
if (powerManagementService.isPowerSavingExclusionAvailable()) {
MenuItem item = menu.findItem(R.id.action_disable_power_save_check);
item.setVisible(true);
item.setChecked(preferences.isPowerCheckDisabled());
item.setOnMenuItemClickListener(this::onDisablePowerSaveCheckClicked);
}
return true;
}
private boolean onDisablePowerSaveCheckClicked(MenuItem powerCheck) {
if (!powerCheck.isChecked()) {
showPowerCheckDialog();
}
preferences.setPowerCheckDisabled(!powerCheck.isChecked());
powerCheck.setChecked(!powerCheck.isChecked());
return true;
}
private void showPowerCheckDialog() {
AlertDialog alertDialog = new AlertDialog.Builder(this)
.setView(findViewById(R.id.root_layout))
.setPositiveButton(R.string.common_ok, (dialog, which) -> dialog.dismiss())
.setTitle(ThemeUtils.getColoredTitle(getResources().getString(R.string.autoupload_disable_power_save_check),
ThemeUtils.primaryAccentColor(this)))
.setMessage(getString(R.string.power_save_check_dialog_message))
.show();
alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(ThemeUtils.primaryAccentColor(this));
}
/**
* sets up the UI elements and loads all media/synced folders.
*/
@ -691,10 +725,7 @@ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderA
}
private void showBatteryOptimizationInfo() {
boolean isSpecialManufacturer = SPECIAL_MANUFACTURER.contains(Build.MANUFACTURER.toLowerCase(Locale.ROOT));
if (isSpecialManufacturer && checkIfBatteryOptimizationEnabled() || checkIfBatteryOptimizationEnabled()) {
if (powerManagementService.isPowerSavingExclusionAvailable() || checkIfBatteryOptimizationEnabled()) {
AlertDialog alertDialog = new AlertDialog.Builder(this, R.style.Theme_ownCloud_Dialog)
.setTitle(getString(R.string.battery_optimization_title))
.setMessage(getString(R.string.battery_optimization_message))

View file

@ -25,5 +25,11 @@
<item
android:id="@+id/action_create_custom_folder"
android:title="@string/autoupload_custom_folder"/>
<item
android:id="@+id/action_disable_power_save_check"
android:title="@string/autoupload_disable_power_save_check"
android:visible="false"
android:checkable="true" />
</group>
</menu>

View file

@ -884,7 +884,8 @@
<string name="copy_internal_link">Copy internal link</string>
<string name="copy_internal_link_subline">Only works for users with access to this folder</string>
<string name="failed_to_download">Failed to pass file to download manager</string>
<string name="autoupload_disable_power_save_check">Disable power save check</string>
<string name="power_save_check_dialog_message">Disabling power save check might result in uploading files when in low battery state!</string>
<string name="etm_title">Engineering Test Mode</string>
<string name="etm_preferences">Preferences</string>

View file

@ -26,6 +26,7 @@ import android.content.Intent
import android.os.BatteryManager
import android.os.Build
import android.os.PowerManager
import com.nextcloud.client.preferences.AppPreferences
import com.nhaarman.mockitokotlin2.any
import com.nhaarman.mockitokotlin2.anyOrNull
import com.nhaarman.mockitokotlin2.eq
@ -61,13 +62,17 @@ class TestPowerManagementService {
internal lateinit var powerManagementService: PowerManagementServiceImpl
@Mock
lateinit var preferences: AppPreferences
@Before
fun setUpBase() {
MockitoAnnotations.initMocks(this)
powerManagementService = PowerManagementServiceImpl(
context = context,
powerManager = platformPowerManager,
deviceInfo = deviceInfo
context,
platformPowerManager,
preferences,
deviceInfo
)
}
}
@ -120,6 +125,21 @@ class TestPowerManagementService {
whenever(deviceInfo.vendor).thenReturn("some_other_nice_vendor")
assertFalse(powerManagementService.isPowerSavingExclusionAvailable)
}
@Test
fun `power saving check is disabled`() {
// GIVEN
// a device which falsely returns power save mode enabled
// power check is overridden by user
whenever(preferences.isPowerCheckDisabled).thenReturn(true)
whenever(platformPowerManager.isPowerSaveMode).thenReturn(true)
// WHEN
// power save mode is checked
// THEN
// power saving is disabled
assertFalse(powerManagementService.isPowerSavingEnabled)
}
}
class BatteryCharging : Base() {