From 3e97ca115b855c19bb31c6ea3f605e67521cb25f Mon Sep 17 00:00:00 2001 From: cketti Date: Tue, 23 May 2017 22:35:30 +0200 Subject: [PATCH 1/3] Extract checks for Doze support to separate class --- .../com/fsck/k9/helper/K9AlarmManager.java | 36 ++++------ .../java/com/fsck/k9/power/DozeChecker.java | 28 ++++++++ .../fsck/k9/helper/K9AlarmManagerTest.java | 70 +++++-------------- 3 files changed, 58 insertions(+), 76 deletions(-) create mode 100644 k9mail/src/main/java/com/fsck/k9/power/DozeChecker.java diff --git a/k9mail/src/main/java/com/fsck/k9/helper/K9AlarmManager.java b/k9mail/src/main/java/com/fsck/k9/helper/K9AlarmManager.java index 24dbf67ad70..4f3f17a09bf 100644 --- a/k9mail/src/main/java/com/fsck/k9/helper/K9AlarmManager.java +++ b/k9mail/src/main/java/com/fsck/k9/helper/K9AlarmManager.java @@ -1,33 +1,35 @@ package com.fsck.k9.helper; + import android.annotation.TargetApi; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.Context; import android.os.Build; -import android.os.PowerManager; import android.support.annotation.VisibleForTesting; +import com.fsck.k9.power.DozeChecker; + public class K9AlarmManager { private final AlarmManager alarmManager; - private final PowerManager powerManager; - private final String packageName; + private final DozeChecker dozeChecker; - @VisibleForTesting - K9AlarmManager(Context context) { - alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); - packageName = context.getPackageName(); + public static K9AlarmManager getAlarmManager(Context context) { + AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + DozeChecker dozeChecker = new DozeChecker(context); + return new K9AlarmManager(alarmManager, dozeChecker); } - public static K9AlarmManager getAlarmManager(Context context) { - return new K9AlarmManager(context); + @VisibleForTesting + K9AlarmManager(AlarmManager alarmManager, DozeChecker dozeChecker) { + this.alarmManager = alarmManager; + this.dozeChecker = dozeChecker; } public void set(int type, long triggerAtMillis, PendingIntent operation) { - if (isDozeSupported() && isDozeWhiteListed()) { + if (dozeChecker.isDeviceIdleModeSupported() && dozeChecker.isAppWhitelisted()) { setAndAllowWhileIdle(type, triggerAtMillis, operation); } else { alarmManager.set(type, triggerAtMillis, operation); @@ -35,22 +37,12 @@ public void set(int type, long triggerAtMillis, PendingIntent operation) { } @TargetApi(Build.VERSION_CODES.M) - public void setAndAllowWhileIdle(int type, long triggerAtMillis, PendingIntent operation) { + private void setAndAllowWhileIdle(int type, long triggerAtMillis, PendingIntent operation) { alarmManager.setAndAllowWhileIdle(type, triggerAtMillis, operation); } public void cancel(PendingIntent operation) { alarmManager.cancel(operation); } - - @VisibleForTesting - protected boolean isDozeSupported() { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M; - } - - @TargetApi(Build.VERSION_CODES.M) - private boolean isDozeWhiteListed() { - return powerManager.isIgnoringBatteryOptimizations(packageName); - } } diff --git a/k9mail/src/main/java/com/fsck/k9/power/DozeChecker.java b/k9mail/src/main/java/com/fsck/k9/power/DozeChecker.java new file mode 100644 index 00000000000..db3c25dd0fd --- /dev/null +++ b/k9mail/src/main/java/com/fsck/k9/power/DozeChecker.java @@ -0,0 +1,28 @@ +package com.fsck.k9.power; + + +import android.annotation.TargetApi; +import android.content.Context; +import android.os.Build; +import android.os.PowerManager; + + +public class DozeChecker { + private final PowerManager powerManager; + private final String packageName; + + + public DozeChecker(Context context) { + powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + packageName = context.getPackageName(); + } + + public boolean isDeviceIdleModeSupported() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M; + } + + @TargetApi(Build.VERSION_CODES.M) + public boolean isAppWhitelisted() { + return powerManager.isIgnoringBatteryOptimizations(packageName); + } +} diff --git a/k9mail/src/test/java/com/fsck/k9/helper/K9AlarmManagerTest.java b/k9mail/src/test/java/com/fsck/k9/helper/K9AlarmManagerTest.java index 037859723b1..29c827d8571 100644 --- a/k9mail/src/test/java/com/fsck/k9/helper/K9AlarmManagerTest.java +++ b/k9mail/src/test/java/com/fsck/k9/helper/K9AlarmManagerTest.java @@ -1,13 +1,10 @@ package com.fsck.k9.helper; -import android.annotation.TargetApi; import android.app.AlarmManager; import android.app.PendingIntent; -import android.content.Context; -import android.os.Build.VERSION_CODES; -import android.os.PowerManager; +import com.fsck.k9.power.DozeChecker; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; @@ -19,7 +16,6 @@ public class K9AlarmManagerTest { - private static final String PACKAGE_NAME = "org.example.package"; private static final int TIMER_TYPE = AlarmManager.RTC_WAKEUP; private static final long TIMEOUT = 15L * 60L * 1000L; private static final PendingIntent PENDING_INTENT = createDummyPendingIntent(); @@ -27,16 +23,21 @@ public class K9AlarmManagerTest { @Mock private AlarmManager systemAlarmManager; + @Mock + private DozeChecker dozeChecker; + + private K9AlarmManager alarmManager; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); + alarmManager = new K9AlarmManager(systemAlarmManager, dozeChecker); } @Test public void set_withoutDozeSupport_shouldCallSetOnAlarmManager() throws Exception { - K9AlarmManager alarmManager = createK9AlarmManagerWithoutDozeSupport(); + configureDozeSupport(false); alarmManager.set(TIMER_TYPE, TIMEOUT, PENDING_INTENT); @@ -45,27 +46,27 @@ public void set_withoutDozeSupport_shouldCallSetOnAlarmManager() throws Exceptio @Test public void set_withDozeSupportAndNotWhiteListed_shouldCallSetOnAlarmManager() throws Exception { - K9AlarmManager alarmManager = createK9AlarmManagerWithDozeSupport(false); + configureDozeSupport(true); alarmManager.set(TIMER_TYPE, TIMEOUT, PENDING_INTENT); verify(systemAlarmManager).set(TIMER_TYPE, TIMEOUT, PENDING_INTENT); } - @TargetApi(VERSION_CODES.M) @Test public void set_withDozeSupportAndWhiteListed_shouldCallSetAndAllowWhileIdleOnAlarmManager() throws Exception { - K9AlarmManager alarmManager = createK9AlarmManagerWithDozeSupport(true); + configureDozeSupport(true); + addAppToBatteryOptimizationWhitelist(); alarmManager.set(TIMER_TYPE, TIMEOUT, PENDING_INTENT); verify(systemAlarmManager).setAndAllowWhileIdle(TIMER_TYPE, TIMEOUT, PENDING_INTENT); } - @TargetApi(VERSION_CODES.M) @Test public void cancel_shouldCallCancelOnAlarmManager() throws Exception { - K9AlarmManager alarmManager = createK9AlarmManagerWithDozeSupport(true); + configureDozeSupport(true); + addAppToBatteryOptimizationWhitelist(); alarmManager.cancel(PENDING_INTENT); @@ -73,54 +74,15 @@ public void cancel_shouldCallCancelOnAlarmManager() throws Exception { } - private K9AlarmManager createK9AlarmManagerWithDozeSupport(boolean whiteListed) { - PowerManager powerManager = createPowerManager(whiteListed); - Context context = createContext(powerManager); - - return new TestK9AlarmManager(context, true); - } - - private K9AlarmManager createK9AlarmManagerWithoutDozeSupport() { - PowerManager powerManager = mock(PowerManager.class); - Context context = createContext(powerManager); - - return new TestK9AlarmManager(context, false); - } - - @TargetApi(VERSION_CODES.M) - private PowerManager createPowerManager(boolean whiteListed) { - PowerManager powerManager = mock(PowerManager.class); - when(powerManager.isIgnoringBatteryOptimizations(PACKAGE_NAME)).thenReturn(whiteListed); - - return powerManager; + private void configureDozeSupport(boolean supported) { + when(dozeChecker.isDeviceIdleModeSupported()).thenReturn(supported); } - private Context createContext(PowerManager powerManager) { - Context context = mock(Context.class); - when(context.getPackageName()).thenReturn(PACKAGE_NAME); - when(context.getSystemService(Context.ALARM_SERVICE)).thenReturn(systemAlarmManager); - when(context.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager); - - return context; + private void addAppToBatteryOptimizationWhitelist() { + when(dozeChecker.isAppWhitelisted()).thenReturn(true); } private static PendingIntent createDummyPendingIntent() { return mock(PendingIntent.class); } - - - class TestK9AlarmManager extends K9AlarmManager { - private final boolean dozeSupported; - - - TestK9AlarmManager(Context context, boolean dozeSupported) { - super(context); - this.dozeSupported = dozeSupported; - } - - @Override - protected boolean isDozeSupported() { - return dozeSupported; - } - } } From 95f36795719032850ae371fcf3c090047818fb61 Mon Sep 17 00:00:00 2001 From: cketti Date: Tue, 23 May 2017 22:36:18 +0200 Subject: [PATCH 2/3] Restart push/poll after device idle mode (Doze) has been left --- k9mail/src/main/java/com/fsck/k9/K9.java | 17 +++- .../com/fsck/k9/power/DeviceIdleManager.java | 85 +++++++++++++++++++ .../com/fsck/k9/power/DeviceIdleReceiver.java | 34 ++++++++ 3 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 k9mail/src/main/java/com/fsck/k9/power/DeviceIdleManager.java create mode 100644 k9mail/src/main/java/com/fsck/k9/power/DeviceIdleReceiver.java diff --git a/k9mail/src/main/java/com/fsck/k9/K9.java b/k9mail/src/main/java/com/fsck/k9/K9.java index 8814a3ab1d0..e118cba8f55 100644 --- a/k9mail/src/main/java/com/fsck/k9/K9.java +++ b/k9mail/src/main/java/com/fsck/k9/K9.java @@ -36,6 +36,7 @@ import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.internet.BinaryTempFileBody; import com.fsck.k9.mailstore.LocalStore; +import com.fsck.k9.power.DeviceIdleManager; import com.fsck.k9.preferences.Storage; import com.fsck.k9.preferences.StorageEditor; import com.fsck.k9.provider.UnreadWidgetProvider; @@ -345,10 +346,22 @@ public static class Share { * whether any accounts are configured. */ public static void setServicesEnabled(Context context) { - int acctLength = Preferences.getPreferences(context).getAvailableAccounts().size(); + Context appContext = context.getApplicationContext(); + int acctLength = Preferences.getPreferences(appContext).getAvailableAccounts().size(); + boolean enable = acctLength > 0; - setServicesEnabled(context, acctLength > 0, null); + setServicesEnabled(appContext, enable, null); + updateDeviceIdleReceiver(appContext, enable); + } + + private static void updateDeviceIdleReceiver(Context context, boolean enable) { + DeviceIdleManager deviceIdleManager = DeviceIdleManager.getInstance(context); + if (enable) { + deviceIdleManager.registerReceiver(); + } else { + deviceIdleManager.unregisterReceiver(); + } } private static void setServicesEnabled(Context context, boolean enabled, Integer wakeLockId) { diff --git a/k9mail/src/main/java/com/fsck/k9/power/DeviceIdleManager.java b/k9mail/src/main/java/com/fsck/k9/power/DeviceIdleManager.java new file mode 100644 index 00000000000..8f4735ba138 --- /dev/null +++ b/k9mail/src/main/java/com/fsck/k9/power/DeviceIdleManager.java @@ -0,0 +1,85 @@ +package com.fsck.k9.power; + + +import android.annotation.TargetApi; +import android.content.Context; +import android.content.IntentFilter; +import android.os.Build; +import android.os.PowerManager; +import android.util.Log; + +import com.fsck.k9.K9; + + +public abstract class DeviceIdleManager { + private static DeviceIdleManager instance; + + + public static synchronized DeviceIdleManager getInstance(Context context) { + if (instance == null) { + DozeChecker dozeChecker = new DozeChecker(context); + if (dozeChecker.isDeviceIdleModeSupported() && !dozeChecker.isAppWhitelisted()) { + instance = RealDeviceIdleManager.newInstance(context); + } else { + instance = new NoOpDeviceIdleManager(); + } + } + return instance; + } + + private DeviceIdleManager() { + } + + public abstract void registerReceiver(); + public abstract void unregisterReceiver(); + + + static class NoOpDeviceIdleManager extends DeviceIdleManager { + @Override + public void registerReceiver() { + // Do nothing + } + + @Override + public void unregisterReceiver() { + // Do nothing + } + } + + @TargetApi(Build.VERSION_CODES.M) + static class RealDeviceIdleManager extends DeviceIdleManager { + private final Context context; + private final DeviceIdleReceiver deviceIdleReceiver; + private final IntentFilter intentFilter; + private boolean registered; + + + static RealDeviceIdleManager newInstance(Context context) { + Context appContext = context.getApplicationContext(); + return new RealDeviceIdleManager(appContext); + } + + private RealDeviceIdleManager(Context context) { + this.context = context; + PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + deviceIdleReceiver = new DeviceIdleReceiver(powerManager); + intentFilter = new IntentFilter(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED); + } + + @Override + public void registerReceiver() { + Log.v(K9.LOG_TAG, "Registering DeviceIdleReceiver"); + registered = true; + context.registerReceiver(deviceIdleReceiver, intentFilter); + } + + @Override + public void unregisterReceiver() { + Log.v(K9.LOG_TAG, "Unregistering DeviceIdleReceiver"); + if (registered) { + context.unregisterReceiver(deviceIdleReceiver); + registered = false; + } + } + } +} diff --git a/k9mail/src/main/java/com/fsck/k9/power/DeviceIdleReceiver.java b/k9mail/src/main/java/com/fsck/k9/power/DeviceIdleReceiver.java new file mode 100644 index 00000000000..de577ef668e --- /dev/null +++ b/k9mail/src/main/java/com/fsck/k9/power/DeviceIdleReceiver.java @@ -0,0 +1,34 @@ +package com.fsck.k9.power; + + +import android.annotation.TargetApi; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.Build; +import android.os.PowerManager; +import android.util.Log; + +import com.fsck.k9.K9; +import com.fsck.k9.service.MailService; + + +@TargetApi(Build.VERSION_CODES.M) +class DeviceIdleReceiver extends BroadcastReceiver { + private final PowerManager powerManager; + + + DeviceIdleReceiver(PowerManager powerManager) { + this.powerManager = powerManager; + } + + @Override + public void onReceive(Context context, Intent intent) { + boolean deviceInIdleMode = powerManager.isDeviceIdleMode(); + Log.v(K9.LOG_TAG, "Device idle mode changed. Idle: " + deviceInIdleMode); + + if (!deviceInIdleMode) { + MailService.actionReset(context, null); + } + } +} From cd0f48ba05b4885ea820ef566b135865532366b0 Mon Sep 17 00:00:00 2001 From: cketti Date: Mon, 28 Aug 2017 21:01:33 +0200 Subject: [PATCH 3/3] Version 5.208 --- k9mail/build.gradle | 4 ++-- k9mail/src/main/res/xml/changelog_master.xml | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/k9mail/build.gradle b/k9mail/build.gradle index 59a7ac7cc84..6ecde8d1baf 100644 --- a/k9mail/build.gradle +++ b/k9mail/build.gradle @@ -51,8 +51,8 @@ android { applicationId "com.fsck.k9" testApplicationId "com.fsck.k9.tests" - versionCode 23270 - versionName '5.207' + versionCode 23271 + versionName '5.208' minSdkVersion 15 targetSdkVersion 22 diff --git a/k9mail/src/main/res/xml/changelog_master.xml b/k9mail/src/main/res/xml/changelog_master.xml index 032ec1a408f..f9b80f88785 100644 --- a/k9mail/src/main/res/xml/changelog_master.xml +++ b/k9mail/src/main/res/xml/changelog_master.xml @@ -5,6 +5,9 @@ Locale specific versions are kept in res/xml-/changelog.xml. --> + + Fixed bug where automatic synchronization wouldn't restart after the device exited doze mode + Improved speed of local message search