Improved error handling

pull/156/head
M66B 6 years ago
parent c81389769e
commit ce9b7e82e2

@ -334,7 +334,11 @@ Unfortunately, it is impossible to make everybody happy and adding lots of setti
To use a Gmail/G suite account, you'll need to enable access for "less secure" apps, 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 see [here](https://support.google.com/accounts/answer/6010255) for Google's instructions
or go [directy to the setting](https://www.google.com/settings/security/lesssecureapps). or go [directy to the setting](https://www.google.com/settings/security/lesssecureapps).
You can solve the error *535-5.7.8 Username and Password not accepted* by enabling "less secure" apps. 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.
You might get the alert "*Please log in via your web browser*". 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. 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.

@ -448,15 +448,7 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB
} else if ("outbox".equals(action)) } else if ("outbox".equals(action))
onMenuOutbox(); onMenuOutbox();
else if (action.startsWith("thread")) {
else if ("error".equals(action)) {
Intent ifaq = new Intent(Intent.ACTION_VIEW);
ifaq.setData(Uri.parse(Helper.FAQ_URI + "#frequently-asked-questions"));
ifaq.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (ifaq.resolveActivity(getPackageManager()) != null)
startActivity(ifaq);
} else if (action.startsWith("thread")) {
intent.putExtra("thread", action.split(":", 2)[1]); intent.putExtra("thread", action.split(":", 2)[1]);
onViewThread(intent); onViewThread(intent);
} }

@ -3069,6 +3069,7 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
message.id = null; message.id = null;
message.folder = drafts.id; message.folder = drafts.id;
message.ui_snoozed = null; message.ui_snoozed = null;
message.error = null;
message.id = db.message().insertMessage(message); message.id = db.message().insertMessage(message);
File target = message.getFile(context); File target = message.getFile(context);

