diff --git a/FAQ.md b/FAQ.md
index 33325824eb..029c4f7926 100644
--- a/FAQ.md
+++ b/FAQ.md
@@ -337,27 +337,36 @@ Unfortunately, it is impossible to make everybody happy and adding lots of setti
**(6) How can I login to Gmail / G suite?**
-To use a Gmail/G suite account, you'll need to enable access for "less secure" apps,
-see [here](https://support.google.com/accounts/answer/6010255) for Google's instructions
+To use a Gmail/G suite account,
+you can either enable access for "less secure apps" and use your account password
+or enable two factor authentication and use an app specific password.
+
+Note that an app specific password is required when two factor authentication is enabled.
+
+**Enable "Less secure apps"**
+
+See [here](https://support.google.com/accounts/answer/6010255) about how to enable "less secure apps"
or go [directy to the setting](https://www.google.com/settings/security/lesssecureapps).
-When "less secure" apps is not enabled,
+
+If you use multiple Gmail accounts, make sure you change the "less secure apps" setting of the right account(s).
+
+Be aware that you need to leave the "less secure apps" settings screen by using the back arrow to apply the setting.
+
+When "less secure apps" is not enabled,
you'll get the error *Authentication failed - invalid credentials* for accounts (IMAP)
and *Username and Password not accepted* for identities (SMTP).
-If you use multiple Gmail accounts, make sure you change the "less secure" setting of the right account(s).
-
-Be aware that you need to leave the "less secure" settings screen by using the back arrow to apply the setting.
+**App specific password**
-See [this FAQ](#user-content-faq111) for more information.
+See [here](https://support.google.com/accounts/answer/185833) about how to generate an app specific password.
You might get the alert "*Please log in via your web browser*".
-This security measure can for example be triggered when too many IP addresses were used in a too short time or when you are using a VPN.
-You can prevent this by using an app specific password.
+This happens when Google considers the network that connects you to the internet (this could be a VPN) to to be unsafe.
+This can be prevented by using an app specific password.
-To login to Gmail / G suite you'll sometimes need an app specific password, for example when two factor authentication is enabled.
-See here for instructions: [https://support.google.com/accounts/answer/185833](https://support.google.com/accounts/answer/185833).
+See [here](https://support.google.com/mail/accounts/answer/78754) for troubleshooting.
-If this doesn't work, see here for more solutions: [https://support.google.com/mail/accounts/answer/78754](https://support.google.com/mail/accounts/answer/78754)
+See [this FAQ](#user-content-faq111) about why OAuth is not being used.
@@ -378,7 +387,7 @@ which happens when reconnecting or if you synchronize manually.
**(8) Can I use a Microsoft Exchange account?**
You can use a Microsoft Exchange account if it is accessible via IMAP.
-See here for more information: [https://support.office.com/en-us/article/what-is-a-microsoft-exchange-account-47f000aa-c2bf-48ac-9bc2-83e5c6036793](https://support.office.com/en-us/article/what-is-a-microsoft-exchange-account-47f000aa-c2bf-48ac-9bc2-83e5c6036793)
+See [here](https://support.office.com/en-us/article/what-is-a-microsoft-exchange-account-47f000aa-c2bf-48ac-9bc2-83e5c6036793) for more information.
Please see [here](#frequently-requested-features) about ActiveSync support.
@@ -1569,7 +1578,7 @@ You will likely need to save the associated identity again as well.
For the correct settings, see [here](https://help.yahoo.com/kb/SLN4075.html).
You might need to enable "*less secure sign in*" for "*outdated*" apps,
-see [here](https://help.yahoo.com/kb/grant-temporary-access-outdated-apps-account-settings-sln27791.html).
+see [here](https://help.yahoo.com/kb/grant-temporary-access-outdated-apps-account-settings-sln27791.html) for more information.
You can directly access this setting [here](https://login.yahoo.com/account/security#less-secure-apps).
Note that FairEmail is using the standard [IMAP protocol](https://en.wikipedia.org/wiki/Internet_Message_Access_Protocol), which is really not outdated.
diff --git a/app/src/main/java/eu/faircode/email/EmailProvider.java b/app/src/main/java/eu/faircode/email/EmailProvider.java
index 7d34f1ae5c..63174c0d60 100644
--- a/app/src/main/java/eu/faircode/email/EmailProvider.java
+++ b/app/src/main/java/eu/faircode/email/EmailProvider.java
@@ -59,6 +59,7 @@ public class EmailProvider {
public int smtp_port;
public boolean smtp_starttls;
public UserType user = UserType.EMAIL;
+ public String helpUrl = null;
public StringBuilder documentation = null; // html
enum UserType {LOCAL, EMAIL}
@@ -410,14 +411,14 @@ public class EmailProvider {
private static EmailProvider addSpecials(Context context, EmailProvider provider) {
if ("imap.gmail.com".equals(provider.imap_host))
- addDocumentation(provider,
- "https://github.com/M66B/open-source-email/blob/master/FAQ.md#user-content-faq6",
- context.getString(R.string.title_setup_instructions));
+ provider.helpUrl = Helper.FAQ_URI + "#user-content-faq6";
+
+ if (provider.imap_host.endsWith("office365.com") ||
+ provider.imap_host.endsWith("live.com"))
+ provider.helpUrl = Helper.FAQ_URI + "#user-content-faq14";
if (provider.imap_host.endsWith("yahoo.com"))
- addDocumentation(provider,
- "https://github.com/M66B/open-source-email/blob/master/FAQ.md#user-content-faq88",
- context.getString(R.string.title_setup_instructions));
+ provider.helpUrl = Helper.FAQ_URI + "#user-content-faq88";
return provider;
}
diff --git a/app/src/main/java/eu/faircode/email/FragmentAccount.java b/app/src/main/java/eu/faircode/email/FragmentAccount.java
index d9f5c38a34..5c01297e16 100644
--- a/app/src/main/java/eu/faircode/email/FragmentAccount.java
+++ b/app/src/main/java/eu/faircode/email/FragmentAccount.java
@@ -24,6 +24,7 @@ import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
+import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -127,6 +128,7 @@ public class FragmentAccount extends FragmentBase {
private Button btnSave;
private ContentLoadingProgressBar pbSave;
private TextView tvError;
+ private Button btnHelp;
private TextView tvInstructions;
private ContentLoadingProgressBar pbWait;
@@ -207,7 +209,9 @@ public class FragmentAccount extends FragmentBase {
btnSave = view.findViewById(R.id.btnSave);
pbSave = view.findViewById(R.id.pbSave);
+
tvError = view.findViewById(R.id.tvError);
+ btnHelp = view.findViewById(R.id.btnHelp);
tvInstructions = view.findViewById(R.id.tvInstructions);
pbWait = view.findViewById(R.id.pbWait);
@@ -351,6 +355,22 @@ public class FragmentAccount extends FragmentBase {
}
});
+ addBackPressedListener(new ActivityBase.IBackPressedListener() {
+ @Override
+ public boolean onBackPressed() {
+ onSave(true);
+ return true;
+ }
+ });
+
+ btnHelp.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(Intent.ACTION_VIEW, (Uri) btnHelp.getTag());
+ Helper.view(getContext(), intent);
+ }
+ });
+
adapter = new ArrayAdapter<>(getContext(), R.layout.spinner_item1, android.R.id.text1, new ArrayList());
adapter.setDropDownViewResource(R.layout.spinner_item1_dropdown);
@@ -366,14 +386,6 @@ public class FragmentAccount extends FragmentBase {
spLeft.setAdapter(adapterSwipe);
spRight.setAdapter(adapterSwipe);
- addBackPressedListener(new ActivityBase.IBackPressedListener() {
- @Override
- public boolean onBackPressed() {
- onSave(true);
- return true;
- }
- });
-
// Initialize
Helper.setViewsEnabled(view, false);
@@ -393,7 +405,9 @@ public class FragmentAccount extends FragmentBase {
btnSave.setVisibility(View.GONE);
pbSave.setVisibility(View.GONE);
+
tvError.setVisibility(View.GONE);
+ btnHelp.setVisibility(View.GONE);
tvInstructions.setVisibility(View.GONE);
tvInstructions.setMovementMethod(LinkMovementMethod.getInstance());
@@ -467,6 +481,7 @@ public class FragmentAccount extends FragmentBase {
tvUtf8.setVisibility(View.GONE);
grpFolders.setVisibility(View.GONE);
tvError.setVisibility(View.GONE);
+ btnHelp.setVisibility(View.GONE);
tvInstructions.setVisibility(View.GONE);
}
@@ -701,6 +716,8 @@ public class FragmentAccount extends FragmentBase {
Helper.setViewsEnabled(view, false);
pbSave.setVisibility(View.VISIBLE);
tvError.setVisibility(View.GONE);
+ btnHelp.setVisibility(View.GONE);
+ tvInstructions.setVisibility(View.GONE);
}
@Override
@@ -838,17 +855,17 @@ public class FragmentAccount extends FragmentBase {
String accountRealm = (account == null ? null : account.realm);
boolean check = (synchronize && (account == null ||
- !account.synchronize ||
- account.insecure != insecure ||
+ !account.synchronize || account.error != null ||
+ !account.insecure.equals(insecure) ||
!host.equals(account.host) || Integer.parseInt(port) != account.port ||
!user.equals(account.user) || !password.equals(account.password) ||
- !Objects.equals(realm, accountRealm) ||
- account.error != null));
+ !Objects.equals(realm, accountRealm)));
boolean reload = (check || account == null ||
account.synchronize != synchronize ||
account.notify != notify ||
!account.poll_interval.equals(Integer.parseInt(interval)) ||
account.partial_fetch != partial_fetch);
+ Log.i("Account check=" + check + " reload=" + reload);
Long last_connected = null;
if (account != null && synchronize == account.synchronize)
@@ -1053,20 +1070,27 @@ public class FragmentAccount extends FragmentBase {
tvError.setText(Helper.formatThrowable(ex, false));
tvError.setVisibility(View.VISIBLE);
- final View target;
+ final EmailProvider provider = (EmailProvider) spProvider.getSelectedItem();
+ if (provider != null && provider.helpUrl != null) {
+ Uri uri = Uri.parse(provider.helpUrl);
+ btnHelp.setTag(uri);
+ btnHelp.setVisibility(View.VISIBLE);
+ }
- EmailProvider provider = (EmailProvider) spProvider.getSelectedItem();
if (provider != null && provider.documentation != null) {
tvInstructions.setText(HtmlHelper.fromHtml(provider.documentation.toString()));
tvInstructions.setVisibility(View.VISIBLE);
- target = tvInstructions;
- } else
- target = tvError;
+ }
new Handler().post(new Runnable() {
@Override
public void run() {
- scroll.smoothScrollTo(0, target.getBottom());
+ if (provider != null && provider.documentation != null)
+ scroll.smoothScrollTo(0, tvInstructions.getBottom());
+ else if (provider != null && provider.helpUrl != null)
+ scroll.smoothScrollTo(0, btnHelp.getBottom());
+ else
+ scroll.smoothScrollTo(0, tvError.getBottom());
}
});
}
diff --git a/app/src/main/java/eu/faircode/email/FragmentIdentity.java b/app/src/main/java/eu/faircode/email/FragmentIdentity.java
index 34793940e0..46d74e29c6 100644
--- a/app/src/main/java/eu/faircode/email/FragmentIdentity.java
+++ b/app/src/main/java/eu/faircode/email/FragmentIdentity.java
@@ -26,6 +26,7 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
+import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.text.Editable;
@@ -122,6 +123,7 @@ public class FragmentIdentity extends FragmentBase {
private Button btnSave;
private ContentLoadingProgressBar pbSave;
private TextView tvError;
+ private Button btnHelp;
private TextView tvInstructions;
private ContentLoadingProgressBar pbWait;
@@ -198,6 +200,7 @@ public class FragmentIdentity extends FragmentBase {
btnSave = view.findViewById(R.id.btnSave);
pbSave = view.findViewById(R.id.pbSave);
tvError = view.findViewById(R.id.tvError);
+ btnHelp = view.findViewById(R.id.btnHelp);
tvInstructions = view.findViewById(R.id.tvInstructions);
tvInstructions.setMovementMethod(LinkMovementMethod.getInstance());
@@ -214,6 +217,7 @@ public class FragmentIdentity extends FragmentBase {
grpAuthorize.setVisibility(position > 0 ? View.VISIBLE : View.GONE);
if (position == 0) {
tvError.setVisibility(View.GONE);
+ btnHelp.setVisibility(View.GONE);
tvInstructions.setVisibility(View.GONE);
grpAdvanced.setVisibility(View.GONE);
}
@@ -400,6 +404,14 @@ public class FragmentIdentity extends FragmentBase {
}
});
+ btnHelp.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(Intent.ACTION_VIEW, (Uri) btnHelp.getTag());
+ Helper.view(getContext(), intent);
+ }
+ });
+
// Initialize
Helper.setViewsEnabled(view, false);
btnAutoConfig.setEnabled(false);
@@ -411,6 +423,7 @@ public class FragmentIdentity extends FragmentBase {
btnSave.setVisibility(View.GONE);
pbSave.setVisibility(View.GONE);
tvError.setVisibility(View.GONE);
+ btnHelp.setVisibility(View.GONE);
tvInstructions.setVisibility(View.GONE);
grpAuthorize.setVisibility(View.GONE);
@@ -508,6 +521,7 @@ public class FragmentIdentity extends FragmentBase {
Helper.setViewsEnabled(view, false);
pbSave.setVisibility(View.VISIBLE);
tvError.setVisibility(View.GONE);
+ btnHelp.setVisibility(View.GONE);
tvInstructions.setVisibility(View.GONE);
}
@@ -645,13 +659,13 @@ public class FragmentIdentity extends FragmentBase {
String identityRealm = (identity == null ? null : identity.realm);
boolean check = (synchronize && (identity == null ||
- !identity.synchronize ||
- identity.insecure != insecure ||
+ !identity.synchronize || identity.error != null ||
+ !identity.insecure.equals(insecure) ||
!host.equals(identity.host) || Integer.parseInt(port) != identity.port ||
!user.equals(identity.user) || !password.equals(identity.password) ||
!Objects.equals(realm, identityRealm) ||
- use_ip != identity.use_ip ||
- identity.error != null));
+ use_ip != identity.use_ip));
+ Log.i("Identity check=" + check);
Long last_connected = null;
if (identity != null && synchronize == identity.synchronize)
@@ -774,20 +788,27 @@ public class FragmentIdentity extends FragmentBase {
tvError.setText(Helper.formatThrowable(ex, false));
tvError.setVisibility(View.VISIBLE);
- final View target;
+ final EmailProvider provider = (EmailProvider) spProvider.getSelectedItem();
+ if (provider != null && provider.helpUrl != null) {
+ Uri uri = Uri.parse(provider.helpUrl);
+ btnHelp.setTag(uri);
+ btnHelp.setVisibility(View.VISIBLE);
+ }
- EmailProvider provider = (EmailProvider) spProvider.getSelectedItem();
if (provider != null && provider.documentation != null) {
tvInstructions.setText(HtmlHelper.fromHtml(provider.documentation.toString()));
tvInstructions.setVisibility(View.VISIBLE);
- target = tvInstructions;
- } else
- target = tvError;
+ }
new Handler().post(new Runnable() {
@Override
public void run() {
- scroll.smoothScrollTo(0, target.getBottom());
+ if (provider != null && provider.documentation != null)
+ scroll.smoothScrollTo(0, tvInstructions.getBottom());
+ else if (provider != null && provider.helpUrl != null)
+ scroll.smoothScrollTo(0, btnHelp.getBottom());
+ else
+ scroll.smoothScrollTo(0, tvError.getBottom());
}
});
}
diff --git a/app/src/main/java/eu/faircode/email/FragmentQuickSetup.java b/app/src/main/java/eu/faircode/email/FragmentQuickSetup.java
index 3ac20282ca..9af3e9fbf9 100644
--- a/app/src/main/java/eu/faircode/email/FragmentQuickSetup.java
+++ b/app/src/main/java/eu/faircode/email/FragmentQuickSetup.java
@@ -24,7 +24,9 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.net.Uri;
import android.os.Bundle;
+import android.os.Handler;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.util.Patterns;
@@ -38,6 +40,7 @@ import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.EditText;
+import android.widget.ScrollView;
import android.widget.TextView;
import androidx.annotation.NonNull;
@@ -64,6 +67,7 @@ import static android.app.Activity.RESULT_OK;
public class FragmentQuickSetup extends FragmentBase {
private ViewGroup view;
+ private ScrollView scroll;
private EditText etName;
private EditText etEmail;
@@ -71,6 +75,7 @@ public class FragmentQuickSetup extends FragmentBase {
private Button btnCheck;
private TextView tvError;
+ private Button btnHelp;
private TextView tvInstructions;
private TextView tvImap;
@@ -87,6 +92,7 @@ public class FragmentQuickSetup extends FragmentBase {
setHasOptionsMenu(true);
view = (ViewGroup) inflater.inflate(R.layout.fragment_quick_setup, container, false);
+ scroll = view.findViewById(R.id.scroll);
// Get controls
etName = view.findViewById(R.id.etName);
@@ -95,6 +101,7 @@ public class FragmentQuickSetup extends FragmentBase {
btnCheck = view.findViewById(R.id.btnCheck);
tvError = view.findViewById(R.id.tvError);
+ btnHelp = view.findViewById(R.id.btnHelp);
tvInstructions = view.findViewById(R.id.tvInstructions);
tvImap = view.findViewById(R.id.tvImap);
@@ -130,8 +137,17 @@ public class FragmentQuickSetup extends FragmentBase {
}
});
+ btnHelp.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(Intent.ACTION_VIEW, (Uri) btnHelp.getTag());
+ Helper.view(getContext(), intent);
+ }
+ });
+
// Initialize
tvError.setVisibility(View.GONE);
+ btnHelp.setVisibility(View.GONE);
tvInstructions.setVisibility(View.GONE);
tvInstructions.setMovementMethod(LinkMovementMethod.getInstance());
grpSetup.setVisibility(View.GONE);
@@ -181,6 +197,7 @@ public class FragmentQuickSetup extends FragmentBase {
Helper.setViewsEnabled(view, false);
tvError.setVisibility(View.GONE);
+ btnHelp.setVisibility(View.GONE);
tvInstructions.setVisibility(View.GONE);
grpSetup.setVisibility(check ? View.GONE : View.VISIBLE);
}
@@ -207,6 +224,8 @@ public class FragmentQuickSetup extends FragmentBase {
String[] dparts = email.split("@");
EmailProvider provider = EmailProvider.fromDomain(context, dparts[1]);
+ if (provider.helpUrl != null)
+ args.putString("help", provider.helpUrl);
if (provider.documentation != null)
args.putString("documentation", provider.documentation.toString());
@@ -377,18 +396,37 @@ public class FragmentQuickSetup extends FragmentBase {
}
@Override
- protected void onException(Bundle args, Throwable ex) {
- if (args.containsKey("documentation")) {
- tvInstructions.setText(HtmlHelper.fromHtml(args.getString("documentation")));
- tvInstructions.setVisibility(View.VISIBLE);
- }
-
+ protected void onException(final Bundle args, Throwable ex) {
if (ex instanceof IllegalArgumentException || ex instanceof UnknownHostException)
Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG).show();
else {
tvError.setText(Helper.formatThrowable(ex, false));
tvError.setVisibility(View.VISIBLE);
}
+
+ if (args.containsKey("help")) {
+ Uri uri = Uri.parse(args.getString("help"));
+ btnHelp.setTag(uri);
+ btnHelp.setVisibility(View.VISIBLE);
+ }
+
+ if (args.containsKey("documentation")) {
+ tvInstructions.setText(HtmlHelper.fromHtml(args.getString("documentation")));
+ tvInstructions.setVisibility(View.VISIBLE);
+ }
+
+ new Handler().post(new Runnable() {
+ @Override
+ public void run() {
+ if (args.containsKey("documentation"))
+ scroll.smoothScrollTo(0, tvInstructions.getBottom());
+ else if (args.containsKey("help"))
+ scroll.smoothScrollTo(0, btnHelp.getBottom());
+ else if (tvError.getVisibility() == View.VISIBLE)
+ scroll.smoothScrollTo(0, tvError.getBottom());
+ }
+ });
+
}
}.execute(FragmentQuickSetup.this, args, "setup:quick");
}
diff --git a/app/src/main/res/layout/fragment_account.xml b/app/src/main/res/layout/fragment_account.xml
index e95debb087..a2959c3a42 100644
--- a/app/src/main/res/layout/fragment_account.xml
+++ b/app/src/main/res/layout/fragment_account.xml
@@ -703,6 +703,18 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btnSave" />
+
+
+ app:layout_constraintTop_toBottomOf="@id/btnHelp" />
+
+
+ app:layout_constraintTop_toBottomOf="@id/btnHelp" />
+
+
+ app:layout_constraintTop_toBottomOf="@id/btnHelp" />
Dadurch wird die App neu gestartet
Details finden Sie in der FAQAktiviert zusätzliche Protokollierung und zeigt Fehlerbehebungsinformationen an verschiedenen Stellen an
- Wählen Sie …
+ Auswählen …Ihr NameIhre E-Mail-AdresseBearbeitung der Absenderadresse erlauben
diff --git a/app/src/main/res/values-es-rES/strings.xml b/app/src/main/res/values-es-rES/strings.xml
index 86976fabaf..ce68dd38c6 100644
--- a/app/src/main/res/values-es-rES/strings.xml
+++ b/app/src/main/res/values-es-rES/strings.xml
@@ -80,6 +80,7 @@
Editar carpetaAjustesAyuda
+ Recibir y enviar mensajes requiere configurar una cuenta y una identidad como en cualquier otra aplicación de correo electrónico.Configuración rápidaPara configurar rápidamente una cuenta y una identidad para la mayoría de los proveedoresLa configuración rápida obtendrá información de configuración de autoconfig.thunderbird.net
diff --git a/app/src/main/res/values-fi-rFI/strings.xml b/app/src/main/res/values-fi-rFI/strings.xml
index cb7d5f4e46..273ab9a20f 100644
--- a/app/src/main/res/values-fi-rFI/strings.xml
+++ b/app/src/main/res/values-fi-rFI/strings.xml
@@ -80,6 +80,7 @@
Muokkaa kansiotaAsetuksetOhje
+ Viestien vastaanottaminen ja lähettäminen vaatii tilin ja identiteetin asettamisen, kuten missä tahansa sähköpostisovelluksessa.Pika-asetuksetTilin ja identiteetin nopeaan asettamiseen useimmille palveluntarjoajillePika-asennus hakee asetustiedot osoitteesta autoconfig.thunderbird.net
diff --git a/app/src/main/res/values-fr-rFR/strings.xml b/app/src/main/res/values-fr-rFR/strings.xml
index 28cab0ab85..1779f78112 100644
--- a/app/src/main/res/values-fr-rFR/strings.xml
+++ b/app/src/main/res/values-fr-rFR/strings.xml
@@ -80,6 +80,7 @@
Modifier le dossierConfigurationAide
+ La réception et l\'envoi de messages nécessitent la création d\'un compte et d\'une identité comme dans toute autre application de courriel.Configuration rapidePour configurer rapidement un compte et une identité pour la plupart des fournisseursLa configuration rapide récupérera les informations de configuration de autoconfig.thunderbird.net