Bring back polling

This commit is contained in:
M66B 2018-09-21 17:19:02 +00:00
parent 5cca576e28
commit 9cafc1223d
7 changed files with 83 additions and 55 deletions

12
FAQ.md
View File

@ -49,13 +49,13 @@ See also [this FAQ](#FAQ16).
Valid security certificates are officially signed (not self signed) and have matching a host name. Valid security certificates are officially signed (not self signed) and have matching a host name.
<a name="FAQ5"></a> <a name="FAQ5"></a>
**(5) What does 'no IDLE support' mean?** ~~**(5) What does 'no IDLE support' mean?**~~
Without [IMAP IDLE](https://en.wikipedia.org/wiki/IMAP_IDLE) emails need to be periodically fetched, ~~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. ~~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. ~~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. ~~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! ~~Almost all email providers offer IMAP IDLE, with as notable exception Yahoo!~~
<a name="FAQ6"></a> <a name="FAQ6"></a>
**(6) How can I login to Gmail / G suite?** **(6) How can I login to Gmail / G suite?**

View File

@ -7,11 +7,6 @@ You'll need to add at least one account to receive email and at least one identi
An internet connection is required to add accounts and identities. An internet connection is required to add accounts and identities.
Your email provider should support IMAP with the IDLE and UIDPLUS extensions.
All up-to-date email providers do, with as notable exception Yahoo!
IMAP IDLE is required to limit battery usage,
see [this FAQ](https://github.com/M66B/open-source-email/blob/master/FAQ.md#FAQ5) for more details.
Your email provider should support secure connections. Your email provider should support secure connections.
If your provider doesn't support secure connections and you care at least a little about your privacy, If your provider doesn't support secure connections and you care at least a little about your privacy,
you are strongly advised to switch to another provider. you are strongly advised to switch to another provider.

View File

@ -110,6 +110,7 @@ public class FragmentAccount extends FragmentEx {
private ImageButton ibPro; private ImageButton ibPro;
private CheckBox cbSynchronize; private CheckBox cbSynchronize;
private CheckBox cbPrimary; private CheckBox cbPrimary;
private EditText etInterval;
private Button btnCheck; private Button btnCheck;
private ProgressBar pbCheck; private ProgressBar pbCheck;
@ -173,6 +174,7 @@ public class FragmentAccount extends FragmentEx {
cbSynchronize = view.findViewById(R.id.cbSynchronize); cbSynchronize = view.findViewById(R.id.cbSynchronize);
cbPrimary = view.findViewById(R.id.cbPrimary); cbPrimary = view.findViewById(R.id.cbPrimary);
etInterval = view.findViewById(R.id.etInterval);
btnCheck = view.findViewById(R.id.btnCheck); btnCheck = view.findViewById(R.id.btnCheck);
pbCheck = view.findViewById(R.id.pbCheck); pbCheck = view.findViewById(R.id.pbCheck);
@ -429,9 +431,6 @@ public class FragmentAccount extends FragmentEx {
throw ex; throw ex;
} }
if (!istore.hasCapability("IDLE"))
throw new MessagingException(getContext().getString(R.string.title_no_idle));
if (!istore.hasCapability("UIDPLUS")) if (!istore.hasCapability("UIDPLUS"))
throw new MessagingException(getContext().getString(R.string.title_no_uidplus)); throw new MessagingException(getContext().getString(R.string.title_no_uidplus));
@ -567,6 +566,7 @@ public class FragmentAccount extends FragmentEx {
args.putInt("color", color); args.putInt("color", color);
args.putString("signature", Html.toHtml(etSignature.getText())); args.putString("signature", Html.toHtml(etSignature.getText()));
args.putBoolean("primary", cbPrimary.isChecked()); args.putBoolean("primary", cbPrimary.isChecked());
args.putString("interval", etInterval.getText().toString());
args.putParcelable("drafts", drafts); args.putParcelable("drafts", drafts);
args.putParcelable("sent", sent); args.putParcelable("sent", sent);
@ -588,6 +588,7 @@ public class FragmentAccount extends FragmentEx {
String signature = args.getString("signature"); String signature = args.getString("signature");
boolean synchronize = args.getBoolean("synchronize"); boolean synchronize = args.getBoolean("synchronize");
boolean primary = args.getBoolean("primary"); boolean primary = args.getBoolean("primary");
String interval = args.getString("interval");
EntityFolder drafts = args.getParcelable("drafts"); EntityFolder drafts = args.getParcelable("drafts");
EntityFolder sent = args.getParcelable("sent"); EntityFolder sent = args.getParcelable("sent");
@ -603,6 +604,8 @@ public class FragmentAccount extends FragmentEx {
throw new Throwable(getContext().getString(R.string.title_no_user)); throw new Throwable(getContext().getString(R.string.title_no_user));
if (TextUtils.isEmpty(password)) if (TextUtils.isEmpty(password))
throw new Throwable(getContext().getString(R.string.title_no_password)); throw new Throwable(getContext().getString(R.string.title_no_password));
if (TextUtils.isEmpty(interval))
interval = "9";
if (synchronize && drafts == null) if (synchronize && drafts == null)
throw new Throwable(getContext().getString(R.string.title_no_drafts)); throw new Throwable(getContext().getString(R.string.title_no_drafts));
@ -653,7 +656,7 @@ public class FragmentAccount extends FragmentEx {
account.synchronize = synchronize; account.synchronize = synchronize;
account.primary = (account.synchronize && primary); account.primary = (account.synchronize && primary);
account.store_sent = false; account.store_sent = false;
account.poll_interval = 9; account.poll_interval = Integer.parseInt(interval);
if (!synchronize) if (!synchronize)
account.error = null; account.error = null;
@ -874,6 +877,7 @@ public class FragmentAccount extends FragmentEx {
cbSynchronize.setChecked(account == null ? true : account.synchronize); cbSynchronize.setChecked(account == null ? true : account.synchronize);
cbPrimary.setChecked(account == null ? true : account.primary); cbPrimary.setChecked(account == null ? true : account.primary);
etInterval.setText(Long.toString(account == null ? 9 : account.poll_interval));
color = (account == null || account.color == null ? Color.TRANSPARENT : account.color); color = (account == null || account.color == null ? Color.TRANSPARENT : account.color);

View File

@ -642,6 +642,8 @@ public class ServiceSynchronize extends LifecycleService {
db.folder().setFolderState(folder.id, null); db.folder().setFolderState(folder.id, null);
db.account().setAccountState(account.id, "connecting"); db.account().setAccountState(account.id, "connecting");
Helper.connect(this, istore, account); Helper.connect(this, istore, account);
final boolean capIdle = istore.hasCapability("IDLE");
Log.i(Helper.TAG, account.name + " idle=" + capIdle);
db.account().setAccountState(account.id, "connected"); db.account().setAccountState(account.id, "connected");
db.account().setAccountError(account.id, null); db.account().setAccountError(account.id, null);
@ -813,15 +815,18 @@ public class ServiceSynchronize extends LifecycleService {
try { try {
Thread.sleep(account.poll_interval * 60 * 1000L); Thread.sleep(account.poll_interval * 60 * 1000L);
Log.i(Helper.TAG, folder.name + " request NOOP"); if (capIdle) {
ifolder.doCommand(new IMAPFolder.ProtocolCommand() { Log.i(Helper.TAG, folder.name + " request NOOP");
public Object doCommand(IMAPProtocol p) throws ProtocolException { ifolder.doCommand(new IMAPFolder.ProtocolCommand() {
Log.i(Helper.TAG, ifolder.getName() + " start NOOP"); public Object doCommand(IMAPProtocol p) throws ProtocolException {
p.simpleCommand("NOOP", null); Log.i(Helper.TAG, ifolder.getName() + " start NOOP");
Log.i(Helper.TAG, ifolder.getName() + " end NOOP"); p.simpleCommand("NOOP", null);
return null; Log.i(Helper.TAG, ifolder.getName() + " end NOOP");
} return null;
}); }
});
} else
synchronizeMessages(account, folder, ifolder, state);
} catch (InterruptedException ex) { } catch (InterruptedException ex) {
Log.w(Helper.TAG, folder.name + " noop " + ex.toString()); Log.w(Helper.TAG, folder.name + " noop " + ex.toString());
@ -845,32 +850,34 @@ public class ServiceSynchronize extends LifecycleService {
noops.add(noop); noops.add(noop);
// Receive folder events // Receive folder events
Thread idle = new Thread(new Runnable() { if (capIdle) {
@Override Thread idle = new Thread(new Runnable() {
public void run() { @Override
try { public void run() {
Log.i(Helper.TAG, folder.name + " start idle"); try {
while (state.running && ifolder.isOpen()) { Log.i(Helper.TAG, folder.name + " start idle");
//Log.i(Helper.TAG, folder.name + " do idle"); while (state.running && ifolder.isOpen()) {
ifolder.idle(false); Log.i(Helper.TAG, folder.name + " do idle");
//Log.i(Helper.TAG, folder.name + " done 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)); } catch (Throwable ex) {
reportError(account.name, folder.name, 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) { synchronized (state) {
state.notifyAll(); state.notifyAll();
}
} finally {
Log.i(Helper.TAG, folder.name + " end idle");
} }
} finally {
Log.i(Helper.TAG, folder.name + " end idle");
} }
} }, "sync.idle." + folder.id);
}, "sync.idle." + folder.id); idle.start();
idle.start(); idlers.add(idle);
idlers.add(idle); }
} }
backoff = CONNECT_BACKOFF_START; backoff = CONNECT_BACKOFF_START;

View File

@ -173,7 +173,6 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvUser" /> app:layout_constraintTop_toBottomOf="@id/tvUser" />
<!-- password --> <!-- password -->
<TextView <TextView
@ -318,6 +317,29 @@
android:text="@string/title_primary_account" android:text="@string/title_primary_account"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/cbSynchronize" /> app:layout_constraintTop_toBottomOf="@id/cbSynchronize" />
<!-- port -->
<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/cbPrimary" />
<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" />
<!-- check --> <!-- check -->
<Button <Button
@ -327,7 +349,7 @@
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:text="@string/title_check" android:text="@string/title_check"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/cbPrimary" /> app:layout_constraintTop_toBottomOf="@id/etInterval" />
<ProgressBar <ProgressBar
android:id="@+id/pbCheck" android:id="@+id/pbCheck"
@ -347,7 +369,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/cbPrimary" /> app:layout_constraintTop_toBottomOf="@id/etInterval" />
<TextView <TextView
android:id="@+id/tvDrafts" android:id="@+id/tvDrafts"
@ -498,7 +520,7 @@
android:id="@+id/grpAdvanced" android:id="@+id/grpAdvanced"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
app:constraint_referenced_ids="tvName,etName,btnColor,vwColor,ibColorDefault,tvSignature,etSignature,ibPro,cbSynchronize,cbPrimary" /> app:constraint_referenced_ids="tvName,etName,btnColor,vwColor,ibColorDefault,tvSignature,etSignature,ibPro,cbSynchronize,cbPrimary,tvInterval,etInterval" />
<androidx.constraintlayout.widget.Group <androidx.constraintlayout.widget.Group
android:id="@+id/grpFolders" android:id="@+id/grpFolders"

View File

@ -110,6 +110,7 @@
<string name="title_synchronize_identity">Synchronize (send messages)</string> <string name="title_synchronize_identity">Synchronize (send messages)</string>
<string name="title_primary_account">Primary (default account)</string> <string name="title_primary_account">Primary (default account)</string>
<string name="title_primary_identity">Primary (default identity)</string> <string name="title_primary_identity">Primary (default identity)</string>
<string name="title_poll_interval">Poll interval (minutes)</string>
<string name="title_check">Check</string> <string name="title_check">Check</string>
<string name="title_no_name">Name missing</string> <string name="title_no_name">Name missing</string>
<string name="title_no_email">Email address missing</string> <string name="title_no_email">Email address missing</string>
@ -119,7 +120,6 @@
<string name="title_no_user">User name missing</string> <string name="title_no_user">User name missing</string>
<string name="title_no_password">Password missing</string> <string name="title_no_password">Password missing</string>
<string name="title_no_drafts">Drafts folder missing</string> <string name="title_no_drafts">Drafts folder missing</string>
<string name="title_no_idle">IMAP IDLE not supported, see the FAQ</string>
<string name="title_no_uidplus">IMAP UIDPLUS not supported, see the FAQ</string> <string name="title_no_uidplus">IMAP UIDPLUS not supported, see the FAQ</string>
<string name="title_account_delete">Delete this account permanently?</string> <string name="title_account_delete">Delete this account permanently?</string>
<string name="title_identity_delete">Delete this identity permanently?</string> <string name="title_identity_delete">Delete this identity permanently?</string>

View File

@ -36,7 +36,7 @@
starttls="false" /> starttls="false" />
</provider> </provider>
<!-- no IMAP IDLE --> <!-- no IMAP IDLE -->
<!--provider <provider
name="Yahoo!" name="Yahoo!"
link="https://help.yahoo.com/kb/SLN4075.html"> link="https://help.yahoo.com/kb/SLN4075.html">
<imap <imap
@ -46,7 +46,7 @@
host="smtp.mail.yahoo.com" host="smtp.mail.yahoo.com"
port="465" port="465"
starttls="false" /> starttls="false" />
</provider--> </provider>
<provider <provider
name="Posteo.de" name="Posteo.de"
link="https://posteo.de/en/help/how-do-i-set-up-posteo-in-an-email-client-pop3-imap-and-smtp"> link="https://posteo.de/en/help/how-do-i-set-up-posteo-in-an-email-client-pop3-imap-and-smtp">
@ -81,7 +81,7 @@
starttls="false" /> starttls="false" />
</provider> </provider>
<!-- no IMAP IDLE --> <!-- no IMAP IDLE -->
<!--provider <provider
name="SFR.fr" name="SFR.fr"
link="https://assistance.sfr.fr/service-et-accessoire/sfr-mail/serveurs-messagerie-sfr.html"> link="https://assistance.sfr.fr/service-et-accessoire/sfr-mail/serveurs-messagerie-sfr.html">
<imap <imap
@ -91,7 +91,7 @@
host="smtp.sfr.fr" host="smtp.sfr.fr"
port="465" port="465"
starttls="false" /> starttls="false" />
</provider--> </provider>
<provider <provider
name="infomaniak" name="infomaniak"
link="https://www.infomaniak.com/fr/support/faq/1075/configurer-une-adresse-email-configuration-logiciel-de-messagerie-parametres-courrier"> link="https://www.infomaniak.com/fr/support/faq/1075/configurer-une-adresse-email-configuration-logiciel-de-messagerie-parametres-courrier">