@ -2129,11 +2129,11 @@ class Core {
String title; String title;
if (account == null) if (account == null)
title = folder.name; title = Helper.localizeFolderName(context, folder.name);
else if (folder == null) else if (folder == null)
title = account.name; title = account.name;
else else
title = account.name + "/" + folder.name; title = account.name + "/" + Helper.localizeFolderName(context, folder.name);
String tag = "error:" + (account == null ? 0 : account.id) + ":" + (folder == null ? 0 : folder.id); String tag = "error:" + (account == null ? 0 : account.id) + ":" + (folder == null ? 0 : folder.id);
@ -2146,7 +2146,7 @@ class Core {
if (ex instanceof AuthenticationFailedException || // Also: Too many simultaneous connections if (ex instanceof AuthenticationFailedException || // Also: Too many simultaneous connections
ex instanceof AlertException || ex instanceof AlertException ||
ex instanceof SendFailedException) ex instanceof SendFailedException)
nm.notify(tag, 1, getNotificationError(context, title, ex).build()); nm.notify(tag, 1, getNotificationError(context, "error", title, ex).build());
// connection failure: Too many simultaneous connections // connection failure: Too many simultaneous connections
@ -2164,18 +2164,12 @@ class Core {
!(ex instanceof MessagingException && ex.getCause() instanceof SocketTimeoutException) && !(ex instanceof MessagingException && ex.getCause() instanceof SocketTimeoutException) &&
!(ex instanceof MessagingException && ex.getCause() instanceof SSLException) && !(ex instanceof MessagingException && ex.getCause() instanceof SSLException) &&
!(ex instanceof MessagingException && "connection failure".equals(ex.getMessage()))) !(ex instanceof MessagingException && "connection failure".equals(ex.getMessage())))
nm.notify(tag, 1, getNotificationError(context, title, ex).build()); nm.notify(tag, 1, getNotificationError(context, "error", title, ex).build());
} }
static NotificationCompat.Builder getNotificationError(Context context, String title, Throwable ex) { static NotificationCompat.Builder getNotificationError(Context context, String channel, String title, Throwable ex) {
return getNotificationError(context, "error", title, ex, true);
}
static NotificationCompat.Builder getNotificationError(Context context, String channel, String title, Throwable ex, boolean debug) {
// Build pending intent // Build pending intent
Intent intent = new Intent(context, ActivityView.class); Intent intent = new Intent(context, ActivityView.class);
if (debug)
intent.setAction("error");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pi = PendingIntent.getActivity( PendingIntent pi = PendingIntent.getActivity(
context, ActivityView.REQUEST_ERROR, intent, PendingIntent.FLAG_UPDATE_CURRENT); context, ActivityView.REQUEST_ERROR, intent, PendingIntent.FLAG_UPDATE_CURRENT);

@ -156,6 +156,7 @@ public class EntityOperation {
boolean seen = message.seen; boolean seen = message.seen;
boolean ui_seen = message.ui_seen; boolean ui_seen = message.ui_seen;
boolean ui_browsed = message.ui_browsed; boolean ui_browsed = message.ui_browsed;
String error = message.error;
message.id = null; message.id = null;
message.account = target.account; message.account = target.account;
@ -167,6 +168,7 @@ public class EntityOperation {
message.ui_seen = true; message.ui_seen = true;
} }
message.ui_browsed = false; message.ui_browsed = false;
message.error = null;
message.id = db.message().insertMessage(message); message.id = db.message().insertMessage(message);
File mtarget = message.getFile(context); File mtarget = message.getFile(context);
long tmpid = message.id; long tmpid = message.id;
@ -179,6 +181,7 @@ public class EntityOperation {
message.seen = seen; message.seen = seen;
message.ui_seen = ui_seen; message.ui_seen = ui_seen;
message.ui_browsed = ui_browsed; message.ui_browsed = ui_browsed;
message.error = error;
if (message.content) if (message.content)
try { try {

@ -639,27 +639,8 @@ public class FragmentAccount extends FragmentBase {
if (ex instanceof IllegalArgumentException) if (ex instanceof IllegalArgumentException)
Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG).show(); Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG).show();
else { else
tvError.setText(Helper.formatThrowable(ex)); showError(ex);
tvError.setVisibility(View.VISIBLE);
final View target;
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());
}
});
}
} }
}.execute(FragmentAccount.this, args, "account:check"); }.execute(FragmentAccount.this, args, "account:check");
} }
@ -858,9 +839,11 @@ public class FragmentAccount extends FragmentBase {
String accountRealm = (account == null ? null : account.realm); String accountRealm = (account == null ? null : account.realm);
boolean check = (synchronize && (account == null || boolean check = (synchronize && (account == null ||
!account.synchronize ||
!host.equals(account.host) || Integer.parseInt(port) != account.port || !host.equals(account.host) || Integer.parseInt(port) != account.port ||
!user.equals(account.user) || !password.equals(account.password) || !user.equals(account.user) || !password.equals(account.password) ||
!Objects.equals(realm, accountRealm))); !Objects.equals(realm, accountRealm) ||
!TextUtils.isEmpty(account.error)));
boolean reload = (check || account == null || boolean reload = (check || account == null ||
account.synchronize != synchronize || account.synchronize != synchronize ||
account.notify != notify || account.notify != notify ||
@ -1078,20 +1061,34 @@ public class FragmentAccount extends FragmentBase {
protected void onException(Bundle args, Throwable ex) { protected void onException(Bundle args, Throwable ex) {
if (ex instanceof IllegalArgumentException) if (ex instanceof IllegalArgumentException)
Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG).show(); Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG).show();
else { else
tvError.setText(Helper.formatThrowable(ex)); showError(ex);
tvError.setVisibility(View.VISIBLE);
new Handler().post(new Runnable() {
@Override
public void run() {
scroll.smoothScrollTo(0, tvError.getBottom());
}
});
}
} }
}.execute(FragmentAccount.this, args, "account:save"); }.execute(FragmentAccount.this, args, "account:save");
} }
private void showError(Throwable ex) {
tvError.setText(Helper.formatThrowable(ex));
tvError.setVisibility(View.VISIBLE);
final View target;
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());
}
});
}
@Override @Override
public void onSaveInstanceState(Bundle outState) { public void onSaveInstanceState(Bundle outState) {
outState.putInt("fair:provider", spProvider.getSelectedItemPosition()); outState.putInt("fair:provider", spProvider.getSelectedItemPosition());

@ -29,6 +29,7 @@ import android.os.Handler;
import android.text.Editable; import android.text.Editable;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.text.method.LinkMovementMethod;
import android.util.Patterns; import android.util.Patterns;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
@ -117,6 +118,7 @@ public class FragmentIdentity extends FragmentBase {
private Button btnSave; private Button btnSave;
private ContentLoadingProgressBar pbSave; private ContentLoadingProgressBar pbSave;
private TextView tvError; private TextView tvError;
private TextView tvInstructions;
private ContentLoadingProgressBar pbWait; private ContentLoadingProgressBar pbWait;
@ -187,6 +189,8 @@ public class FragmentIdentity extends FragmentBase {
btnSave = view.findViewById(R.id.btnSave); btnSave = view.findViewById(R.id.btnSave);
pbSave = view.findViewById(R.id.pbSave); pbSave = view.findViewById(R.id.pbSave);
tvError = view.findViewById(R.id.tvError); tvError = view.findViewById(R.id.tvError);
tvInstructions = view.findViewById(R.id.tvInstructions);
tvInstructions.setMovementMethod(LinkMovementMethod.getInstance());
pbWait = view.findViewById(R.id.pbWait); pbWait = view.findViewById(R.id.pbWait);
@ -201,6 +205,7 @@ public class FragmentIdentity extends FragmentBase {
grpAuthorize.setVisibility(position > 0 ? View.VISIBLE : View.GONE); grpAuthorize.setVisibility(position > 0 ? View.VISIBLE : View.GONE);
if (position == 0) { if (position == 0) {
tvError.setVisibility(View.GONE); tvError.setVisibility(View.GONE);
tvInstructions.setVisibility(View.GONE);
grpAdvanced.setVisibility(View.GONE); grpAdvanced.setVisibility(View.GONE);
} }
tilPassword.setEndIconMode(position == 0 ? END_ICON_PASSWORD_TOGGLE : END_ICON_NONE); tilPassword.setEndIconMode(position == 0 ? END_ICON_PASSWORD_TOGGLE : END_ICON_NONE);
@ -429,6 +434,7 @@ public class FragmentIdentity extends FragmentBase {
btnSave.setVisibility(View.GONE); btnSave.setVisibility(View.GONE);
pbSave.setVisibility(View.GONE); pbSave.setVisibility(View.GONE);
tvError.setVisibility(View.GONE); tvError.setVisibility(View.GONE);
tvInstructions.setVisibility(View.GONE);
grpAuthorize.setVisibility(View.GONE); grpAuthorize.setVisibility(View.GONE);
grpAdvanced.setVisibility(View.GONE); grpAdvanced.setVisibility(View.GONE);
@ -525,6 +531,7 @@ public class FragmentIdentity extends FragmentBase {
Helper.setViewsEnabled(view, false); Helper.setViewsEnabled(view, false);
pbSave.setVisibility(View.VISIBLE); pbSave.setVisibility(View.VISIBLE);
tvError.setVisibility(View.GONE); tvError.setVisibility(View.GONE);
tvInstructions.setVisibility(View.GONE);
} }
@Override @Override
@ -659,10 +666,12 @@ public class FragmentIdentity extends FragmentBase {
String identityRealm = (identity == null ? null : identity.realm); String identityRealm = (identity == null ? null : identity.realm);
boolean check = (synchronize && (identity == null || boolean check = (synchronize && (identity == null ||
!identity.synchronize ||
!host.equals(identity.host) || Integer.parseInt(port) != identity.port || !host.equals(identity.host) || Integer.parseInt(port) != identity.port ||
!user.equals(identity.user) || !password.equals(identity.password) || !user.equals(identity.user) || !password.equals(identity.password) ||
!Objects.equals(realm, identityRealm) || !Objects.equals(realm, identityRealm) ||
use_ip != identity.use_ip)); use_ip != identity.use_ip) ||
!TextUtils.isEmpty(identity.error));
Long last_connected = null; Long last_connected = null;
if (identity != null && synchronize == identity.synchronize) if (identity != null && synchronize == identity.synchronize)
@ -784,20 +793,34 @@ public class FragmentIdentity extends FragmentBase {
protected void onException(Bundle args, Throwable ex) { protected void onException(Bundle args, Throwable ex) {
if (ex instanceof IllegalArgumentException) if (ex instanceof IllegalArgumentException)
Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG).show(); Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG).show();
else { else
tvError.setText(Helper.formatThrowable(ex)); showError(ex);
tvError.setVisibility(View.VISIBLE);
new Handler().post(new Runnable() {
@Override
public void run() {
scroll.smoothScrollTo(0, tvError.getBottom());
}
});
}
} }
}.execute(FragmentIdentity.this, args, "identity:save"); }.execute(FragmentIdentity.this, args, "identity:save");
} }
private void showError(Throwable ex) {
tvError.setText(Helper.formatThrowable(ex));
tvError.setVisibility(View.VISIBLE);
final View target;
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());
}
});
}
@Override @Override
public void onSaveInstanceState(Bundle outState) { public void onSaveInstanceState(Bundle outState) {
outState.putInt("fair:account", spAccount.getSelectedItemPosition()); outState.putInt("fair:account", spAccount.getSelectedItemPosition());

@ -416,7 +416,7 @@ public class ServiceSend extends LifecycleService {
Log.i("Reporting send error after=" + delayed); Log.i("Reporting send error after=" + delayed);
NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify("send", message.identity.intValue(), nm.notify("send", message.identity.intValue(),
Core.getNotificationError(this, ident.name, ex).build()); Core.getNotificationError(this, "error", ident.name, ex).build());
} }
throw ex; throw ex;

@ -711,7 +711,7 @@ public class ServiceSynchronize extends LifecycleService {
.format(account.last_connected)), ex); .format(account.last_connected)), ex);
NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify("receive", account.id.intValue(), nm.notify("receive", account.id.intValue(),
Core.getNotificationError(this, "warning", account.name, warning, false) Core.getNotificationError(this, "warning", account.name, warning)
.build()); .build());
} }
} }

