Synchronize on demand

This commit is contained in:
M66B 2019-02-17 18:04:22 +00:00
parent c4208d1a61
commit 7b55d4e0fc
7 changed files with 137 additions and 51 deletions

2
FAQ.md
View File

@ -28,7 +28,7 @@ For authorizing:
## Planned features
* Synchronize on demand
* ~~Synchronize on demand~~
Anything on this list is in random order and *might* be added in the near future.

View File

@ -38,8 +38,6 @@ import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.google.android.material.snackbar.Snackbar;
import java.util.ArrayList;
import java.util.List;
@ -236,7 +234,8 @@ public class AdapterFolder extends RecyclerView.Adapter<AdapterFolder.ViewHolder
PopupMenu popupMenu = new PopupMenu(context, itemView);
popupMenu.getMenu().add(Menu.NONE, action_synchronize_now, 1, R.string.title_synchronize_now);
popupMenu.getMenu().add(Menu.NONE, action_synchronize_now, 1, R.string.title_synchronize_now)
.setEnabled(folder.account != null || "connected".equals(folder.state) /* outbox */);
if (folder.account != null)
popupMenu.getMenu().add(Menu.NONE, action_delete_local, 2, R.string.title_delete_local);
@ -283,25 +282,33 @@ public class AdapterFolder extends RecyclerView.Adapter<AdapterFolder.ViewHolder
args.putLong("account", folder.account == null ? -1 : folder.account);
args.putLong("folder", folder.id);
new SimpleTask<Boolean>() {
new SimpleTask<Void>() {
@Override
protected Boolean onExecute(Context context, Bundle args) {
protected Void onExecute(Context context, Bundle args) {
long aid = args.getLong("account");
long fid = args.getLong("folder");
DB db = DB.getInstance(context);
EntityOperation.sync(db, fid);
try {
db.beginTransaction();
if (aid < 0) // outbox
return "connected".equals(db.folder().getFolder(fid).state);
else
return "connected".equals(db.account().getAccount(aid).state);
}
if (aid < 0) // outbox
EntityOperation.sync(db, fid);
else {
if ("connected".equals(db.account().getAccount(aid).state))
EntityOperation.sync(db, fid);
else {
db.folder().setFolderSyncState(folder.id, "requested");
ServiceSynchronize.sync(context, fid);
}
}
@Override
protected void onExecuted(Bundle args, Boolean connected) {
if (!connected)
Snackbar.make(itemView, R.string.title_sync_queued, Snackbar.LENGTH_LONG).show();
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
return null;
}
@Override

View File

@ -29,6 +29,7 @@ import java.text.Collator;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Locale;
@ -162,6 +163,17 @@ public class EntityFolder implements Serializable {
public EntityFolder() {
}
long getSyncDays() {
int days = sync_days;
if (last_sync != null) {
int ago_days = (int) ((new Date().getTime() - last_sync) / (24 * 3600 * 1000L)) + 1;
if (ago_days > days)
days = ago_days;
}
return (initialize ? Math.min(DEFAULT_INIT, keep_days) : days);
}
static int getIcon(String type) {
if (EntityFolder.INBOX.equals(type))
return R.drawable.baseline_move_to_inbox_24;

View File

@ -102,15 +102,8 @@ public class EntityOperation {
EntityFolder folder = db.folder().getFolder(fid);
int sync_days = folder.sync_days;
if (folder.last_sync != null) {
int ago_days = (int) ((new Date().getTime() - folder.last_sync) / (24 * 3600 * 1000L)) + 1;
if (ago_days > sync_days)
sync_days = ago_days;
}
JSONArray jargs = new JSONArray();
jargs.put(folder.initialize ? Math.min(EntityFolder.DEFAULT_INIT, folder.keep_days) : sync_days);
jargs.put(folder.getSyncDays());
jargs.put(folder.keep_days);
jargs.put(folder.download);

View File

@ -487,22 +487,10 @@ public class FragmentMessages extends FragmentBase {
args.putLong("folder", folder);
new SimpleTask<Boolean>() {
@Override
protected void onPreExecute(Bundle args) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
boolean enabled = prefs.getBoolean("enabled", true);
if (!enabled) {
prefs.edit().putBoolean("enabled", true).apply();
ServiceSynchronize.reload(getContext(), "refresh");
}
}
@Override
protected Boolean onExecute(Context context, Bundle args) {
long fid = args.getLong("folder");
boolean connected = false;
DB db = DB.getInstance(context);
try {
db.beginTransaction();
@ -518,33 +506,34 @@ public class FragmentMessages extends FragmentBase {
folders.add(folder);
}
for (EntityFolder folder : folders) {
EntityOperation.sync(db, folder.id);
boolean now = false;
for (EntityFolder folder : folders)
if (folder.account == null) { // outbox
if ("connected".equals(folder.state))
connected = true;
now = ("connected".equals(folder.state));
EntityOperation.sync(db, folder.id);
} else {
now = true;
EntityAccount account = db.account().getAccount(folder.account);
if ("connected".equals(account.state))
connected = true;
EntityOperation.sync(db, folder.id);
else {
db.folder().setFolderSyncState(folder.id, "requested");
ServiceSynchronize.sync(context, folder.id);
}
}
}
db.setTransactionSuccessful();
return now;
} finally {
db.endTransaction();
}
return connected;
}
@Override
protected void onExecuted(Bundle args, Boolean connected) {
if (!connected) {
protected void onExecuted(Bundle args, Boolean now) {
if (!now)
swipeRefresh.setRefreshing(false);
Snackbar.make(view, R.string.title_sync_queued, Snackbar.LENGTH_LONG).show();
}
}
@Override

View File

@ -343,6 +343,15 @@ public class ServiceSynchronize extends LifecycleService {
serviceManager.service_reload(intent.getStringExtra("reason"));
break;
case "synchronize":
executor.submit(new Runnable() {
@Override
public void run() {
synchronizeOnDemand(Long.parseLong(parts[1]));
}
});
break;
case "summary":
case "clear":
case "seen":
@ -2210,6 +2219,77 @@ public class ServiceSynchronize extends LifecycleService {
}
}
private void synchronizeOnDemand(long fid) {
Log.i("Synchronize on demand folder=" + fid);
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wlAccount = pm.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, BuildConfig.APPLICATION_ID + ":sync." + fid);
DB db = DB.getInstance(this);
EntityFolder folder = null;
EntityAccount account = null;
Store istore = null;
try {
wlAccount.acquire();
folder = db.folder().getFolder(fid);
account = db.account().getAccount(folder.account);
// Create session
Properties props = MessageHelper.getSessionProperties(account.auth_type, account.realm, account.insecure);
final Session isession = Session.getInstance(props, null);
isession.setDebug(true);
// Connect account
Log.i(account.name + " connecting");
db.account().setAccountState(account.id, "connecting");
istore = isession.getStore(account.getProtocol());
Helper.connect(this, istore, account);
db.account().setAccountState(account.id, "connected");
Log.i(account.name + " connected");
// Connect folder
Log.i(folder.name + " connecting");
db.folder().setFolderState(folder.id, "connecting");
Folder ifolder = istore.getFolder(folder.name);
ifolder.open(Folder.READ_WRITE);
db.folder().setFolderState(folder.id, "connected");
db.folder().setFolderError(folder.id, null);
Log.i(folder.name + " connected");
// Synchronize messages
JSONArray jargs = new JSONArray();
jargs.put(folder.getSyncDays());
jargs.put(folder.keep_days);
jargs.put(folder.download);
synchronizeMessages(account, folder, (IMAPFolder) ifolder, jargs, new ServiceState());
} catch (Throwable ex) {
db.account().setAccountError(account.id, Helper.formatThrowable(ex));
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex));
} finally {
if (istore != null) {
Log.i(account.name + " closing");
db.account().setAccountState(account.id, "closing");
db.folder().setFolderState(folder.id, "closing");
try {
istore.close();
} catch (MessagingException ex) {
Log.e(ex);
}
db.account().setAccountState(account.id, null);
db.folder().setFolderState(folder.id, null);
Log.i(account.name + " closed");
}
wlAccount.release();
}
}
private void synchronizeFolders(EntityAccount account, Store istore, ServiceState state) throws MessagingException {
DB db = DB.getInstance(this);
try {
@ -3273,7 +3353,7 @@ public class ServiceSynchronize extends LifecycleService {
try {
wl.acquire();
EntityLog.log(ServiceSynchronize.this, "Reload " +
EntityLog.log(ServiceSynchronize.this, "Reload" +
" stop=" + doStop + " start=" + doStart + " queued=" + queued + " " + reason);
if (doStop)
@ -3339,6 +3419,12 @@ public class ServiceSynchronize extends LifecycleService {
.putExtra("reason", reason));
}
public static void sync(Context context, long folder) {
ContextCompat.startForegroundService(context,
new Intent(context, ServiceSynchronize.class)
.setAction("synchronize:" + folder));
}
private class ServiceState {
private Thread thread;
private Semaphore semaphore = new Semaphore(0);

View File

@ -353,7 +353,6 @@
<string name="title_ask_show_html">Showing the original message can leak privacy sensitive information</string>
<string name="title_ask_show_image">Showing images can leak privacy sensitive information</string>
<string name="title_ask_edit_ref">Edit reformatted replied/forwarded message text?</string>
<string name="title_sync_queued">Synchronization will take place on next account connection</string>
<string name="title_fix">Fix</string>
<string name="title_compose">Compose</string>