mirror of
https://github.com/M66B/FairEmail.git
synced 2026-01-04 11:54:10 +01:00
Added setting to poll instead of synchronize
This commit is contained in:
9
FAQ.md
9
FAQ.md
@@ -323,18 +323,19 @@ If you are using a VPN, the VPN provider might block the connection because it i
|
||||
<a name="faq23"></a>
|
||||
**(23) Why do I get 'Too many simultaneous connections' ?**
|
||||
|
||||
The message *Too many simultaneous connections* is sent by the email server when there are too many connections to the same email account at the same time.
|
||||
The message *Too many simultaneous connections* is sent by the email server
|
||||
when there are too many folder connections for the same email account at the same time.
|
||||
|
||||
Possible causes are:
|
||||
|
||||
* There are multiple email clients connected to the same account
|
||||
* The same email client is connected multiple times to the same account
|
||||
* The previous connection was terminated abruptly for example by losing internet connectivity
|
||||
* The previous connection was terminated abruptly for example by abruptly losing internet connectivity, for example when turning on flight mode
|
||||
|
||||
If only FairEmail is connecting to the email server, first try to wait half an hour to see if the problem resolves itself,
|
||||
else try to reduce the number of folders to synchronize.
|
||||
else enable the folder settings '*Poll instead of synchronize*' for some folders.
|
||||
|
||||
The maximum number of simultaneous connections for Gmail is 15,
|
||||
The maximum number of simultaneous folder connections for Gmail is 15,
|
||||
so you can synchronize at most 15 folders simultaneously on *all* your devices at the same time.
|
||||
See [here](https://support.google.com/mail/answer/7126229) for details.
|
||||
|
||||
|
||||
1180
app/schemas/eu.faircode.email.DB/16.json
Normal file
1180
app/schemas/eu.faircode.email.DB/16.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -46,7 +46,7 @@ import io.requery.android.database.sqlite.RequerySQLiteOpenHelperFactory;
|
||||
// https://developer.android.com/topic/libraries/architecture/room.html
|
||||
|
||||
@Database(
|
||||
version = 15,
|
||||
version = 16,
|
||||
entities = {
|
||||
EntityIdentity.class,
|
||||
EntityAccount.class,
|
||||
@@ -241,6 +241,13 @@ public abstract class DB extends RoomDatabase {
|
||||
db.execSQL("ALTER TABLE `folder` ADD COLUMN `sync_state` TEXT");
|
||||
}
|
||||
})
|
||||
.addMigrations(new Migration(15, 16) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase db) {
|
||||
Log.i(Helper.TAG, "DB migration from version " + startVersion + " to " + endVersion);
|
||||
db.execSQL("ALTER TABLE `folder` ADD COLUMN `poll` INTEGER NOT NULL DEFAULT 0");
|
||||
}
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -149,6 +149,7 @@ public interface DaoFolder {
|
||||
", display = :display" +
|
||||
", hide = :hide" +
|
||||
", synchronize = :synchronize" +
|
||||
", poll = :poll" +
|
||||
", unified = :unified" +
|
||||
", `sync_days` = :sync_days" +
|
||||
", `keep_days` = :keep_days" +
|
||||
@@ -158,6 +159,7 @@ public interface DaoFolder {
|
||||
String name, String display,
|
||||
boolean hide,
|
||||
boolean synchronize,
|
||||
boolean poll,
|
||||
boolean unified,
|
||||
int sync_days, int keep_days);
|
||||
|
||||
|
||||
@@ -69,6 +69,8 @@ public class EntityFolder implements Serializable {
|
||||
@NonNull
|
||||
public Boolean synchronize;
|
||||
@NonNull
|
||||
public Boolean poll = false;
|
||||
@NonNull
|
||||
public Integer sync_days;
|
||||
@NonNull
|
||||
public Integer keep_days;
|
||||
@@ -168,6 +170,9 @@ public class EntityFolder implements Serializable {
|
||||
this.type.equals(other.type) &&
|
||||
this.level.equals(other.level) &&
|
||||
this.synchronize.equals(other.synchronize) &&
|
||||
this.poll.equals(other.poll) &&
|
||||
this.sync_days.equals(other.sync_days) &&
|
||||
this.keep_days.equals(other.keep_days) &&
|
||||
(this.display == null ? other.display == null : this.display.equals(other.display)) &&
|
||||
this.hide == other.hide &&
|
||||
this.unified == other.unified &&
|
||||
@@ -189,6 +194,7 @@ public class EntityFolder implements Serializable {
|
||||
json.put("type", type);
|
||||
json.put("level", level);
|
||||
json.put("synchronize", synchronize);
|
||||
json.put("poll", poll);
|
||||
json.put("sync_days", sync_days);
|
||||
json.put("keep_days", keep_days);
|
||||
json.put("display", display);
|
||||
@@ -208,6 +214,10 @@ public class EntityFolder implements Serializable {
|
||||
folder.level = 0;
|
||||
|
||||
folder.synchronize = json.getBoolean("synchronize");
|
||||
if (json.has("poll"))
|
||||
folder.poll = json.getBoolean("poll");
|
||||
else
|
||||
folder.poll = false;
|
||||
|
||||
if (json.has("after"))
|
||||
folder.sync_days = json.getInt("after");
|
||||
|
||||
@@ -29,6 +29,7 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ProgressBar;
|
||||
@@ -52,6 +53,7 @@ public class FragmentFolder extends FragmentEx {
|
||||
private EditText etDisplay;
|
||||
private CheckBox cbHide;
|
||||
private CheckBox cbSynchronize;
|
||||
private CheckBox cbPoll;
|
||||
private CheckBox cbUnified;
|
||||
private EditText etSyncDays;
|
||||
private EditText etKeepDays;
|
||||
@@ -85,6 +87,7 @@ public class FragmentFolder extends FragmentEx {
|
||||
etDisplay = view.findViewById(R.id.etDisplay);
|
||||
cbHide = view.findViewById(R.id.cbHide);
|
||||
cbSynchronize = view.findViewById(R.id.cbSynchronize);
|
||||
cbPoll = view.findViewById(R.id.cbPoll);
|
||||
cbUnified = view.findViewById(R.id.cbUnified);
|
||||
etSyncDays = view.findViewById(R.id.etSyncDays);
|
||||
etKeepDays = view.findViewById(R.id.etKeepDays);
|
||||
@@ -93,6 +96,13 @@ public class FragmentFolder extends FragmentEx {
|
||||
pbSave = view.findViewById(R.id.pbSave);
|
||||
pbWait = view.findViewById(R.id.pbWait);
|
||||
|
||||
cbSynchronize.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
cbPoll.setEnabled(isChecked);
|
||||
}
|
||||
});
|
||||
|
||||
btnSave.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
@@ -109,6 +119,7 @@ public class FragmentFolder extends FragmentEx {
|
||||
args.putBoolean("hide", cbHide.isChecked());
|
||||
args.putBoolean("unified", cbUnified.isChecked());
|
||||
args.putBoolean("synchronize", cbSynchronize.isChecked());
|
||||
args.putBoolean("poll", cbPoll.isChecked());
|
||||
args.putString("sync", etSyncDays.getText().toString());
|
||||
args.putString("keep", etKeepDays.getText().toString());
|
||||
|
||||
@@ -122,6 +133,7 @@ public class FragmentFolder extends FragmentEx {
|
||||
boolean hide = args.getBoolean("hide");
|
||||
boolean unified = args.getBoolean("unified");
|
||||
boolean synchronize = args.getBoolean("synchronize");
|
||||
boolean poll = args.getBoolean("poll");
|
||||
String sync = args.getString("sync");
|
||||
String keep = args.getString("keep");
|
||||
|
||||
@@ -132,7 +144,8 @@ public class FragmentFolder extends FragmentEx {
|
||||
if (keep_days < sync_days)
|
||||
keep_days = sync_days;
|
||||
|
||||
EntityFolder folder = null;
|
||||
boolean reload = false;
|
||||
EntityFolder folder;
|
||||
|
||||
IMAPStore istore = null;
|
||||
DB db = DB.getInstance(getContext());
|
||||
@@ -167,6 +180,7 @@ public class FragmentFolder extends FragmentEx {
|
||||
create.type = EntityFolder.USER;
|
||||
create.unified = unified;
|
||||
create.synchronize = synchronize;
|
||||
create.poll = poll;
|
||||
create.sync_days = sync_days;
|
||||
create.keep_days = keep_days;
|
||||
db.folder().insertFolder(create);
|
||||
@@ -182,6 +196,10 @@ public class FragmentFolder extends FragmentEx {
|
||||
}
|
||||
|
||||
if (folder != null) {
|
||||
reload = (!folder.name.equals(name) ||
|
||||
!folder.synchronize.equals(synchronize) ||
|
||||
!folder.poll.equals(poll));
|
||||
|
||||
Calendar cal_keep = Calendar.getInstance();
|
||||
cal_keep.add(Calendar.DAY_OF_MONTH, -keep_days);
|
||||
cal_keep.set(Calendar.HOUR_OF_DAY, 0);
|
||||
@@ -194,7 +212,12 @@ public class FragmentFolder extends FragmentEx {
|
||||
keep_time = 0;
|
||||
|
||||
Log.i(Helper.TAG, "Updating folder=" + name);
|
||||
db.folder().setFolderProperties(id, name, display, hide, synchronize, unified, sync_days, keep_days);
|
||||
db.folder().setFolderProperties(id,
|
||||
name, display,
|
||||
hide,
|
||||
synchronize, poll,
|
||||
unified,
|
||||
sync_days, keep_days);
|
||||
|
||||
db.message().deleteMessagesBefore(id, keep_time, true);
|
||||
|
||||
@@ -210,7 +233,7 @@ public class FragmentFolder extends FragmentEx {
|
||||
istore.close();
|
||||
}
|
||||
|
||||
if (folder == null || !folder.name.equals(name))
|
||||
if (folder == null || !folder.name.equals(name) || reload)
|
||||
ServiceSynchronize.reload(getContext(), "save folder");
|
||||
else
|
||||
EntityOperation.sync(db, folder.id);
|
||||
@@ -350,6 +373,7 @@ public class FragmentFolder extends FragmentEx {
|
||||
cbHide.setChecked(folder == null ? false : folder.hide);
|
||||
cbUnified.setChecked(folder == null ? false : folder.unified);
|
||||
cbSynchronize.setChecked(folder == null || folder.synchronize);
|
||||
cbPoll.setChecked(folder == null ? false : folder.poll);
|
||||
etSyncDays.setText(Integer.toString(folder == null ? EntityFolder.DEFAULT_USER_SYNC : folder.sync_days));
|
||||
etKeepDays.setText(Integer.toString(folder == null ? EntityFolder.DEFAULT_USER_SYNC : folder.keep_days));
|
||||
}
|
||||
@@ -358,6 +382,7 @@ public class FragmentFolder extends FragmentEx {
|
||||
pbWait.setVisibility(View.GONE);
|
||||
Helper.setViewsEnabled(view, true);
|
||||
etRename.setEnabled(folder == null || EntityFolder.USER.equals(folder.type));
|
||||
cbPoll.setEnabled(cbSynchronize.isChecked());
|
||||
btnSave.setEnabled(true);
|
||||
ibDelete.setVisibility(folder == null || !EntityFolder.USER.equals(folder.type) ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
|
||||
@@ -886,53 +886,134 @@ public class ServiceSynchronize extends LifecycleService {
|
||||
// Update folder list
|
||||
synchronizeFolders(account, istore, state);
|
||||
|
||||
// Open folders
|
||||
for (final EntityFolder folder : db.folder().getFolders(account.id, true)) {
|
||||
Log.i(Helper.TAG, account.name + " sync folder " + folder.name);
|
||||
// Open synchronizing folders
|
||||
for (final EntityFolder folder : db.folder().getFolders(account.id)) {
|
||||
if (folder.synchronize && !folder.poll && capIdle) {
|
||||
Log.i(Helper.TAG, account.name + " sync folder " + folder.name);
|
||||
|
||||
db.folder().setFolderState(folder.id, "connecting");
|
||||
db.folder().setFolderState(folder.id, "connecting");
|
||||
|
||||
final IMAPFolder ifolder = (IMAPFolder) istore.getFolder(folder.name);
|
||||
try {
|
||||
ifolder.open(Folder.READ_WRITE);
|
||||
} catch (Throwable ex) {
|
||||
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex));
|
||||
throw ex;
|
||||
}
|
||||
folders.put(folder, ifolder);
|
||||
final IMAPFolder ifolder = (IMAPFolder) istore.getFolder(folder.name);
|
||||
try {
|
||||
ifolder.open(Folder.READ_WRITE);
|
||||
} catch (Throwable ex) {
|
||||
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex));
|
||||
throw ex;
|
||||
}
|
||||
folders.put(folder, ifolder);
|
||||
|
||||
db.folder().setFolderState(folder.id, "connected");
|
||||
db.folder().setFolderError(folder.id, null);
|
||||
db.folder().setFolderState(folder.id, "connected");
|
||||
db.folder().setFolderError(folder.id, null);
|
||||
|
||||
Log.i(Helper.TAG, account.name + " folder " + folder.name + " flags=" + ifolder.getPermanentFlags());
|
||||
Log.i(Helper.TAG, account.name + " folder " + folder.name + " flags=" + ifolder.getPermanentFlags());
|
||||
|
||||
// Listen for new and deleted messages
|
||||
ifolder.addMessageCountListener(new MessageCountAdapter() {
|
||||
@Override
|
||||
public void messagesAdded(MessageCountEvent e) {
|
||||
try {
|
||||
wlAccount.acquire();
|
||||
Log.i(Helper.TAG, folder.name + " messages added");
|
||||
// Listen for new and deleted messages
|
||||
ifolder.addMessageCountListener(new MessageCountAdapter() {
|
||||
@Override
|
||||
public void messagesAdded(MessageCountEvent e) {
|
||||
try {
|
||||
wlAccount.acquire();
|
||||
Log.i(Helper.TAG, folder.name + " messages added");
|
||||
|
||||
FetchProfile fp = new FetchProfile();
|
||||
fp.add(FetchProfile.Item.ENVELOPE);
|
||||
fp.add(FetchProfile.Item.FLAGS);
|
||||
fp.add(FetchProfile.Item.CONTENT_INFO); // body structure
|
||||
fp.add(UIDFolder.FetchProfileItem.UID);
|
||||
fp.add(IMAPFolder.FetchProfileItem.HEADERS);
|
||||
fp.add(IMAPFolder.FetchProfileItem.MESSAGE);
|
||||
fp.add(FetchProfile.Item.SIZE);
|
||||
fp.add(IMAPFolder.FetchProfileItem.INTERNALDATE);
|
||||
ifolder.fetch(e.getMessages(), fp);
|
||||
FetchProfile fp = new FetchProfile();
|
||||
fp.add(FetchProfile.Item.ENVELOPE);
|
||||
fp.add(FetchProfile.Item.FLAGS);
|
||||
fp.add(FetchProfile.Item.CONTENT_INFO); // body structure
|
||||
fp.add(UIDFolder.FetchProfileItem.UID);
|
||||
fp.add(IMAPFolder.FetchProfileItem.HEADERS);
|
||||
fp.add(IMAPFolder.FetchProfileItem.MESSAGE);
|
||||
fp.add(FetchProfile.Item.SIZE);
|
||||
fp.add(IMAPFolder.FetchProfileItem.INTERNALDATE);
|
||||
ifolder.fetch(e.getMessages(), fp);
|
||||
|
||||
for (Message imessage : e.getMessages())
|
||||
for (Message imessage : e.getMessages())
|
||||
try {
|
||||
long id;
|
||||
try {
|
||||
db.beginTransaction();
|
||||
id = synchronizeMessage(
|
||||
ServiceSynchronize.this,
|
||||
folder, ifolder, (IMAPMessage) imessage,
|
||||
false, false, false);
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
|
||||
try {
|
||||
db.beginTransaction();
|
||||
downloadMessage(ServiceSynchronize.this, folder, ifolder, (IMAPMessage) imessage, id);
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
} catch (MessageRemovedException ex) {
|
||||
Log.w(Helper.TAG, folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
|
||||
} catch (IOException ex) {
|
||||
if (ex.getCause() instanceof MessageRemovedException)
|
||||
Log.w(Helper.TAG, folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
|
||||
else
|
||||
throw ex;
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
Log.e(Helper.TAG, folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
|
||||
reportError(account, folder, ex);
|
||||
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex));
|
||||
state.error();
|
||||
} finally {
|
||||
wlAccount.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void messagesRemoved(MessageCountEvent e) {
|
||||
try {
|
||||
wlAccount.acquire();
|
||||
Log.i(Helper.TAG, folder.name + " messages removed");
|
||||
for (Message imessage : e.getMessages())
|
||||
try {
|
||||
long uid = ifolder.getUID(imessage);
|
||||
|
||||
DB db = DB.getInstance(ServiceSynchronize.this);
|
||||
int count = db.message().deleteMessage(folder.id, uid);
|
||||
|
||||
Log.i(Helper.TAG, "Deleted uid=" + uid + " count=" + count);
|
||||
} catch (MessageRemovedException ex) {
|
||||
Log.w(Helper.TAG, folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
Log.e(Helper.TAG, folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
|
||||
reportError(account, folder, ex);
|
||||
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex));
|
||||
state.error();
|
||||
} finally {
|
||||
wlAccount.release();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Flags (like "seen") at the remote could be changed while synchronizing
|
||||
|
||||
// Listen for changed messages
|
||||
ifolder.addMessageChangedListener(new MessageChangedListener() {
|
||||
@Override
|
||||
public void messageChanged(MessageChangedEvent e) {
|
||||
try {
|
||||
wlAccount.acquire();
|
||||
try {
|
||||
Log.i(Helper.TAG, folder.name + " message changed");
|
||||
|
||||
FetchProfile fp = new FetchProfile();
|
||||
fp.add(UIDFolder.FetchProfileItem.UID);
|
||||
fp.add(IMAPFolder.FetchProfileItem.FLAGS);
|
||||
ifolder.fetch(new Message[]{e.getMessage()}, fp);
|
||||
|
||||
long id;
|
||||
try {
|
||||
db.beginTransaction();
|
||||
id = synchronizeMessage(
|
||||
ServiceSynchronize.this,
|
||||
folder, ifolder, (IMAPMessage) imessage,
|
||||
folder, ifolder, (IMAPMessage) e.getMessage(),
|
||||
false, false, false);
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
@@ -941,7 +1022,7 @@ public class ServiceSynchronize extends LifecycleService {
|
||||
|
||||
try {
|
||||
db.beginTransaction();
|
||||
downloadMessage(ServiceSynchronize.this, folder, ifolder, (IMAPMessage) imessage, id);
|
||||
downloadMessage(ServiceSynchronize.this, folder, ifolder, (IMAPMessage) e.getMessage(), id);
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
@@ -954,99 +1035,18 @@ public class ServiceSynchronize extends LifecycleService {
|
||||
else
|
||||
throw ex;
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
Log.e(Helper.TAG, folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
|
||||
reportError(account, folder, ex);
|
||||
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex));
|
||||
state.error();
|
||||
} finally {
|
||||
wlAccount.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void messagesRemoved(MessageCountEvent e) {
|
||||
try {
|
||||
wlAccount.acquire();
|
||||
Log.i(Helper.TAG, folder.name + " messages removed");
|
||||
for (Message imessage : e.getMessages())
|
||||
try {
|
||||
long uid = ifolder.getUID(imessage);
|
||||
|
||||
DB db = DB.getInstance(ServiceSynchronize.this);
|
||||
int count = db.message().deleteMessage(folder.id, uid);
|
||||
|
||||
Log.i(Helper.TAG, "Deleted uid=" + uid + " count=" + count);
|
||||
} catch (MessageRemovedException ex) {
|
||||
Log.w(Helper.TAG, folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
Log.e(Helper.TAG, folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
|
||||
reportError(account, folder, ex);
|
||||
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex));
|
||||
state.error();
|
||||
} finally {
|
||||
wlAccount.release();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Flags (like "seen") at the remote could be changed while synchronizing
|
||||
|
||||
// Listen for changed messages
|
||||
ifolder.addMessageChangedListener(new MessageChangedListener() {
|
||||
@Override
|
||||
public void messageChanged(MessageChangedEvent e) {
|
||||
try {
|
||||
wlAccount.acquire();
|
||||
try {
|
||||
Log.i(Helper.TAG, folder.name + " message changed");
|
||||
|
||||
FetchProfile fp = new FetchProfile();
|
||||
fp.add(UIDFolder.FetchProfileItem.UID);
|
||||
fp.add(IMAPFolder.FetchProfileItem.FLAGS);
|
||||
ifolder.fetch(new Message[]{e.getMessage()}, fp);
|
||||
|
||||
long id;
|
||||
try {
|
||||
db.beginTransaction();
|
||||
id = synchronizeMessage(
|
||||
ServiceSynchronize.this,
|
||||
folder, ifolder, (IMAPMessage) e.getMessage(),
|
||||
false, false, false);
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
|
||||
try {
|
||||
db.beginTransaction();
|
||||
downloadMessage(ServiceSynchronize.this, folder, ifolder, (IMAPMessage) e.getMessage(), id);
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
} catch (MessageRemovedException ex) {
|
||||
Log.w(Helper.TAG, folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
|
||||
} catch (IOException ex) {
|
||||
if (ex.getCause() instanceof MessageRemovedException)
|
||||
Log.w(Helper.TAG, folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
|
||||
else
|
||||
throw ex;
|
||||
} catch (Throwable ex) {
|
||||
Log.e(Helper.TAG, folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
|
||||
reportError(account, folder, ex);
|
||||
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex));
|
||||
state.error();
|
||||
} finally {
|
||||
wlAccount.release();
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
Log.e(Helper.TAG, folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
|
||||
reportError(account, folder, ex);
|
||||
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex));
|
||||
state.error();
|
||||
} finally {
|
||||
wlAccount.release();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Idle folder
|
||||
if (capIdle) {
|
||||
// Idle folder
|
||||
Thread idler = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -1068,13 +1068,12 @@ public class ServiceSynchronize extends LifecycleService {
|
||||
}, "idler." + folder.id);
|
||||
idler.start();
|
||||
idlers.add(idler);
|
||||
}
|
||||
|
||||
EntityOperation.sync(db, folder.id);
|
||||
}
|
||||
EntityOperation.sync(db, folder.id);
|
||||
} else
|
||||
folders.put(folder, null);
|
||||
|
||||
// Observe folder operations
|
||||
for (final EntityFolder folder : db.folder().getFolders(account.id)) {
|
||||
// Observe operations
|
||||
Handler handler = new Handler(getMainLooper()) {
|
||||
private List<Long> handling = new ArrayList<>();
|
||||
private final PowerManager.WakeLock wlFolder = pm.newWakeLock(
|
||||
@@ -1109,55 +1108,50 @@ public class ServiceSynchronize extends LifecycleService {
|
||||
Log.i(Helper.TAG, folder.name + " process");
|
||||
|
||||
// Get folder
|
||||
EntityFolder ofolder = null;
|
||||
IMAPFolder ifolder = null;
|
||||
for (EntityFolder f : folders.keySet())
|
||||
if (f.id.equals(folder.id)) {
|
||||
ofolder = f;
|
||||
ifolder = folders.get(f);
|
||||
ifolder = folders.get(f); // null when polling
|
||||
break;
|
||||
}
|
||||
|
||||
final boolean shouldClose = (ofolder == null);
|
||||
final boolean shouldClose = (ifolder == null);
|
||||
|
||||
try {
|
||||
if (ofolder == null)
|
||||
ofolder = db.folder().getFolder(folder.id);
|
||||
|
||||
Log.i(Helper.TAG, ofolder.name + " run " + (shouldClose ? "offline" : "online"));
|
||||
Log.i(Helper.TAG, folder.name + " run " + (shouldClose ? "offline" : "online"));
|
||||
|
||||
if (ifolder == null) {
|
||||
// Prevent unnecessary folder connections
|
||||
if (db.operation().getOperationCount(ofolder.id, null) == 0)
|
||||
if (db.operation().getOperationCount(folder.id, null) == 0)
|
||||
return;
|
||||
|
||||
db.folder().setFolderState(ofolder.id, "connecting");
|
||||
db.folder().setFolderState(folder.id, "connecting");
|
||||
|
||||
ifolder = (IMAPFolder) istore.getFolder(ofolder.name);
|
||||
ifolder = (IMAPFolder) istore.getFolder(folder.name);
|
||||
ifolder.open(Folder.READ_WRITE);
|
||||
|
||||
db.folder().setFolderState(ofolder.id, "connected");
|
||||
db.folder().setFolderError(ofolder.id, null);
|
||||
db.folder().setFolderState(folder.id, "connected");
|
||||
db.folder().setFolderError(folder.id, null);
|
||||
}
|
||||
|
||||
processOperations(account, ofolder, isession, istore, ifolder, state);
|
||||
processOperations(account, folder, isession, istore, ifolder, state);
|
||||
|
||||
} catch (Throwable ex) {
|
||||
Log.e(Helper.TAG, ofolder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
|
||||
reportError(account, ofolder, ex);
|
||||
db.folder().setFolderError(ofolder.id, Helper.formatThrowable(ex));
|
||||
Log.e(Helper.TAG, folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
|
||||
reportError(account, folder, ex);
|
||||
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex));
|
||||
state.error();
|
||||
} finally {
|
||||
if (shouldClose) {
|
||||
if (ifolder != null && ifolder.isOpen()) {
|
||||
db.folder().setFolderState(ofolder.id, "closing");
|
||||
db.folder().setFolderState(folder.id, "closing");
|
||||
try {
|
||||
ifolder.close(false);
|
||||
} catch (MessagingException ex) {
|
||||
Log.w(Helper.TAG, ofolder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
|
||||
Log.w(Helper.TAG, folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
|
||||
}
|
||||
}
|
||||
db.folder().setFolderState(ofolder.id, null);
|
||||
db.folder().setFolderState(folder.id, null);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
@@ -1170,6 +1164,8 @@ public class ServiceSynchronize extends LifecycleService {
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Start watching for operations
|
||||
handler.sendEmptyMessage(1);
|
||||
handlers.add(handler);
|
||||
}
|
||||
@@ -1197,11 +1193,12 @@ public class ServiceSynchronize extends LifecycleService {
|
||||
throw new StoreClosedException(istore);
|
||||
|
||||
for (EntityFolder folder : folders.keySet())
|
||||
if (capIdle) {
|
||||
if (!folders.get(folder).isOpen())
|
||||
throw new FolderClosedException(folders.get(folder));
|
||||
} else
|
||||
synchronizeMessages(account, folder, folders.get(folder), state);
|
||||
if (folder.synchronize)
|
||||
if (!folder.poll && capIdle) {
|
||||
if (!folders.get(folder).isOpen())
|
||||
throw new FolderClosedException(folders.get(folder));
|
||||
} else
|
||||
EntityOperation.sync(db, folder.id);
|
||||
|
||||
// Successfully connected: reset back off time
|
||||
backoff = CONNECT_BACKOFF_START;
|
||||
@@ -1230,10 +1227,6 @@ public class ServiceSynchronize extends LifecycleService {
|
||||
// Cleanup
|
||||
am.cancel(pi);
|
||||
unregisterReceiver(alarm);
|
||||
|
||||
for (Handler handler : handlers)
|
||||
handler.sendEmptyMessage(0);
|
||||
handlers.clear();
|
||||
}
|
||||
|
||||
Log.i(Helper.TAG, account.name + " done state=" + state);
|
||||
@@ -1244,10 +1237,16 @@ public class ServiceSynchronize extends LifecycleService {
|
||||
EntityLog.log(ServiceSynchronize.this, account.name + " " + Helper.formatThrowable(ex));
|
||||
db.account().setAccountError(account.id, Helper.formatThrowable(ex));
|
||||
} finally {
|
||||
// Stop watching for operations
|
||||
for (Handler handler : handlers)
|
||||
handler.sendEmptyMessage(0);
|
||||
handlers.clear();
|
||||
|
||||
EntityLog.log(this, account.name + " closing");
|
||||
db.account().setAccountState(account.id, "closing");
|
||||
for (EntityFolder folder : folders.keySet())
|
||||
db.folder().setFolderState(folder.id, "closing");
|
||||
if (folder.synchronize && !folder.poll)
|
||||
db.folder().setFolderState(folder.id, "closing");
|
||||
|
||||
// Close store
|
||||
try {
|
||||
@@ -1267,7 +1266,8 @@ public class ServiceSynchronize extends LifecycleService {
|
||||
idlers.clear();
|
||||
|
||||
for (EntityFolder folder : folders.keySet())
|
||||
db.folder().setFolderState(folder.id, null);
|
||||
if (folder.synchronize && !folder.poll)
|
||||
db.folder().setFolderState(folder.id, null);
|
||||
}
|
||||
|
||||
if (state.running())
|
||||
|
||||
@@ -80,6 +80,15 @@
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/cbUnified" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/cbPoll"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:text="@string/title_poll_folder"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/cbSynchronize" />
|
||||
|
||||
<!-- after -->
|
||||
|
||||
<TextView
|
||||
@@ -90,7 +99,7 @@
|
||||
android:text="@string/title_sync_days"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/cbSynchronize" />
|
||||
app:layout_constraintTop_toBottomOf="@id/cbPoll" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etSyncDays"
|
||||
|
||||
@@ -180,6 +180,7 @@
|
||||
<string name="title_hide_folders">Hide hidden folders</string>
|
||||
<string name="title_show_folders">Show hidden folders</string>
|
||||
<string name="title_synchronize_folder">Synchronize (receive messages)</string>
|
||||
<string name="title_poll_folder">Poll instead of synchronize</string>
|
||||
<string name="title_unified_folder">Show in unified inbox</string>
|
||||
<string name="title_sync_days">Synchronize messages (days)</string>
|
||||
<string name="title_keep_days">Keep messages (days)</string>
|
||||
|
||||
Reference in New Issue
Block a user