@ -707,7 +707,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:text="provider instructions" android:text="provider instructions"
android:textAppearance="@style/TextAppearance.AppCompat.Small" android:textAppearance="@style/TextAppearance.AppCompat.Medium"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvError" /> app:layout_constraintTop_toBottomOf="@id/tvError" />

@ -36,10 +36,10 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:contentDescription="@string/title_legend_close_hint" android:contentDescription="@string/title_legend_close_hint"
app:srcCompat="@drawable/baseline_close_24"
app:layout_constraintBottom_toBottomOf="@id/tvHintActions" app:layout_constraintBottom_toBottomOf="@id/tvHintActions"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/tvHintActions" /> app:layout_constraintTop_toTopOf="@id/tvHintActions"
app:srcCompat="@drawable/baseline_close_24" />
<View <View
android:id="@+id/vSeparatorActions" android:id="@+id/vSeparatorActions"
@ -71,10 +71,10 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:contentDescription="@string/title_legend_close_hint" android:contentDescription="@string/title_legend_close_hint"
app:srcCompat="@drawable/baseline_close_24"
app:layout_constraintBottom_toBottomOf="@id/tvHintSync" app:layout_constraintBottom_toBottomOf="@id/tvHintSync"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/tvHintSync" /> app:layout_constraintTop_toTopOf="@id/tvHintSync"
app:srcCompat="@drawable/baseline_close_24" />
<View <View
android:id="@+id/vSeparatorSync" android:id="@+id/vSeparatorSync"
@ -135,10 +135,10 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="end|bottom" android:layout_gravity="end|bottom"
android:layout_margin="@dimen/fab_padding" android:layout_margin="@dimen/fab_padding"
app:srcCompat="@drawable/baseline_create_new_folder_24"
android:tint="@color/colorActionForeground" android:tint="@color/colorActionForeground"
android:tooltipText="@string/title_add" android:tooltipText="@string/title_add"
app:backgroundTint="?attr/colorAccent" /> app:backgroundTint="?attr/colorAccent"
app:srcCompat="@drawable/baseline_create_new_folder_24" />
<com.google.android.material.floatingactionbutton.FloatingActionButton <com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fabError" android:id="@+id/fabError"
@ -146,8 +146,8 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|bottom" android:layout_gravity="center_horizontal|bottom"
android:layout_margin="@dimen/fab_padding" android:layout_margin="@dimen/fab_padding"
v="@drawable/baseline_warning_24"
android:tint="@color/colorActionForeground" android:tint="@color/colorActionForeground"
android:tooltipText="@string/title_compose" android:tooltipText="@string/title_compose"
app:backgroundTint="@color/lightColorWarning" /> app:backgroundTint="@color/lightColorWarning"
app:srcCompat="@drawable/baseline_warning_24" />
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>

@ -612,6 +612,16 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btnSave" /> app:layout_constraintTop_toBottomOf="@id/btnSave" />
<TextView
android:id="@+id/tvInstructions"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="provider instructions"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvError" />
<eu.faircode.email.ContentLoadingProgressBar <eu.faircode.email.ContentLoadingProgressBar
android:id="@+id/pbWait" android:id="@+id/pbWait"
style="@style/Base.Widget.AppCompat.ProgressBar" style="@style/Base.Widget.AppCompat.ProgressBar"

@ -101,7 +101,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:text="provider instructions" android:text="provider instructions"
android:textAppearance="@style/TextAppearance.AppCompat.Small" android:textAppearance="@style/TextAppearance.AppCompat.Medium"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvError" /> app:layout_constraintTop_toBottomOf="@id/tvError" />

Loading…
Cancel
Save