diff --git a/FAQ.md b/FAQ.md
index 7bafdbda..82ee9b87 100644
--- a/FAQ.md
+++ b/FAQ.md
@@ -45,6 +45,9 @@ Valid security certificates are officially signed (not self signed) and have mat
Without [IMAP IDLE](https://en.wikipedia.org/wiki/IMAP_IDLE) emails need to be periodically fetched,
which is a waste of battery power and internet bandwidth and will delay notification of new emails.
+Since the goal of FairEmail is to offer safe and fast email, providers without IMAP IDLE are not supported.
+You should consider this a problem of the provider, not of the app.
+Almost all email providers offer IMAP IDLE, with as notable exception Yahoo!
**(6) How can I login to Gmail / G suite?**
diff --git a/app/src/main/java/eu/faircode/email/EntityAccount.java b/app/src/main/java/eu/faircode/email/EntityAccount.java
index e356c8d2..d2c399bb 100644
--- a/app/src/main/java/eu/faircode/email/EntityAccount.java
+++ b/app/src/main/java/eu/faircode/email/EntityAccount.java
@@ -51,7 +51,7 @@ public class EntityAccount {
@NonNull
public Boolean store_sent; // obsolete
@NonNull
- public Integer poll_interval;
+ public Integer poll_interval; // NOOP interval
public Long seen_until;
public String state;
public String error;
diff --git a/app/src/main/java/eu/faircode/email/FragmentAccount.java b/app/src/main/java/eu/faircode/email/FragmentAccount.java
index ba07da4e..77996808 100644
--- a/app/src/main/java/eu/faircode/email/FragmentAccount.java
+++ b/app/src/main/java/eu/faircode/email/FragmentAccount.java
@@ -94,7 +94,6 @@ public class FragmentAccount extends FragmentEx {
private EditText etInterval;
private Button btnCheck;
private ProgressBar pbCheck;
- private TextView tvIdle;
private Spinner spDrafts;
private Spinner spSent;
private Spinner spAll;
@@ -105,6 +104,7 @@ public class FragmentAccount extends FragmentEx {
private ImageButton ibDelete;
private ProgressBar pbWait;
private Group grpInstructions;
+ private Group grpInterval;
private Group grpFolders;
private long id = -1;
@@ -140,7 +140,6 @@ public class FragmentAccount extends FragmentEx {
etInterval = view.findViewById(R.id.etInterval);
btnCheck = view.findViewById(R.id.btnCheck);
pbCheck = view.findViewById(R.id.pbCheck);
- tvIdle = view.findViewById(R.id.tvIdle);
spDrafts = view.findViewById(R.id.spDrafts);
spSent = view.findViewById(R.id.spSent);
spAll = view.findViewById(R.id.spAll);
@@ -151,6 +150,7 @@ public class FragmentAccount extends FragmentEx {
ibDelete = view.findViewById(R.id.ibDelete);
pbWait = view.findViewById(R.id.pbWait);
grpInstructions = view.findViewById(R.id.grpInstructions);
+ grpInterval = view.findViewById(R.id.grpInterval);
grpFolders = view.findViewById(R.id.grpFolders);
// Wire controls
@@ -282,11 +282,12 @@ public class FragmentAccount extends FragmentEx {
istore = (IMAPStore) isession.getStore("imaps");
istore.connect(host, Integer.parseInt(port), user, password);
+ if (!istore.hasCapability("IDLE"))
+ throw new MessagingException(getContext().getString(R.string.title_no_idle));
+
if (!istore.hasCapability("UIDPLUS"))
throw new MessagingException(getContext().getString(R.string.title_no_uidplus));
- args.putBoolean("idle", istore.hasCapability("IDLE"));
-
for (Folder ifolder : istore.getDefaultFolder().list("*")) {
String type = null;
@@ -352,8 +353,6 @@ public class FragmentAccount extends FragmentEx {
// Refreshed token
tilPassword.getEditText().setText(args.getString("password"));
- tvIdle.setVisibility(args.getBoolean("idle") ? View.GONE : View.VISIBLE);
-
final Collator collator = Collator.getInstance(Locale.getDefault());
collator.setStrength(Collator.SECONDARY); // Case insensitive, process accents etc
@@ -672,11 +671,12 @@ public class FragmentAccount extends FragmentEx {
tilPassword.setPasswordVisibilityToggleEnabled(id < 0);
tvLink.setMovementMethod(LinkMovementMethod.getInstance());
btnAuthorize.setEnabled(false);
+ grpInstructions.setVisibility(View.GONE);
+ grpInterval.setVisibility(View.GONE);
btnCheck.setEnabled(false);
pbCheck.setVisibility(View.GONE);
btnSave.setVisibility(View.GONE);
pbSave.setVisibility(View.GONE);
- tvIdle.setVisibility(View.GONE);
grpFolders.setVisibility(View.GONE);
ibDelete.setVisibility(View.GONE);
@@ -755,6 +755,8 @@ public class FragmentAccount extends FragmentEx {
cbPrimary.setEnabled(cbSynchronize.isChecked());
+ grpInterval.setVisibility(BuildConfig.DEBUG ? View.VISIBLE : View.GONE);
+
btnCheck.setVisibility(cbSynchronize.isChecked() ? View.VISIBLE : View.GONE);
btnSave.setVisibility(cbSynchronize.isChecked() ? View.GONE : View.VISIBLE);
diff --git a/app/src/main/java/eu/faircode/email/ServiceSynchronize.java b/app/src/main/java/eu/faircode/email/ServiceSynchronize.java
index 3a3915ae..c62c715d 100644
--- a/app/src/main/java/eu/faircode/email/ServiceSynchronize.java
+++ b/app/src/main/java/eu/faircode/email/ServiceSynchronize.java
@@ -459,7 +459,6 @@ public class ServiceSynchronize extends LifecycleService {
db.folder().setFolderState(folder.id, null);
db.account().setAccountState(account.id, "connecting");
istore.connect(account.host, account.port, account.user, account.password);
- boolean hasIdle = istore.hasCapability("IDLE");
backoff = CONNECT_BACKOFF_START;
db.account().setAccountState(account.id, "connected");
@@ -583,18 +582,15 @@ public class ServiceSynchronize extends LifecycleService {
try {
Thread.sleep(account.poll_interval * 60 * 1000L);
- if (istore.hasCapability("IDLE")) {
- Log.i(Helper.TAG, folder.name + " request NOOP");
- ifolder.doCommand(new IMAPFolder.ProtocolCommand() {
- public Object doCommand(IMAPProtocol p) throws ProtocolException {
- Log.i(Helper.TAG, ifolder.getName() + " start NOOP");
- p.simpleCommand("NOOP", null);
- Log.i(Helper.TAG, ifolder.getName() + " end NOOP");
- return null;
- }
- });
- } else
- synchronizeMessages(account, folder, ifolder, state);
+ Log.i(Helper.TAG, folder.name + " request NOOP");
+ ifolder.doCommand(new IMAPFolder.ProtocolCommand() {
+ public Object doCommand(IMAPProtocol p) throws ProtocolException {
+ Log.i(Helper.TAG, ifolder.getName() + " start NOOP");
+ p.simpleCommand("NOOP", null);
+ Log.i(Helper.TAG, ifolder.getName() + " end NOOP");
+ return null;
+ }
+ });
} catch (InterruptedException ex) {
Log.w(Helper.TAG, folder.name + " noop " + ex.toString());
@@ -618,34 +614,32 @@ public class ServiceSynchronize extends LifecycleService {
noops.add(noop);
// Receive folder events
- if (hasIdle) {
- Thread idle = new Thread(new Runnable() {
- @Override
- public void run() {
- try {
- Log.i(Helper.TAG, folder.name + " start idle");
- while (state.running && ifolder.isOpen()) {
- Log.i(Helper.TAG, folder.name + " do idle");
- ifolder.idle(false);
- Log.i(Helper.TAG, folder.name + " done idle");
- }
- } catch (Throwable ex) {
- Log.e(Helper.TAG, folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
- reportError(account.name, folder.name, ex);
+ Thread idle = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ Log.i(Helper.TAG, folder.name + " start idle");
+ while (state.running && ifolder.isOpen()) {
+ Log.i(Helper.TAG, folder.name + " do idle");
+ ifolder.idle(false);
+ Log.i(Helper.TAG, folder.name + " done idle");
+ }
+ } 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));
+ db.folder().setFolderError(folder.id, Helper.formatThrowable(ex));
- synchronized (state) {
- state.notifyAll();
- }
- } finally {
- Log.i(Helper.TAG, folder.name + " end idle");
+ synchronized (state) {
+ state.notifyAll();
}
+ } finally {
+ Log.i(Helper.TAG, folder.name + " end idle");
}
- }, "sync.idle." + folder.id);
- idle.start();
- idlers.add(idle);
- }
+ }
+ }, "sync.idle." + folder.id);
+ idle.start();
+ idlers.add(idle);
}
BroadcastReceiver processFolder = new BroadcastReceiver() {
diff --git a/app/src/main/res/layout/fragment_account.xml b/app/src/main/res/layout/fragment_account.xml
index 9d1e331f..57ae0e25 100644
--- a/app/src/main/res/layout/fragment_account.xml
+++ b/app/src/main/res/layout/fragment_account.xml
@@ -258,18 +258,6 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/etInterval" />
-
-
+ app:layout_constraintTop_toBottomOf="@id/btnCheck" />
+
+
Select account
Instructions
Store sent messages (enable if needed only)
- Poll/keep-alive interval (minutes)
+ Keep-alive interval (minutes)
Synchronize (receive messages)
Synchronize (send messages)
Primary (default account)
diff --git a/app/src/main/res/xml/providers.xml b/app/src/main/res/xml/providers.xml
index c8e95139..ee3aedf8 100644
--- a/app/src/main/res/xml/providers.xml
+++ b/app/src/main/res/xml/providers.xml
@@ -36,7 +36,7 @@
starttls="false" />
-
-
+
@@ -81,7 +81,7 @@
starttls="false" />
-
-
+