mirror of https://github.com/M66B/FairEmail.git
Use alarm manager to keep alive
This commit is contained in:
parent
b19f496ce0
commit
2f0babc479
|
@ -124,9 +124,8 @@ public interface DaoFolder {
|
||||||
", synchronize = :synchronize" +
|
", synchronize = :synchronize" +
|
||||||
", unified = :unified" +
|
", unified = :unified" +
|
||||||
", `after` = :after" +
|
", `after` = :after" +
|
||||||
", `poll_interval` = :poll_interval" +
|
|
||||||
" WHERE id = :id")
|
" WHERE id = :id")
|
||||||
int setFolderProperties(long id, String name, String display, boolean hide, boolean synchronize, boolean unified, int after, Integer poll_interval);
|
int setFolderProperties(long id, String name, String display, boolean hide, boolean synchronize, boolean unified, int after);
|
||||||
|
|
||||||
@Query("UPDATE folder SET name = :name WHERE account = :account AND name = :old")
|
@Query("UPDATE folder SET name = :name WHERE account = :account AND name = :old")
|
||||||
int renameFolder(long account, String old, String name);
|
int renameFolder(long account, String old, String name);
|
||||||
|
|
|
@ -60,7 +60,7 @@ public class EntityFolder implements Serializable {
|
||||||
public String type;
|
public String type;
|
||||||
@NonNull
|
@NonNull
|
||||||
public Boolean synchronize;
|
public Boolean synchronize;
|
||||||
public Integer poll_interval;
|
public Integer poll_interval; // obsolete
|
||||||
@NonNull
|
@NonNull
|
||||||
public Integer after; // days
|
public Integer after; // days
|
||||||
public String display;
|
public String display;
|
||||||
|
|
|
@ -44,7 +44,6 @@ import javax.mail.Session;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.constraintlayout.widget.Group;
|
|
||||||
import androidx.lifecycle.Observer;
|
import androidx.lifecycle.Observer;
|
||||||
|
|
||||||
public class FragmentFolder extends FragmentEx {
|
public class FragmentFolder extends FragmentEx {
|
||||||
|
@ -55,12 +54,10 @@ public class FragmentFolder extends FragmentEx {
|
||||||
private CheckBox cbSynchronize;
|
private CheckBox cbSynchronize;
|
||||||
private CheckBox cbUnified;
|
private CheckBox cbUnified;
|
||||||
private EditText etAfter;
|
private EditText etAfter;
|
||||||
private EditText etInterval;
|
|
||||||
private Button btnSave;
|
private Button btnSave;
|
||||||
private ImageButton ibDelete;
|
private ImageButton ibDelete;
|
||||||
private ProgressBar pbSave;
|
private ProgressBar pbSave;
|
||||||
private ProgressBar pbWait;
|
private ProgressBar pbWait;
|
||||||
private Group grpInterval;
|
|
||||||
|
|
||||||
private long id = -1;
|
private long id = -1;
|
||||||
private long account = -1;
|
private long account = -1;
|
||||||
|
@ -89,12 +86,10 @@ public class FragmentFolder extends FragmentEx {
|
||||||
cbSynchronize = view.findViewById(R.id.cbSynchronize);
|
cbSynchronize = view.findViewById(R.id.cbSynchronize);
|
||||||
cbUnified = view.findViewById(R.id.cbUnified);
|
cbUnified = view.findViewById(R.id.cbUnified);
|
||||||
etAfter = view.findViewById(R.id.etAfter);
|
etAfter = view.findViewById(R.id.etAfter);
|
||||||
etInterval = view.findViewById(R.id.etInterval);
|
|
||||||
btnSave = view.findViewById(R.id.btnSave);
|
btnSave = view.findViewById(R.id.btnSave);
|
||||||
ibDelete = view.findViewById(R.id.ibDelete);
|
ibDelete = view.findViewById(R.id.ibDelete);
|
||||||
pbSave = view.findViewById(R.id.pbSave);
|
pbSave = view.findViewById(R.id.pbSave);
|
||||||
pbWait = view.findViewById(R.id.pbWait);
|
pbWait = view.findViewById(R.id.pbWait);
|
||||||
grpInterval = view.findViewById(R.id.grpInterval);
|
|
||||||
|
|
||||||
btnSave.setOnClickListener(new View.OnClickListener() {
|
btnSave.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -113,7 +108,6 @@ public class FragmentFolder extends FragmentEx {
|
||||||
args.putBoolean("unified", cbUnified.isChecked());
|
args.putBoolean("unified", cbUnified.isChecked());
|
||||||
args.putBoolean("synchronize", cbSynchronize.isChecked());
|
args.putBoolean("synchronize", cbSynchronize.isChecked());
|
||||||
args.putString("after", etAfter.getText().toString());
|
args.putString("after", etAfter.getText().toString());
|
||||||
args.putString("interval", etInterval.getText().toString());
|
|
||||||
|
|
||||||
new SimpleTask<Void>() {
|
new SimpleTask<Void>() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -126,12 +120,10 @@ public class FragmentFolder extends FragmentEx {
|
||||||
boolean unified = args.getBoolean("unified");
|
boolean unified = args.getBoolean("unified");
|
||||||
boolean synchronize = args.getBoolean("synchronize");
|
boolean synchronize = args.getBoolean("synchronize");
|
||||||
String after = args.getString("after");
|
String after = args.getString("after");
|
||||||
String interval = args.getString("interval");
|
|
||||||
|
|
||||||
if (TextUtils.isEmpty(display) || display.equals(name))
|
if (TextUtils.isEmpty(display) || display.equals(name))
|
||||||
display = null;
|
display = null;
|
||||||
int days = (TextUtils.isEmpty(after) ? EntityFolder.DEFAULT_USER_SYNC : Integer.parseInt(after));
|
int days = (TextUtils.isEmpty(after) ? EntityFolder.DEFAULT_USER_SYNC : Integer.parseInt(after));
|
||||||
Integer poll_interval = (TextUtils.isEmpty(interval) ? null : Integer.parseInt(interval));
|
|
||||||
|
|
||||||
IMAPStore istore = null;
|
IMAPStore istore = null;
|
||||||
DB db = DB.getInstance(getContext());
|
DB db = DB.getInstance(getContext());
|
||||||
|
@ -165,7 +157,6 @@ public class FragmentFolder extends FragmentEx {
|
||||||
create.unified = unified;
|
create.unified = unified;
|
||||||
create.synchronize = synchronize;
|
create.synchronize = synchronize;
|
||||||
create.after = days;
|
create.after = days;
|
||||||
create.poll_interval = poll_interval;
|
|
||||||
db.folder().insertFolder(create);
|
db.folder().insertFolder(create);
|
||||||
} else {
|
} else {
|
||||||
Log.i(Helper.TAG, "Renaming folder=" + name);
|
Log.i(Helper.TAG, "Renaming folder=" + name);
|
||||||
|
@ -180,7 +171,7 @@ public class FragmentFolder extends FragmentEx {
|
||||||
|
|
||||||
if (folder != null) {
|
if (folder != null) {
|
||||||
Log.i(Helper.TAG, "Updating folder=" + name);
|
Log.i(Helper.TAG, "Updating folder=" + name);
|
||||||
db.folder().setFolderProperties(id, name, display, hide, synchronize, unified, days, poll_interval);
|
db.folder().setFolderProperties(id, name, display, hide, synchronize, unified, days);
|
||||||
if (!synchronize)
|
if (!synchronize)
|
||||||
db.folder().setFolderError(id, null);
|
db.folder().setFolderError(id, null);
|
||||||
}
|
}
|
||||||
|
@ -303,7 +294,6 @@ public class FragmentFolder extends FragmentEx {
|
||||||
ibDelete.setVisibility(View.GONE);
|
ibDelete.setVisibility(View.GONE);
|
||||||
pbSave.setVisibility(View.GONE);
|
pbSave.setVisibility(View.GONE);
|
||||||
pbWait.setVisibility(View.VISIBLE);
|
pbWait.setVisibility(View.VISIBLE);
|
||||||
grpInterval.setVisibility(View.GONE);
|
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
@ -330,7 +320,6 @@ public class FragmentFolder extends FragmentEx {
|
||||||
cbUnified.setChecked(folder == null ? false : folder.unified);
|
cbUnified.setChecked(folder == null ? false : folder.unified);
|
||||||
cbSynchronize.setChecked(folder == null || folder.synchronize);
|
cbSynchronize.setChecked(folder == null || folder.synchronize);
|
||||||
etAfter.setText(Integer.toString(folder == null ? EntityFolder.DEFAULT_USER_SYNC : folder.after));
|
etAfter.setText(Integer.toString(folder == null ? EntityFolder.DEFAULT_USER_SYNC : folder.after));
|
||||||
etInterval.setText(folder == null || folder.poll_interval == null ? null : Integer.toString(folder.poll_interval));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Consider previous save as cancelled
|
// Consider previous save as cancelled
|
||||||
|
@ -341,57 +330,5 @@ public class FragmentFolder extends FragmentEx {
|
||||||
ibDelete.setVisibility(folder == null || !EntityFolder.USER.equals(folder.type) ? View.GONE : View.VISIBLE);
|
ibDelete.setVisibility(folder == null || !EntityFolder.USER.equals(folder.type) ? View.GONE : View.VISIBLE);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Bundle args = new Bundle();
|
|
||||||
args.putLong("id", id);
|
|
||||||
args.putLong("account", account);
|
|
||||||
|
|
||||||
new SimpleTask<Boolean>() {
|
|
||||||
@Override
|
|
||||||
protected Boolean onLoad(Context context, Bundle args) throws Throwable {
|
|
||||||
long fid = args.getLong("id");
|
|
||||||
long aid = args.getLong("account");
|
|
||||||
|
|
||||||
IMAPStore istore = null;
|
|
||||||
DB db = DB.getInstance(getContext());
|
|
||||||
try {
|
|
||||||
db.beginTransaction();
|
|
||||||
|
|
||||||
EntityAccount account;
|
|
||||||
if (fid < 0)
|
|
||||||
account = db.account().getAccount(aid);
|
|
||||||
else {
|
|
||||||
EntityFolder folder = db.folder().getFolder(fid);
|
|
||||||
account = db.account().getAccount(folder.account);
|
|
||||||
}
|
|
||||||
|
|
||||||
db.setTransactionSuccessful();
|
|
||||||
|
|
||||||
Properties props = MessageHelper.getSessionProperties(account.auth_type);
|
|
||||||
Session isession = Session.getInstance(props, null);
|
|
||||||
istore = (IMAPStore) isession.getStore("imaps");
|
|
||||||
istore.connect(account.host, account.port, account.user, account.password);
|
|
||||||
|
|
||||||
return istore.hasCapability("IDLE");
|
|
||||||
} finally {
|
|
||||||
db.endTransaction();
|
|
||||||
|
|
||||||
if (istore != null)
|
|
||||||
istore.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onLoaded(Bundle args, Boolean capIdle) {
|
|
||||||
grpInterval.setVisibility(capIdle ? View.GONE : View.VISIBLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onException(Bundle args, Throwable ex) {
|
|
||||||
grpInterval.setVisibility(View.VISIBLE);
|
|
||||||
if (BuildConfig.DEBUG)
|
|
||||||
Helper.unexpectedError(getContext(), ex);
|
|
||||||
}
|
|
||||||
}.load(this, args);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ package eu.faircode.email;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
|
import android.app.AlarmManager;
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
|
@ -78,9 +79,7 @@ import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.Semaphore;
|
||||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import javax.mail.Address;
|
import javax.mail.Address;
|
||||||
import javax.mail.AuthenticationFailedException;
|
import javax.mail.AuthenticationFailedException;
|
||||||
|
@ -571,7 +570,7 @@ public class ServiceSynchronize extends LifecycleService {
|
||||||
|
|
||||||
final IMAPStore istore = (IMAPStore) isession.getStore("imaps");
|
final IMAPStore istore = (IMAPStore) isession.getStore("imaps");
|
||||||
final Map<EntityFolder, IMAPFolder> folders = new HashMap<>();
|
final Map<EntityFolder, IMAPFolder> folders = new HashMap<>();
|
||||||
List<Thread> pollers = new ArrayList<>();
|
List<Thread> syncs = new ArrayList<>();
|
||||||
List<Thread> idlers = new ArrayList<>();
|
List<Thread> idlers = new ArrayList<>();
|
||||||
try {
|
try {
|
||||||
// Listen for store events
|
// Listen for store events
|
||||||
|
@ -613,7 +612,6 @@ public class ServiceSynchronize extends LifecycleService {
|
||||||
|
|
||||||
// Listen for connection events
|
// Listen for connection events
|
||||||
istore.addConnectionListener(new ConnectionAdapter() {
|
istore.addConnectionListener(new ConnectionAdapter() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void opened(ConnectionEvent e) {
|
public void opened(ConnectionEvent e) {
|
||||||
Log.i(Helper.TAG, account.name + " opened");
|
Log.i(Helper.TAG, account.name + " opened");
|
||||||
|
@ -664,8 +662,8 @@ public class ServiceSynchronize extends LifecycleService {
|
||||||
db.folder().setFolderState(folder.id, "connected");
|
db.folder().setFolderState(folder.id, "connected");
|
||||||
db.folder().setFolderError(folder.id, null);
|
db.folder().setFolderError(folder.id, null);
|
||||||
|
|
||||||
// Keep folder connection alive
|
// Synchronize folder
|
||||||
Thread poller = new Thread(new Runnable() {
|
Thread sync = new Thread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
|
@ -797,46 +795,6 @@ public class ServiceSynchronize extends LifecycleService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!capIdle) {
|
|
||||||
Log.i(Helper.TAG, folder.name + " start polling");
|
|
||||||
|
|
||||||
PowerManager pm = getSystemService(PowerManager.class);
|
|
||||||
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, account.name + "/" + folder.name);
|
|
||||||
|
|
||||||
final Thread pthread = Thread.currentThread();
|
|
||||||
int rate = (folder.poll_interval == null ? 9 : folder.poll_interval);
|
|
||||||
ScheduledThreadPoolExecutor scheduler = new ScheduledThreadPoolExecutor(1);
|
|
||||||
ScheduledFuture future = scheduler.scheduleAtFixedRate(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
Log.i(Helper.TAG, folder.name + " wakeup poll");
|
|
||||||
pthread.interrupt();
|
|
||||||
}
|
|
||||||
}, rate, rate, TimeUnit.MINUTES);
|
|
||||||
|
|
||||||
while (state.running) {
|
|
||||||
try {
|
|
||||||
Thread.sleep(Long.MAX_VALUE);
|
|
||||||
} catch (InterruptedException ex) {
|
|
||||||
Log.w(Helper.TAG, folder.name + " poll " + ex.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
wl.acquire();
|
|
||||||
synchronizeMessages(account, folder, ifolder, state);
|
|
||||||
} catch (Throwable ex) {
|
|
||||||
Log.e(Helper.TAG, folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
|
|
||||||
reportError(account.name, folder.name, ex);
|
|
||||||
|
|
||||||
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex));
|
|
||||||
} finally {
|
|
||||||
wl.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
future.cancel(false);
|
|
||||||
}
|
|
||||||
} catch (Throwable ex) {
|
} catch (Throwable ex) {
|
||||||
Log.e(Helper.TAG, folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
|
Log.e(Helper.TAG, folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
|
||||||
reportError(account.name, folder.name, ex);
|
reportError(account.name, folder.name, ex);
|
||||||
|
@ -844,16 +802,13 @@ public class ServiceSynchronize extends LifecycleService {
|
||||||
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex));
|
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex));
|
||||||
|
|
||||||
state.thread.interrupt();
|
state.thread.interrupt();
|
||||||
} finally {
|
|
||||||
if (!capIdle)
|
|
||||||
Log.i(Helper.TAG, folder.name + " end polling");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, "sync.poller." + folder.id);
|
}, "sync." + folder.id);
|
||||||
poller.start();
|
sync.start();
|
||||||
pollers.add(poller);
|
syncs.add(sync);
|
||||||
|
|
||||||
// Receive folder events
|
// Idle folder
|
||||||
if (capIdle) {
|
if (capIdle) {
|
||||||
Thread idler = new Thread(new Runnable() {
|
Thread idler = new Thread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -876,7 +831,7 @@ public class ServiceSynchronize extends LifecycleService {
|
||||||
Log.i(Helper.TAG, folder.name + " end idle");
|
Log.i(Helper.TAG, folder.name + " end idle");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, "sync.idle." + folder.id);
|
}, "idler." + folder.id);
|
||||||
idler.start();
|
idler.start();
|
||||||
idlers.add(idler);
|
idlers.add(idler);
|
||||||
}
|
}
|
||||||
|
@ -884,6 +839,7 @@ public class ServiceSynchronize extends LifecycleService {
|
||||||
|
|
||||||
backoff = CONNECT_BACKOFF_START;
|
backoff = CONNECT_BACKOFF_START;
|
||||||
|
|
||||||
|
// Process folder actions
|
||||||
BroadcastReceiver processFolder = new BroadcastReceiver() {
|
BroadcastReceiver processFolder = new BroadcastReceiver() {
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, final Intent intent) {
|
public void onReceive(Context context, final Intent intent) {
|
||||||
|
@ -961,52 +917,78 @@ public class ServiceSynchronize extends LifecycleService {
|
||||||
f.addAction(ACTION_SYNCHRONIZE_FOLDER);
|
f.addAction(ACTION_SYNCHRONIZE_FOLDER);
|
||||||
f.addAction(ACTION_PROCESS_OPERATIONS);
|
f.addAction(ACTION_PROCESS_OPERATIONS);
|
||||||
f.addDataType("account/" + account.id);
|
f.addDataType("account/" + account.id);
|
||||||
|
|
||||||
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(ServiceSynchronize.this);
|
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(ServiceSynchronize.this);
|
||||||
lbm.registerReceiver(processFolder, f);
|
lbm.registerReceiver(processFolder, f);
|
||||||
|
|
||||||
try {
|
// Create barrier
|
||||||
PowerManager pm = getSystemService(PowerManager.class);
|
final Semaphore sem = new Semaphore(0);
|
||||||
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, account.name);
|
|
||||||
|
|
||||||
ScheduledThreadPoolExecutor scheduler = new ScheduledThreadPoolExecutor(1);
|
// Keep alive
|
||||||
ScheduledFuture future = scheduler.scheduleAtFixedRate(new Runnable() {
|
final PowerManager pm = getSystemService(PowerManager.class);
|
||||||
@Override
|
final PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "account." + account.id);
|
||||||
public void run() {
|
|
||||||
Log.i(Helper.TAG, account.name + " wakeup check");
|
|
||||||
state.thread.interrupt();
|
|
||||||
}
|
|
||||||
}, account.poll_interval, account.poll_interval, TimeUnit.MINUTES);
|
|
||||||
|
|
||||||
// Keep store alive
|
final AlarmManager am = getSystemService(AlarmManager.class);
|
||||||
while (state.running) {
|
final String id = BuildConfig.APPLICATION_ID + ".POLL." + account.id;
|
||||||
EntityLog.log(this, account.name + " wait=" + account.poll_interval);
|
final PendingIntent pi = PendingIntent.getBroadcast(ServiceSynchronize.this, 0, new Intent(id), 0);
|
||||||
|
|
||||||
|
BroadcastReceiver alive = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
EntityLog.log(context, account.name + " keep alive");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Thread.sleep(Long.MAX_VALUE);
|
|
||||||
} catch (InterruptedException ex) {
|
|
||||||
Log.w(Helper.TAG, account.name + " wait " + ex.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.running) try {
|
|
||||||
wl.acquire();
|
wl.acquire();
|
||||||
|
|
||||||
if (!istore.isConnected())
|
if (!istore.isConnected())
|
||||||
throw new StoreClosedException(istore);
|
throw new StoreClosedException(istore);
|
||||||
|
|
||||||
for (EntityFolder folder : folders.keySet())
|
for (EntityFolder folder : folders.keySet())
|
||||||
if (!folders.get(folder).isOpen())
|
if (capIdle) {
|
||||||
throw new FolderClosedException(folders.get(folder));
|
if (!folders.get(folder).isOpen())
|
||||||
|
throw new FolderClosedException(folders.get(folder));
|
||||||
|
} else
|
||||||
|
synchronizeMessages(account, folder, folders.get(folder), state);
|
||||||
|
} catch (Throwable ex) {
|
||||||
|
Log.e(Helper.TAG, account.name + " " + ex + "\n" + Log.getStackTraceString(ex));
|
||||||
|
reportError(account.name, null, ex);
|
||||||
|
|
||||||
|
db.account().setAccountError(account.id, Helper.formatThrowable(ex));
|
||||||
|
|
||||||
|
sem.release();
|
||||||
} finally {
|
} finally {
|
||||||
wl.release();
|
wl.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reschedule alarm
|
||||||
|
am.setAndAllowWhileIdle(
|
||||||
|
AlarmManager.RTC_WAKEUP,
|
||||||
|
System.currentTimeMillis() + account.poll_interval * 60 * 1000L,
|
||||||
|
pi);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
registerReceiver(alive, new IntentFilter(id));
|
||||||
|
|
||||||
future.cancel(false);
|
// Schedule alarm
|
||||||
|
EntityLog.log(this, account.name + " wait=" + account.poll_interval);
|
||||||
|
am.setAndAllowWhileIdle(
|
||||||
|
AlarmManager.RTC_WAKEUP,
|
||||||
|
System.currentTimeMillis() + account.poll_interval * 60 * 1000L,
|
||||||
|
pi);
|
||||||
|
|
||||||
Log.i(Helper.TAG, account.name + " done running=" + state.running);
|
// Wait for interrupt or exception
|
||||||
|
try {
|
||||||
|
sem.acquire();
|
||||||
|
} catch (InterruptedException ex) {
|
||||||
|
Log.w(Helper.TAG, account.name + " semaphore " + ex.toString());
|
||||||
} finally {
|
} finally {
|
||||||
|
// Cleanup
|
||||||
|
am.cancel(pi);
|
||||||
|
unregisterReceiver(alive);
|
||||||
lbm.unregisterReceiver(processFolder);
|
lbm.unregisterReceiver(processFolder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Log.i(Helper.TAG, account.name + " done running=" + state.running);
|
||||||
} catch (Throwable ex) {
|
} catch (Throwable ex) {
|
||||||
Log.e(Helper.TAG, account.name + " " + ex + "\n" + Log.getStackTraceString(ex));
|
Log.e(Helper.TAG, account.name + " " + ex + "\n" + Log.getStackTraceString(ex));
|
||||||
reportError(account.name, null, ex);
|
reportError(account.name, null, ex);
|
||||||
|
@ -1018,10 +1000,16 @@ public class ServiceSynchronize extends LifecycleService {
|
||||||
for (EntityFolder folder : folders.keySet())
|
for (EntityFolder folder : folders.keySet())
|
||||||
db.folder().setFolderState(folder.id, "closing");
|
db.folder().setFolderState(folder.id, "closing");
|
||||||
|
|
||||||
// Stop pollers
|
// Stop syncs
|
||||||
for (Thread poller : pollers) {
|
for (Thread sync : syncs) {
|
||||||
poller.interrupt();
|
sync.interrupt();
|
||||||
join(poller);
|
join(sync);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop idlers
|
||||||
|
for (Thread idler : idlers) {
|
||||||
|
idler.interrupt();
|
||||||
|
join(idler);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close store
|
// Close store
|
||||||
|
@ -1053,12 +1041,6 @@ public class ServiceSynchronize extends LifecycleService {
|
||||||
for (EntityFolder folder : folders.keySet())
|
for (EntityFolder folder : folders.keySet())
|
||||||
db.folder().setFolderState(folder.id, null);
|
db.folder().setFolderState(folder.id, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop idlers
|
|
||||||
for (Thread idler : idlers) {
|
|
||||||
idler.interrupt();
|
|
||||||
join(idler);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.running) {
|
if (state.running) {
|
||||||
|
|
|
@ -102,26 +102,6 @@
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/tvAfter" />
|
app:layout_constraintTop_toBottomOf="@id/tvAfter" />
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/tvInterval"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:text="@string/title_poll_interval"
|
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/etAfter" />
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/etInterval"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:hint="9"
|
|
||||||
android:inputType="number"
|
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/tvInterval" />
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/btnSave"
|
android:id="@+id/btnSave"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
@ -129,7 +109,7 @@
|
||||||
android:layout_marginTop="12dp"
|
android:layout_marginTop="12dp"
|
||||||
android:text="@string/title_save"
|
android:text="@string/title_save"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/etInterval" />
|
app:layout_constraintTop_toBottomOf="@id/etAfter" />
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/ibDelete"
|
android:id="@+id/ibDelete"
|
||||||
|
@ -138,7 +118,7 @@
|
||||||
android:layout_marginTop="12dp"
|
android:layout_marginTop="12dp"
|
||||||
android:src="@drawable/baseline_delete_24"
|
android:src="@drawable/baseline_delete_24"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/etInterval" />
|
app:layout_constraintTop_toBottomOf="@id/etAfter" />
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/pbSave"
|
android:id="@+id/pbSave"
|
||||||
|
@ -161,11 +141,5 @@
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.Group
|
|
||||||
android:id="@+id/grpInterval"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
app:constraint_referenced_ids="tvInterval,etInterval" />
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
Loading…
Reference in New Issue