From deff0ce80df08c161998516aa5ab2cdece63a1e5 Mon Sep 17 00:00:00 2001 From: TETRA2000 Date: Fri, 15 Jan 2016 08:11:05 +0900 Subject: [PATCH 1/4] update to API 23 --- build.gradle | 2 +- k9mail-library/build.gradle | 3 +++ k9mail/src/main/java/com/fsck/k9/helper/MergeCursor.java | 8 ++++++++ .../main/java/com/fsck/k9/provider/MessageProvider.java | 8 ++++++++ .../pulltorefresh/library/PullToRefreshWebView.java | 4 ++-- 5 files changed, 22 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 512fdbfac2..65226215b2 100644 --- a/build.gradle +++ b/build.gradle @@ -13,7 +13,7 @@ project.ext { preDexLibs = !project.hasProperty('disablePreDex') testCoverage = project.hasProperty('testCoverage') - compileSdkVersion = 22 + compileSdkVersion = 23 buildToolsVersion = '23.0.1' } diff --git a/k9mail-library/build.gradle b/k9mail-library/build.gradle index f3550c278e..d002e67a00 100644 --- a/k9mail-library/build.gradle +++ b/k9mail-library/build.gradle @@ -31,6 +31,9 @@ android { testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } + // for using Apache HTTP Client + useLibrary 'org.apache.http.legacy' + buildTypes { debug { testCoverageEnabled rootProject.testCoverage diff --git a/k9mail/src/main/java/com/fsck/k9/helper/MergeCursor.java b/k9mail/src/main/java/com/fsck/k9/helper/MergeCursor.java index a69ebf5bcc..96dff9ccb0 100644 --- a/k9mail/src/main/java/com/fsck/k9/helper/MergeCursor.java +++ b/k9mail/src/main/java/com/fsck/k9/helper/MergeCursor.java @@ -19,12 +19,14 @@ package com.fsck.k9.helper; import java.util.Comparator; +import android.annotation.TargetApi; import android.content.ContentResolver; import android.database.CharArrayBuffer; import android.database.ContentObserver; import android.database.Cursor; import android.database.DataSetObserver; import android.net.Uri; +import android.os.Build; import android.os.Bundle; @@ -213,6 +215,12 @@ public class MergeCursor implements Cursor { return mActiveCursor.getWantsAllOnMoveCalls(); } + @TargetApi(Build.VERSION_CODES.M) + @Override + public void setExtras(Bundle extras) { + mActiveCursor.setExtras(extras); + } + @Override public boolean isAfterLast() { int count = getCount(); diff --git a/k9mail/src/main/java/com/fsck/k9/provider/MessageProvider.java b/k9mail/src/main/java/com/fsck/k9/provider/MessageProvider.java index fec2b4f5ff..b54abf98c0 100644 --- a/k9mail/src/main/java/com/fsck/k9/provider/MessageProvider.java +++ b/k9mail/src/main/java/com/fsck/k9/provider/MessageProvider.java @@ -1,5 +1,6 @@ package com.fsck.k9.provider; +import android.annotation.TargetApi; import android.app.Application; import android.content.ContentProvider; import android.content.ContentResolver; @@ -15,6 +16,7 @@ import android.database.DataSetObserver; import android.database.MatrixCursor; import android.net.Uri; import android.os.Binder; +import android.os.Build; import android.os.Bundle; import android.provider.BaseColumns; import android.util.Log; @@ -715,6 +717,12 @@ public class MessageProvider extends ContentProvider { return mCursor.getWantsAllOnMoveCalls(); } + @TargetApi(Build.VERSION_CODES.M) + @Override + public void setExtras(Bundle extras) { + mCursor.setExtras(extras); + } + @Override public boolean isAfterLast() { checkClosed(); diff --git a/plugins/Android-PullToRefresh/library/src/com/handmark/pulltorefresh/library/PullToRefreshWebView.java b/plugins/Android-PullToRefresh/library/src/com/handmark/pulltorefresh/library/PullToRefreshWebView.java index 3f873de086..d1abc54231 100644 --- a/plugins/Android-PullToRefresh/library/src/com/handmark/pulltorefresh/library/PullToRefreshWebView.java +++ b/plugins/Android-PullToRefresh/library/src/com/handmark/pulltorefresh/library/PullToRefreshWebView.java @@ -112,7 +112,7 @@ public class PullToRefreshWebView extends PullToRefreshBase { @Override protected boolean isReadyForPullEnd() { - float exactContentHeight = FloatMath.floor(mRefreshableView.getContentHeight() * mRefreshableView.getScale()); + double exactContentHeight = Math.floor(mRefreshableView.getContentHeight() * mRefreshableView.getScale()); return mRefreshableView.getScrollY() >= (exactContentHeight - mRefreshableView.getHeight()); } @@ -158,7 +158,7 @@ public class PullToRefreshWebView extends PullToRefreshBase { } private int getScrollRange() { - return (int) Math.max(0, FloatMath.floor(mRefreshableView.getContentHeight() * mRefreshableView.getScale()) + return (int) Math.max(0, Math.floor(mRefreshableView.getContentHeight() * mRefreshableView.getScale()) - (getHeight() - getPaddingBottom() - getPaddingTop())); } } From 47a6d1bf27d79508081558715e4cbf97d9e7bf3b Mon Sep 17 00:00:00 2001 From: TETRA2000 Date: Fri, 15 Jan 2016 08:13:02 +0900 Subject: [PATCH 2/4] enable syncing while idle on Android M devices --- .../com/fsck/k9/helper/K9AlarmManager.java | 66 +++++++++++++++++++ .../com/fsck/k9/service/BootReceiver.java | 8 +-- 2 files changed, 70 insertions(+), 4 deletions(-) create mode 100644 k9mail/src/main/java/com/fsck/k9/helper/K9AlarmManager.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 new file mode 100644 index 0000000000..9232fdf356 --- /dev/null +++ b/k9mail/src/main/java/com/fsck/k9/helper/K9AlarmManager.java @@ -0,0 +1,66 @@ +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; + +public class K9AlarmManager { + private AlarmManager alarmManager; + private PowerManager powerManager; + private String packageName; + + private 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) { + return new K9AlarmManager(context); + } + + public void set(int type, long triggerAtMillis, PendingIntent operation) { + if (isDozeSupported() && isDozeWhiteListed()) { + setAndAllowWhileIdle(type, triggerAtMillis, operation); + } else { + alarmManager.set(type, triggerAtMillis, operation); + } + } + + @TargetApi(Build.VERSION_CODES.M) + public void setAndAllowWhileIdle(int type, long triggerAtMillis, PendingIntent operation) { + alarmManager.setAndAllowWhileIdle(type, triggerAtMillis, operation); + } + + @TargetApi(Build.VERSION_CODES.KITKAT) + public void setExact(int type, long triggerAtMillis, PendingIntent operation) { + if (isDozeSupported() && isDozeWhiteListed()) { + setExactAndAllowWhileIdle(type, triggerAtMillis, operation); + } else { + alarmManager.setExact(type, triggerAtMillis, operation); + } + } + + @TargetApi(Build.VERSION_CODES.M) + public void setExactAndAllowWhileIdle(int type, long triggerAtMillis, PendingIntent operation) { + alarmManager.setExactAndAllowWhileIdle(type, triggerAtMillis, operation); + } + + + public void cancel(PendingIntent operation) { + alarmManager.cancel(operation); + } + + private 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/service/BootReceiver.java b/k9mail/src/main/java/com/fsck/k9/service/BootReceiver.java index dec7cb1ef5..5361a4b4fc 100644 --- a/k9mail/src/main/java/com/fsck/k9/service/BootReceiver.java +++ b/k9mail/src/main/java/com/fsck/k9/service/BootReceiver.java @@ -12,6 +12,7 @@ import android.net.Uri; import android.util.Log; import com.fsck.k9.K9; +import com.fsck.k9.helper.K9AlarmManager; public class BootReceiver extends CoreReceiver { @@ -61,7 +62,7 @@ public class BootReceiver extends CoreReceiver { Log.i(K9.LOG_TAG, "BootReceiver Scheduling intent " + alarmedIntent + " for " + new Date(atTime)); PendingIntent pi = buildPendingIntent(context, intent); - AlarmManager alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE); + K9AlarmManager alarmMgr = K9AlarmManager.getAlarmManager(context); alarmMgr.set(AlarmManager.RTC_WAKEUP, atTime, pi); } else if (CANCEL_INTENT.equals(action)) { @@ -71,7 +72,7 @@ public class BootReceiver extends CoreReceiver { PendingIntent pi = buildPendingIntent(context, intent); - AlarmManager alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE); + K9AlarmManager alarmMgr = K9AlarmManager.getAlarmManager(context); alarmMgr.cancel(pi); } @@ -119,8 +120,7 @@ public class BootReceiver extends CoreReceiver { * @param context */ public static void purgeSchedule(final Context context) { - final AlarmManager alarmService = (AlarmManager) context - .getSystemService(Context.ALARM_SERVICE); + final K9AlarmManager alarmService = K9AlarmManager.getAlarmManager(context); alarmService.cancel(PendingIntent.getBroadcast(context, 0, new Intent() { @Override public boolean filterEquals(final Intent other) { From 4d241f0f04d760035bb93df184147d5c23d2e3fa Mon Sep 17 00:00:00 2001 From: cketti Date: Fri, 15 Jan 2016 08:56:29 +0100 Subject: [PATCH 3/4] Remove unused methods --- .../java/com/fsck/k9/helper/K9AlarmManager.java | 15 --------------- 1 file changed, 15 deletions(-) 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 9232fdf356..0f3fd495a1 100644 --- a/k9mail/src/main/java/com/fsck/k9/helper/K9AlarmManager.java +++ b/k9mail/src/main/java/com/fsck/k9/helper/K9AlarmManager.java @@ -35,21 +35,6 @@ public class K9AlarmManager { alarmManager.setAndAllowWhileIdle(type, triggerAtMillis, operation); } - @TargetApi(Build.VERSION_CODES.KITKAT) - public void setExact(int type, long triggerAtMillis, PendingIntent operation) { - if (isDozeSupported() && isDozeWhiteListed()) { - setExactAndAllowWhileIdle(type, triggerAtMillis, operation); - } else { - alarmManager.setExact(type, triggerAtMillis, operation); - } - } - - @TargetApi(Build.VERSION_CODES.M) - public void setExactAndAllowWhileIdle(int type, long triggerAtMillis, PendingIntent operation) { - alarmManager.setExactAndAllowWhileIdle(type, triggerAtMillis, operation); - } - - public void cancel(PendingIntent operation) { alarmManager.cancel(operation); } From 3e23eeb289d23422a47a27a73b9dbfc562db3743 Mon Sep 17 00:00:00 2001 From: cketti Date: Fri, 15 Jan 2016 09:39:29 +0100 Subject: [PATCH 4/4] Add tests for K9AlarmManager --- .../com/fsck/k9/helper/K9AlarmManager.java | 15 ++- .../fsck/k9/helper/K9AlarmManagerTest.java | 126 ++++++++++++++++++ 2 files changed, 136 insertions(+), 5 deletions(-) create mode 100644 k9mail/src/test/java/com/fsck/k9/helper/K9AlarmManagerTest.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 0f3fd495a1..24dbf67ad7 100644 --- a/k9mail/src/main/java/com/fsck/k9/helper/K9AlarmManager.java +++ b/k9mail/src/main/java/com/fsck/k9/helper/K9AlarmManager.java @@ -6,13 +6,17 @@ import android.app.PendingIntent; import android.content.Context; import android.os.Build; import android.os.PowerManager; +import android.support.annotation.VisibleForTesting; + public class K9AlarmManager { - private AlarmManager alarmManager; - private PowerManager powerManager; - private String packageName; + private final AlarmManager alarmManager; + private final PowerManager powerManager; + private final String packageName; - private K9AlarmManager(Context context) { + + @VisibleForTesting + K9AlarmManager(Context context) { alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); packageName = context.getPackageName(); @@ -39,7 +43,8 @@ public class K9AlarmManager { alarmManager.cancel(operation); } - private boolean isDozeSupported() { + @VisibleForTesting + protected boolean isDozeSupported() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M; } diff --git a/k9mail/src/test/java/com/fsck/k9/helper/K9AlarmManagerTest.java b/k9mail/src/test/java/com/fsck/k9/helper/K9AlarmManagerTest.java new file mode 100644 index 0000000000..037859723b --- /dev/null +++ b/k9mail/src/test/java/com/fsck/k9/helper/K9AlarmManagerTest.java @@ -0,0 +1,126 @@ +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 org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + + +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(); + + + @Mock + private AlarmManager systemAlarmManager; + + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + } + + @Test + public void set_withoutDozeSupport_shouldCallSetOnAlarmManager() throws Exception { + K9AlarmManager alarmManager = createK9AlarmManagerWithoutDozeSupport(); + + alarmManager.set(TIMER_TYPE, TIMEOUT, PENDING_INTENT); + + verify(systemAlarmManager).set(TIMER_TYPE, TIMEOUT, PENDING_INTENT); + } + + @Test + public void set_withDozeSupportAndNotWhiteListed_shouldCallSetOnAlarmManager() throws Exception { + K9AlarmManager alarmManager = createK9AlarmManagerWithDozeSupport(false); + + 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); + + 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); + + alarmManager.cancel(PENDING_INTENT); + + verify(systemAlarmManager).cancel(PENDING_INTENT); + } + + + 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 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 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; + } + } +}