diff --git a/FAQ.md b/FAQ.md index 7bafdbdafe..82ee9b8763 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 e356c8d2e1..d2c399bbf1 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 ba07da4e69..77996808c5 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 3a3915ae7f..c62c715d8c 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); - - db.folder().setFolderError(folder.id, Helper.formatThrowable(ex)); - - synchronized (state) { - state.notifyAll(); - } - } finally { - Log.i(Helper.TAG, folder.name + " end idle"); + 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)); + + 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 9d1e331f37..57ae0e2560 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 c8e951392e..ee3aedf86c 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" /> - - +