diff --git a/app/src/main/java/eu/faircode/email/ActivityView.java b/app/src/main/java/eu/faircode/email/ActivityView.java index a3668f592f..4aa6a2334b 100644 --- a/app/src/main/java/eu/faircode/email/ActivityView.java +++ b/app/src/main/java/eu/faircode/email/ActivityView.java @@ -819,19 +819,7 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB if (intent.getBooleanExtra("refresh", false)) { intent.removeExtra("refresh"); setIntent(intent); - - new SimpleTask() { - @Override - protected Void onExecute(Context context, Bundle args) { - WorkerPoll.sync(context, null); - return null; - } - - @Override - protected void onException(Bundle args, Throwable ex) { - Log.unexpectedError(getSupportFragmentManager(), ex); - } - }.execute(this, new Bundle(), "view:refresh"); + ServiceSynchronize.poll(this, null); } String action = intent.getAction(); diff --git a/app/src/main/java/eu/faircode/email/FragmentOptionsSynchronize.java b/app/src/main/java/eu/faircode/email/FragmentOptionsSynchronize.java index 769f63eea7..a15f643f36 100644 --- a/app/src/main/java/eu/faircode/email/FragmentOptionsSynchronize.java +++ b/app/src/main/java/eu/faircode/email/FragmentOptionsSynchronize.java @@ -148,8 +148,7 @@ public class FragmentOptionsSynchronize extends FragmentBase implements SharedPr @Override public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { prefs.edit().putBoolean("schedule", checked).apply(); - if (checked) - ServiceSynchronize.reschedule(getContext()); + ServiceSynchronize.reschedule(getContext()); } }); diff --git a/app/src/main/java/eu/faircode/email/ServiceExternal.java b/app/src/main/java/eu/faircode/email/ServiceExternal.java index c9be31bc59..0b22444347 100644 --- a/app/src/main/java/eu/faircode/email/ServiceExternal.java +++ b/app/src/main/java/eu/faircode/email/ServiceExternal.java @@ -90,7 +90,7 @@ public class ServiceExternal extends Service { public void run() { if (accountName == null) { if (enabled == null) - WorkerPoll.sync(context, null); + ServiceSynchronize.poll(context, null); else { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); prefs.edit().putBoolean("enabled", enabled).apply(); @@ -106,7 +106,7 @@ public class ServiceExternal extends Service { } if (enabled == null) - WorkerPoll.sync(context, account.id); + ServiceSynchronize.poll(context, account.id); else { db.account().setAccountSynchronize(account.id, enabled); ServiceSynchronize.eval(context, "external account=" + accountName + " enabled=" + enabled); diff --git a/app/src/main/java/eu/faircode/email/ServiceSynchronize.java b/app/src/main/java/eu/faircode/email/ServiceSynchronize.java index 77e2df6fc0..4ab195f579 100644 --- a/app/src/main/java/eu/faircode/email/ServiceSynchronize.java +++ b/app/src/main/java/eu/faircode/email/ServiceSynchronize.java @@ -121,8 +121,9 @@ public class ServiceSynchronize extends ServiceBase implements SharedPreferences )); static final int PI_ALARM = 1; - static final int PI_BACKOFF = 2; - static final int PI_KEEPALIVE = 3; + static final int PI_POLL = 2; + static final int PI_BACKOFF = 3; + static final int PI_KEEPALIVE = 4; @Override public void onCreate() { @@ -224,11 +225,12 @@ public class ServiceSynchronize extends ServiceBase implements SharedPreferences // Some networks disallow email server connections: // - reload on network type change when disconnected - if (reload || + if (reload || sync || prev.canRun() != current.canRun() || !prev.accountState.equals(current.accountState) || (!"connected".equals(current.accountState.state) && !Objects.equals(prev.networkState.getType(), current.networkState.getType()))) { + if (prev.canRun() || current.canRun()) EntityLog.log(ServiceSynchronize.this, "### changed " + current + " reload=" + reload + @@ -239,8 +241,14 @@ public class ServiceSynchronize extends ServiceBase implements SharedPreferences " enabled=" + current.accountState.synchronize + " state=" + current.accountState.state + " type=" + prev.networkState.getType() + "/" + current.networkState.getType()); + if (prev.canRun()) - stop(prev); + if (sync && current.canRun()) { + sync(current); + continue; + } else + stop(prev); + if (current.canRun()) start(current, current.accountState.isEnabled(current.enabled) || sync); } @@ -325,6 +333,22 @@ public class ServiceSynchronize extends ServiceBase implements SharedPreferences }); } + private void sync(final TupleAccountNetworkState accountNetworkState) { + EntityLog.log(ServiceSynchronize.this, "Service sync=" + accountNetworkState); + + queue.submit(new Runnable() { + @Override + public void run() { + DB db = DB.getInstance(ServiceSynchronize.this); + List folders = db.folder().getSynchronizingFolders(accountNetworkState.accountState.id); + if (folders.size() > 0) + Collections.sort(folders, folders.get(0).getComparator(ServiceSynchronize.this)); + for (EntityFolder folder : folders) + EntityOperation.sync(ServiceSynchronize.this, folder.id, false); + } + }); + } + private void delete(final TupleAccountNetworkState accountNetworkState) { EntityLog.log(ServiceSynchronize.this, "Service delete=" + accountNetworkState); @@ -613,6 +637,7 @@ public class ServiceSynchronize extends ServiceBase implements SharedPreferences private void onEval(Intent intent) { Bundle command = new Bundle(); command.putString("name", "eval"); + command.putBoolean("sync", intent.getBooleanExtra("sync", false)); command.putLong("account", intent.getLongExtra("account", -1)); liveAccountNetworkState.post(command); } @@ -1540,31 +1565,46 @@ public class ServiceSynchronize extends ServiceBase implements SharedPreferences } private static void schedule(Context context) { - Intent intent = new Intent(context, ServiceSynchronize.class); - intent.setAction("alarm"); - PendingIntent pi = PendingIntentCompat.getForegroundService( - context, PI_ALARM, intent, PendingIntent.FLAG_UPDATE_CURRENT); + Intent alarm = new Intent(context, ServiceSynchronize.class); + alarm.setAction("alarm"); + PendingIntent piAlarm = PendingIntentCompat.getForegroundService( + context, PI_ALARM, alarm, PendingIntent.FLAG_UPDATE_CURRENT); + + Intent poll = new Intent(context, ServiceSynchronize.class); + poll.setAction("poll"); + PendingIntent piPoll = PendingIntentCompat.getForegroundService( + context, PI_POLL, poll, PendingIntent.FLAG_UPDATE_CURRENT); AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - am.cancel(pi); + am.cancel(piAlarm); + am.cancel(piPoll); + boolean enabled; + long now = new Date().getTime(); long[] schedule = getSchedule(context); if (schedule == null) - return; + enabled = true; + else { + long next = (now < schedule[0] ? schedule[0] : schedule[1]); + enabled = (now >= schedule[0] && now < schedule[1]); - long now = new Date().getTime(); - long next = (now < schedule[0] ? schedule[0] : schedule[1]); - boolean enabled = (now >= schedule[0] && now < schedule[1]); + Log.i("Schedule now=" + new Date(now)); + Log.i("Schedule start=" + new Date(schedule[0])); + Log.i("Schedule end=" + new Date(schedule[1])); + Log.i("Schedule next=" + new Date(next)); + Log.i("Schedule enabled=" + enabled); - Log.i("Schedule now=" + new Date(now)); - Log.i("Schedule start=" + new Date(schedule[0])); - Log.i("Schedule end=" + new Date(schedule[1])); - Log.i("Schedule next=" + new Date(next)); - Log.i("Schedule enabled=" + enabled); + AlarmManagerCompat.setAndAllowWhileIdle(am, AlarmManager.RTC_WAKEUP, next, piAlarm); + } - AlarmManagerCompat.setAndAllowWhileIdle(am, AlarmManager.RTC_WAKEUP, next, pi); - - WorkerPoll.init(context, enabled); + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + int pollInterval = prefs.getInt("poll_interval", 0); + if (enabled && pollInterval > 0) { + long interval = pollInterval * 60 * 1000L; + long trigger = now / interval * interval + interval; + Log.i("Poll interval=" + pollInterval + " trigger=" + new Date(trigger)); + am.setInexactRepeating(AlarmManager.RTC_WAKEUP, trigger, interval, piPoll); + } } private static long[] getSchedule(Context context) { @@ -1660,6 +1700,15 @@ public class ServiceSynchronize extends ServiceBase implements SharedPreferences .putExtra("reason", reason)); } + static void poll(Context context, Long account) { + ContextCompat.startForegroundService(context, + new Intent(context, ServiceSynchronize.class) + .setAction("eval") + .putExtra("account", account == null ? -1 : account) + .putExtra("sync", true) + .putExtra("reason", "poll")); + } + static void reload(Context context, Long account, String reason) { ContextCompat.startForegroundService(context, new Intent(context, ServiceSynchronize.class) diff --git a/app/src/main/java/eu/faircode/email/ServiceTileUnseen.java b/app/src/main/java/eu/faircode/email/ServiceTileUnseen.java index b7b19062ba..06519cf5a8 100644 --- a/app/src/main/java/eu/faircode/email/ServiceTileUnseen.java +++ b/app/src/main/java/eu/faircode/email/ServiceTileUnseen.java @@ -20,7 +20,6 @@ package eu.faircode.email; */ import android.annotation.TargetApi; -import android.content.Context; import android.content.Intent; import android.graphics.drawable.Icon; import android.os.Build; @@ -97,12 +96,6 @@ public class ServiceTileUnseen extends TileService { public void onClick() { Log.i("Click tile unseen"); - final Context context = getApplicationContext(); - new Thread(new Runnable() { - @Override - public void run() { - WorkerPoll.sync(context, null); - } - }).start(); + ServiceSynchronize.poll(this, null); } } diff --git a/app/src/main/java/eu/faircode/email/WorkerPoll.java b/app/src/main/java/eu/faircode/email/WorkerPoll.java deleted file mode 100644 index 74472ad462..0000000000 --- a/app/src/main/java/eu/faircode/email/WorkerPoll.java +++ /dev/null @@ -1,104 +0,0 @@ -package eu.faircode.email; - -/* - This file is part of FairEmail. - - FairEmail 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. - - FairEmail 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 FairEmail. If not, see . - - Copyright 2018-2020 by Marcel Bokhorst (M66B) -*/ - -import android.content.Context; -import android.content.SharedPreferences; - -import androidx.annotation.NonNull; -import androidx.preference.PreferenceManager; -import androidx.work.ExistingPeriodicWorkPolicy; -import androidx.work.PeriodicWorkRequest; -import androidx.work.WorkManager; -import androidx.work.Worker; -import androidx.work.WorkerParameters; - -import java.util.Collections; -import java.util.List; -import java.util.concurrent.TimeUnit; - -public class WorkerPoll extends Worker { - public WorkerPoll(@NonNull Context context, @NonNull WorkerParameters workerParams) { - super(context, workerParams); - Log.i("Instance " + getName()); - } - - @NonNull - @Override - public Result doWork() { - Log.i("Running " + getName()); - - sync(getApplicationContext(), null); - - return Result.success(); - } - - static void init(Context context, boolean enabled) { - try { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - int pollInterval = prefs.getInt("poll_interval", 0); - if (enabled && pollInterval > 0) { - Log.i("Queuing " + getName() + " every " + pollInterval + " minutes"); - - PeriodicWorkRequest workRequest = - new PeriodicWorkRequest.Builder(WorkerPoll.class, pollInterval, TimeUnit.MINUTES) - .build(); - WorkManager.getInstance(context) - .enqueueUniquePeriodicWork(getName(), ExistingPeriodicWorkPolicy.REPLACE, workRequest); - - Log.i("Queued " + getName()); - } else { - Log.i("Cancelling " + getName()); - WorkManager.getInstance(context).cancelUniqueWork(getName()); - Log.i("Cancelled " + getName()); - } - } catch (IllegalStateException ex) { - // https://issuetracker.google.com/issues/138465476 - Log.w(ex); - } - } - - static void sync(Context context, Long aid) { - DB db = DB.getInstance(context); - try { - db.beginTransaction(); - - List accounts = db.account().getSynchronizingAccounts(); - for (EntityAccount account : accounts) - if (aid == null || account.id.equals(aid)) { - List folders = db.folder().getSynchronizingFolders(account.id); - if (folders.size() > 0) - Collections.sort(folders, folders.get(0).getComparator(context)); - for (EntityFolder folder : folders) - EntityOperation.sync(context, folder.id, false); - } - - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } - - ServiceSynchronize.eval(context, "refresh/poll"); - } - - private static String getName() { - return WorkerPoll.class.getSimpleName(); - } -}