Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 72af527d authored by Vincent Breitmoser's avatar Vincent Breitmoser Committed by GitHub
Browse files

Merge pull request #2545 from k9mail/doze_improvements

Doze improvements
parents a8d2bfcf 0ada7fd5
Loading
Loading
Loading
Loading
+15 −2
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.internet.BinaryTempFileBody;
import com.fsck.k9.mail.ssl.LocalKeyStore;
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;
@@ -353,10 +354,22 @@ public class K9 extends Application {
     * 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) {
+16 −24
Original line number Diff line number Diff line
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.RequiresApi;
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);
        }
    }

    @TargetApi(Build.VERSION_CODES.M)
    public void setAndAllowWhileIdle(int type, long triggerAtMillis, PendingIntent operation) {
    @RequiresApi(Build.VERSION_CODES.M)
    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);
    }
}
+84 −0
Original line number Diff line number Diff line
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 timber.log.Timber;


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() {
            Timber.v("Registering DeviceIdleReceiver");
            registered = true;
            context.registerReceiver(deviceIdleReceiver, intentFilter);
        }

        @Override
        public void unregisterReceiver() {
            Timber.v("Unregistering DeviceIdleReceiver");
            if (registered) {
                context.unregisterReceiver(deviceIdleReceiver);
                registered = false;
            }
        }
    }
}
+33 −0
Original line number Diff line number Diff line
package com.fsck.k9.power;


import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.PowerManager;
import android.support.annotation.RequiresApi;

import com.fsck.k9.service.MailService;
import timber.log.Timber;


@RequiresApi(api = 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();
        Timber.v("Device idle mode changed. Idle: %b", deviceInIdleMode);

        if (!deviceInIdleMode) {
            MailService.actionReset(context, null);
        }
    }
}
+28 −0
Original line number Diff line number Diff line
package com.fsck.k9.power;


import android.content.Context;
import android.os.Build;
import android.os.PowerManager;
import android.support.annotation.RequiresApi;


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;
    }

    @RequiresApi(api = Build.VERSION_CODES.M)
    public boolean isAppWhitelisted() {
        return powerManager.isIgnoringBatteryOptimizations(packageName);
    }
}
Loading