Removed select account

This commit is contained in:
M66B 2019-06-19 14:51:19 +02:00
parent 4e72ae5a16
commit eddfad1e25
24 changed files with 1896 additions and 604 deletions

2
FAQ.md
View File

@ -198,9 +198,7 @@ The following Android permissions are needed:
* *foreground service* (FOREGROUND_SERVICE): to run a foreground service on Android 9 Pie and later, see also the next question
* *prevent device from sleeping* (WAKE_LOCK): to keep the device awake while synchronizing messages
* Optional: *read your contacts* (READ_CONTACTS): to autocomplete addresses and to show photos
* Optional: *find accounts on the device* (GET_ACCOUNTS): to use [OAuth](https://en.wikipedia.org/wiki/OAuth) instead of passwords
* Optional: *read the contents of your SD card* (READ_EXTERNAL_STORAGE): to accept files from other, outdated apps, see also [this FAQ](#user-content-faq49)
* Android 5.1 Lollipop and before: *use accounts on the device* (USE_CREDENTIALS): needed to select accounts (not used/needed on later Android versions)
The following permissions are needed to show the count of unread messages as a badge (see also [this FAQ](#user-content-faq106)):

File diff suppressed because it is too large Load Diff

View File

@ -6,10 +6,6 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission
android:name="android.permission.USE_CREDENTIALS"
android:maxSdkVersion="22" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />

View File

@ -98,8 +98,7 @@ public class ActivityEml extends ActivityBase {
AssetFileDescriptor descriptor = resolver.openTypedAssetFileDescriptor(uri, "*/*", null);
try (InputStream is = new BufferedInputStream(descriptor.createInputStream())) {
Properties props = MessageHelper.getSessionProperties(
ConnectionHelper.AUTH_TYPE_PASSWORD, null, false);
Properties props = MessageHelper.getSessionProperties(null, false);
Session isession = Session.getInstance(props, null);
MimeMessage mmessage = new MimeMessage(isession, is);

View File

@ -108,12 +108,9 @@ public class ActivitySetup extends ActivityBilling implements FragmentManager.On
private static final int KEY_LENGTH = 256;
static final int REQUEST_PERMISSION = 1;
static final int REQUEST_CHOOSE_ACCOUNT = 2;
static final int REQUEST_SOUND = 3;
static final int REQUEST_EXPORT = 4;
static final int REQUEST_IMPORT = 5;
static final int REQUEST_SOUND = 2;
static final int REQUEST_EXPORT = 3;
static final int REQUEST_IMPORT = 4;
static final String ACTION_QUICK_SETUP = BuildConfig.APPLICATION_ID + ".ACTION_QUICK_SETUP";
static final String ACTION_VIEW_ACCOUNTS = BuildConfig.APPLICATION_ID + ".ACTION_VIEW_ACCOUNTS";

View File

@ -77,6 +77,7 @@ public class AdapterAccount extends RecyclerView.Adapter<AdapterAccount.ViewHold
private ImageView ivState;
private TextView tvHost;
private TextView tvLast;
private TextView tvAuthorize;
private TextView tvIdentity;
private TextView tvDrafts;
private TextView tvWarning;
@ -98,6 +99,7 @@ public class AdapterAccount extends RecyclerView.Adapter<AdapterAccount.ViewHold
ivState = itemView.findViewById(R.id.ivState);
tvHost = itemView.findViewById(R.id.tvHost);
tvLast = itemView.findViewById(R.id.tvLast);
tvAuthorize = itemView.findViewById(R.id.tvAuthorize);
tvIdentity = itemView.findViewById(R.id.tvIdentity);
tvDrafts = itemView.findViewById(R.id.tvDrafts);
tvWarning = itemView.findViewById(R.id.tvWarning);
@ -152,6 +154,7 @@ public class AdapterAccount extends RecyclerView.Adapter<AdapterAccount.ViewHold
tvLast.setText(context.getString(R.string.title_last_connected,
account.last_connected == null ? "-" : df.format(account.last_connected)));
tvAuthorize.setVisibility(account.auth_type == ConnectionHelper.AUTH_TYPE_PASSWORD ? View.GONE : View.VISIBLE);
tvIdentity.setVisibility(account.identities > 0 || !settings ? View.GONE : View.VISIBLE);
tvDrafts.setVisibility(account.drafts || !settings ? View.GONE : View.VISIBLE);

View File

@ -68,6 +68,7 @@ public class AdapterIdentity extends RecyclerView.Adapter<AdapterIdentity.ViewHo
private ImageView ivState;
private TextView tvAccount;
private TextView tvLast;
private TextView tvAuthorize;
private TextView tvError;
private TwoStateOwner powner = new TwoStateOwner(owner, "IdentityPopup");
@ -85,6 +86,7 @@ public class AdapterIdentity extends RecyclerView.Adapter<AdapterIdentity.ViewHo
ivState = itemView.findViewById(R.id.ivState);
tvAccount = itemView.findViewById(R.id.tvAccount);
tvLast = itemView.findViewById(R.id.tvLast);
tvAuthorize = itemView.findViewById(R.id.tvAuthorize);
tvError = itemView.findViewById(R.id.tvError);
}
@ -121,6 +123,8 @@ public class AdapterIdentity extends RecyclerView.Adapter<AdapterIdentity.ViewHo
tvLast.setText(context.getString(R.string.title_last_connected,
identity.last_connected == null ? "-" : df.format(identity.last_connected)));
tvAuthorize.setVisibility(identity.auth_type == ConnectionHelper.AUTH_TYPE_PASSWORD ? View.GONE : View.VISIBLE);
tvError.setText(identity.error);
tvError.setVisibility(identity.error == null ? View.GONE : View.VISIBLE);
}

View File

@ -19,8 +19,6 @@ package eu.faircode.email;
Copyright 2018-2019 by Marcel Bokhorst (M66B)
*/
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Handler;
@ -232,7 +230,7 @@ public class BoundaryCallbackMessages extends PagedList.BoundaryCallback<TupleMe
return found;
}
private int load_server() throws MessagingException, IOException, AuthenticatorException, OperationCanceledException {
private int load_server() throws MessagingException, IOException {
DB db = DB.getInstance(context);
final EntityFolder browsable = db.folder().getBrowsableFolder(folder, query != null);
@ -255,7 +253,7 @@ public class BoundaryCallbackMessages extends PagedList.BoundaryCallback<TupleMe
String protocol = account.getProtocol();
// Get properties
Properties props = MessageHelper.getSessionProperties(account.auth_type, account.realm, account.insecure);
Properties props = MessageHelper.getSessionProperties(account.realm, account.insecure);
if (!account.partial_fetch) {
props.put("mail.imap.partialfetch", "false");
props.put("mail.imaps.partialfetch", "false");

View File

@ -1,9 +1,5 @@
package eu.faircode.email;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.content.Context;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
@ -20,7 +16,6 @@ import com.bugsnag.android.BreadcrumbType;
import com.bugsnag.android.Bugsnag;
import com.sun.mail.imap.IMAPStore;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
@ -28,7 +23,6 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.mail.AuthenticationFailedException;
import javax.mail.MessagingException;
public class ConnectionHelper {
@ -244,18 +238,8 @@ public class ConnectionHelper {
return true;
}
static void connect(Context context, IMAPStore istore, EntityAccount account)
throws MessagingException, AuthenticatorException, OperationCanceledException, IOException {
try {
istore.connect(account.host, account.port, account.user, account.password);
} catch (AuthenticationFailedException ex) {
if (account.auth_type == AUTH_TYPE_GMAIL) {
account.password = refreshToken(context, "com.google", account.user, account.password);
DB.getInstance(context).account().setAccountPassword(account.id, account.password);
istore.connect(account.host, account.port, account.user, account.password);
} else
throw ex;
}
static void connect(Context context, IMAPStore istore, EntityAccount account) throws MessagingException {
istore.connect(account.host, account.port, account.user, account.password);
// https://www.ietf.org/rfc/rfc2971.txt
if (istore.hasCapability("ID"))
@ -277,33 +261,6 @@ public class ConnectionHelper {
}
}
static String refreshToken(Context context, String type, String name, String current)
throws AuthenticatorException, OperationCanceledException, IOException {
if (!Helper.hasValidFingerprint(context))
throw new IllegalArgumentException("Please see the FAQ question 109");
AccountManager am = AccountManager.get(context);
Account[] accounts = am.getAccountsByType(type);
for (Account account : accounts)
if (name.equals(account.name)) {
Log.i("Refreshing token");
am.invalidateAuthToken(type, current);
String refreshed = am.blockingGetAuthToken(account, getAuthTokenType(type), true);
if (refreshed == null)
throw new OperationCanceledException("no token");
Log.i("Refreshed token");
return refreshed;
}
return current;
}
static String getAuthTokenType(String type) {
// https://developers.google.com/gmail/imap/xoauth2-protocol
if ("com.google".equals(type))
return "oauth2:https://mail.google.com/";
return null;
}
static boolean airplaneMode(Context context) {
return Settings.System.getInt(context.getContentResolver(),
Settings.Global.AIRPLANE_MODE_ON, 0) != 0;

View File

@ -54,7 +54,7 @@ import io.requery.android.database.sqlite.RequerySQLiteOpenHelperFactory;
// https://developer.android.com/topic/libraries/architecture/room.html
@Database(
version = 88,
version = 89,
entities = {
EntityIdentity.class,
EntityAccount.class,
@ -876,6 +876,14 @@ public abstract class DB extends RoomDatabase {
db.execSQL("ALTER TABLE `account` ADD COLUMN `partial_fetch` INTEGER NOT NULL DEFAULT 1");
}
})
.addMigrations(new Migration(88, 89) {
@Override
public void migrate(SupportSQLiteDatabase db) {
Log.i("DB migration from version " + startVersion + " to " + endVersion);
db.execSQL("UPDATE account SET synchronize = 0 WHERE auth_type = " + ConnectionHelper.AUTH_TYPE_GMAIL);
db.execSQL("UPDATE identity SET synchronize = 0 WHERE auth_type = " + ConnectionHelper.AUTH_TYPE_GMAIL);
}
})
.build();
}

View File

@ -119,6 +119,7 @@ public class EmailProvider {
throw new IllegalAccessException(name);
} else if (eventType == XmlPullParser.END_TAG) {
if ("provider".equals(xml.getName())) {
addSpecials(context, provider);
result.add(provider);
provider = null;
}
@ -411,12 +412,12 @@ public class EmailProvider {
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_setting_gmail));
context.getString(R.string.title_setup_instructions));
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_setting_yahoo));
context.getString(R.string.title_setup_instructions));
return provider;
}
@ -440,12 +441,6 @@ public class EmailProvider {
return (records == null || records.length == 0 ? null : (SRVRecord) records[0]);
}
int getAuthType() {
if ("com.google".equals(type))
return ConnectionHelper.AUTH_TYPE_GMAIL;
return ConnectionHelper.AUTH_TYPE_PASSWORD;
}
@Override
public String toString() {
return name;

View File

@ -19,28 +19,19 @@ package eu.faircode.email;
Copyright 2018-2019 by Marcel Bokhorst (M66B)
*/
import android.Manifest;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AccountManagerCallback;
import android.accounts.AccountManagerFuture;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.app.Activity;
import android.app.NotificationManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
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;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.method.LinkMovementMethod;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@ -62,7 +53,6 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.Group;
import androidx.lifecycle.Lifecycle;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.android.colorpicker.ColorPickerDialog;
@ -73,7 +63,6 @@ import com.sun.mail.imap.IMAPFolder;
import com.sun.mail.imap.IMAPStore;
import com.sun.mail.imap.protocol.IMAPProtocol;
import java.io.IOException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
@ -82,12 +71,10 @@ import java.util.List;
import java.util.Objects;
import java.util.Properties;
import javax.mail.AuthenticationFailedException;
import javax.mail.Folder;
import javax.mail.Session;
import javax.mail.Store;
import static android.accounts.AccountManager.newChooseAccountIntent;
import static com.google.android.material.textfield.TextInputLayout.END_ICON_NONE;
import static com.google.android.material.textfield.TextInputLayout.END_ICON_PASSWORD_TOGGLE;
@ -100,8 +87,6 @@ public class FragmentAccount extends FragmentBase {
private EditText etDomain;
private Button btnAutoConfig;
private Button btnAuthorize;
private TextView tvAuthorizeOptional;
private EditText etHost;
private RadioGroup rgEncryption;
private CheckBox cbInsecure;
@ -140,6 +125,7 @@ public class FragmentAccount extends FragmentBase {
private Button btnSave;
private ContentLoadingProgressBar pbSave;
private TextView tvError;
private TextView tvInstructions;
private ContentLoadingProgressBar pbWait;
@ -150,7 +136,6 @@ public class FragmentAccount extends FragmentBase {
private long id = -1;
private boolean saving = false;
private int auth_type = ConnectionHelper.AUTH_TYPE_PASSWORD;
private int color = Color.TRANSPARENT;
@Override
@ -177,8 +162,6 @@ public class FragmentAccount extends FragmentBase {
etDomain = view.findViewById(R.id.etDomain);
btnAutoConfig = view.findViewById(R.id.btnAutoConfig);
btnAuthorize = view.findViewById(R.id.btnAuthorize);
tvAuthorizeOptional = view.findViewById(R.id.tvAuthorizeOptional);
etHost = view.findViewById(R.id.etHost);
etPort = view.findViewById(R.id.etPort);
rgEncryption = view.findViewById(R.id.rgEncryption);
@ -217,6 +200,7 @@ public class FragmentAccount extends FragmentBase {
btnSave = view.findViewById(R.id.btnSave);
pbSave = view.findViewById(R.id.pbSave);
tvError = view.findViewById(R.id.tvError);
tvInstructions = view.findViewById(R.id.tvInstructions);
pbWait = view.findViewById(R.id.pbWait);
@ -234,9 +218,6 @@ public class FragmentAccount extends FragmentBase {
grpServer.setVisibility(position == 1 ? View.VISIBLE : View.GONE);
grpAuthorize.setVisibility(position > 0 ? View.VISIBLE : View.GONE);
btnAuthorize.setVisibility(provider.type == null ? View.GONE : View.VISIBLE);
tvAuthorizeOptional.setVisibility(provider.type == null ? View.GONE : View.VISIBLE);
btnAdvanced.setVisibility(position > 0 ? View.VISIBLE : View.GONE);
if (position == 0)
grpAdvanced.setVisibility(View.GONE);
@ -250,8 +231,6 @@ public class FragmentAccount extends FragmentBase {
return;
adapterView.setTag(position);
auth_type = ConnectionHelper.AUTH_TYPE_PASSWORD;
etHost.setText(provider.imap_host);
etPort.setText(provider.imap_host == null ? null : Integer.toString(provider.imap_port));
rgEncryption.check(provider.imap_starttls ? R.id.radio_starttls : R.id.radio_ssl);
@ -260,8 +239,6 @@ public class FragmentAccount extends FragmentBase {
etUser.setText(null);
tilPassword.getEditText().setText(null);
etRealm.setText(null);
tilPassword.setEnabled(true);
etRealm.setEnabled(true);
etName.setText(position > 1 ? provider.name : null);
@ -303,28 +280,6 @@ public class FragmentAccount extends FragmentBase {
}
});
etUser.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
String user = etUser.getText().toString();
if (auth_type != ConnectionHelper.AUTH_TYPE_PASSWORD && !user.equals(etUser.getTag())) {
auth_type = ConnectionHelper.AUTH_TYPE_PASSWORD;
tilPassword.getEditText().setText(null);
tilPassword.setEnabled(true);
tilPassword.setEndIconMode(END_ICON_PASSWORD_TOGGLE);
etRealm.setEnabled(true);
}
}
@Override
public void afterTextChanged(Editable s) {
}
});
setColor(color);
btnColor.setOnClickListener(new View.OnClickListener() {
@Override
@ -355,39 +310,6 @@ public class FragmentAccount extends FragmentBase {
}
});
btnAuthorize.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
EmailProvider provider = (EmailProvider) spProvider.getSelectedItem();
Log.i("Authorize " + provider);
if ("com.google".equals(provider.type)) {
if (!Helper.hasValidFingerprint(getContext())) {
Snackbar snackbar = Snackbar.make(view, R.string.title_no_xoauth2, Snackbar.LENGTH_LONG);
final Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(Helper.FAQ_URI + "#user-content-faq109"));
if (intent.resolveActivity(getContext().getPackageManager()) != null)
snackbar.setAction(R.string.title_info, new View.OnClickListener() {
@Override
public void onClick(View view) {
startActivity(intent);
}
});
snackbar.show();
return;
}
String permission = Manifest.permission.GET_ACCOUNTS;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O &&
!Helper.hasPermission(getContext(), permission)) {
Log.i("Requesting " + permission);
requestPermissions(new String[]{permission}, ActivitySetup.REQUEST_PERMISSION);
} else
selectAccount();
}
}
});
btnAdvanced.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@ -465,8 +387,6 @@ public class FragmentAccount extends FragmentBase {
btnAutoConfig.setEnabled(false);
btnAuthorize.setVisibility(View.GONE);
tvAuthorizeOptional.setVisibility(View.GONE);
rgEncryption.setVisibility(View.GONE);
cbInsecure.setVisibility(View.GONE);
tilPassword.setEndIconMode(id < 0 ? END_ICON_PASSWORD_TOGGLE : END_ICON_NONE);
@ -482,6 +402,8 @@ public class FragmentAccount extends FragmentBase {
btnSave.setVisibility(View.GONE);
pbSave.setVisibility(View.GONE);
tvError.setVisibility(View.GONE);
tvInstructions.setVisibility(View.GONE);
tvInstructions.setMovementMethod(LinkMovementMethod.getInstance());
grpServer.setVisibility(View.GONE);
grpAuthorize.setVisibility(View.GONE);
@ -534,7 +456,6 @@ public class FragmentAccount extends FragmentBase {
private void onCheck() {
Bundle args = new Bundle();
args.putLong("id", id);
args.putInt("auth_type", auth_type);
args.putString("host", etHost.getText().toString());
args.putBoolean("starttls", rgEncryption.getCheckedRadioButtonId() == R.id.radio_starttls);
args.putBoolean("insecure", cbInsecure.isChecked());
@ -554,6 +475,7 @@ public class FragmentAccount extends FragmentBase {
tvUtf8.setVisibility(View.GONE);
grpFolders.setVisibility(View.GONE);
tvError.setVisibility(View.GONE);
tvInstructions.setVisibility(View.GONE);
}
@Override
@ -567,7 +489,6 @@ public class FragmentAccount extends FragmentBase {
@Override
protected CheckResult onExecute(Context context, Bundle args) throws Throwable {
long id = args.getLong("id");
int auth_type = args.getInt("auth_type");
String host = args.getString("host");
boolean starttls = args.getBoolean("starttls");
boolean insecure = args.getBoolean("insecure");
@ -595,19 +516,11 @@ public class FragmentAccount extends FragmentBase {
result.folders = new ArrayList<>();
// Check IMAP server / get folders
Properties props = MessageHelper.getSessionProperties(auth_type, realm, insecure);
Properties props = MessageHelper.getSessionProperties(realm, insecure);
Session isession = Session.getInstance(props, null);
isession.setDebug(true);
try (Store istore = isession.getStore("imap" + (starttls ? "" : "s"))) {
try {
istore.connect(host, Integer.parseInt(port), user, password);
} catch (AuthenticationFailedException ex) {
if (auth_type == ConnectionHelper.AUTH_TYPE_GMAIL) {
password = ConnectionHelper.refreshToken(context, "com.google", user, password);
istore.connect(host, Integer.parseInt(port), user, password);
} else
throw ex;
}
istore.connect(host, Integer.parseInt(port), user, password);
result.idle = ((IMAPStore) istore).hasCapability("IDLE");
@ -729,10 +642,21 @@ public class FragmentAccount extends FragmentBase {
else {
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, tvError.getBottom());
scroll.smoothScrollTo(0, target.getBottom());
}
});
}
@ -767,7 +691,6 @@ public class FragmentAccount extends FragmentBase {
Bundle args = new Bundle();
args.putLong("id", id);
args.putInt("auth_type", auth_type);
args.putString("host", etHost.getText().toString());
args.putBoolean("starttls", rgEncryption.getCheckedRadioButtonId() == R.id.radio_starttls);
args.putBoolean("insecure", cbInsecure.isChecked());
@ -818,7 +741,6 @@ public class FragmentAccount extends FragmentBase {
protected Boolean onExecute(Context context, Bundle args) throws Throwable {
long id = args.getLong("id");
int auth_type = args.getInt("auth_type");
String host = args.getString("host");
boolean starttls = args.getBoolean("starttls");
boolean insecure = args.getBoolean("insecure");
@ -874,8 +796,6 @@ public class FragmentAccount extends FragmentBase {
if (account == null)
return !TextUtils.isEmpty(host) && !TextUtils.isEmpty(user);
if (!Objects.equals(account.auth_type, auth_type))
return true;
if (!Objects.equals(account.host, host))
return true;
if (!Objects.equals(account.starttls, starttls))
@ -938,7 +858,6 @@ public class FragmentAccount extends FragmentBase {
String accountRealm = (account == null ? null : account.realm);
boolean check = (synchronize && (account == null ||
auth_type != account.auth_type ||
!host.equals(account.host) || Integer.parseInt(port) != account.port ||
!user.equals(account.user) || !password.equals(account.password) ||
!Objects.equals(realm, accountRealm)));
@ -955,20 +874,12 @@ public class FragmentAccount extends FragmentBase {
// Check IMAP server
EntityFolder inbox = null;
if (check) {
Properties props = MessageHelper.getSessionProperties(auth_type, realm, insecure);
Properties props = MessageHelper.getSessionProperties(realm, insecure);
Session isession = Session.getInstance(props, null);
isession.setDebug(true);
try (Store istore = isession.getStore("imap" + (starttls ? "" : "s"))) {
try {
istore.connect(host, Integer.parseInt(port), user, password);
} catch (AuthenticationFailedException ex) {
if (auth_type == ConnectionHelper.AUTH_TYPE_GMAIL) {
password = ConnectionHelper.refreshToken(context, "com.google", user, password);
istore.connect(host, Integer.parseInt(port), user, password);
} else
throw ex;
}
istore.connect(host, Integer.parseInt(port), user, password);
for (Folder ifolder : istore.getDefaultFolder().list("*")) {
// Check folder attributes
@ -998,7 +909,7 @@ public class FragmentAccount extends FragmentBase {
if (account == null)
account = new EntityAccount();
account.auth_type = auth_type;
account.auth_type = ConnectionHelper.AUTH_TYPE_PASSWORD;
account.host = host;
account.starttls = starttls;
account.insecure = insecure;
@ -1178,7 +1089,6 @@ public class FragmentAccount extends FragmentBase {
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putInt("fair:provider", spProvider.getSelectedItemPosition());
outState.putInt("fair:auth_type", auth_type);
outState.putString("fair:password", tilPassword.getEditText().getText().toString());
outState.putInt("fair:advanced", grpAdvanced.getVisibility());
outState.putInt("fair:color", color);
@ -1212,8 +1122,6 @@ public class FragmentAccount extends FragmentBase {
spProvider.setAdapter(aaProvider);
if (savedInstanceState == null) {
auth_type = (account == null ? ConnectionHelper.AUTH_TYPE_PASSWORD : account.auth_type);
if (account != null) {
boolean found = false;
for (int pos = 2; pos < providers.size(); pos++) {
@ -1237,7 +1145,6 @@ public class FragmentAccount extends FragmentBase {
rgEncryption.check(account != null && account.starttls ? R.id.radio_starttls : R.id.radio_ssl);
cbInsecure.setChecked(account == null ? false : account.insecure);
etUser.setTag(account == null || auth_type == ConnectionHelper.AUTH_TYPE_PASSWORD ? null : account.user);
etUser.setText(account == null ? null : account.user);
tilPassword.getEditText().setText(account == null ? null : account.password);
etRealm.setText(account == null ? null : account.realm);
@ -1275,7 +1182,6 @@ public class FragmentAccount extends FragmentBase {
spProvider.setTag(provider);
spProvider.setSelection(provider);
auth_type = savedInstanceState.getInt("fair:auth_type");
tilPassword.getEditText().setText(savedInstanceState.getString("fair:password"));
grpAdvanced.setVisibility(savedInstanceState.getInt("fair:advanced"));
color = savedInstanceState.getInt("fair:color");
@ -1283,9 +1189,6 @@ public class FragmentAccount extends FragmentBase {
Helper.setViewsEnabled(view, true);
tilPassword.setEnabled(auth_type == ConnectionHelper.AUTH_TYPE_PASSWORD);
etRealm.setEnabled(auth_type == ConnectionHelper.AUTH_TYPE_PASSWORD);
setColor(color);
cbPrimary.setEnabled(cbSynchronize.isChecked());
@ -1396,103 +1299,6 @@ public class FragmentAccount extends FragmentBase {
.show();
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode == ActivitySetup.REQUEST_PERMISSION)
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
selectAccount();
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK)
if (requestCode == ActivitySetup.REQUEST_CHOOSE_ACCOUNT) {
String name = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
String type = data.getStringExtra(AccountManager.KEY_ACCOUNT_TYPE);
AccountManager am = AccountManager.get(getContext());
Account[] accounts = am.getAccountsByType(type);
Log.i("Accounts=" + accounts.length);
for (final Account account : accounts)
if (name.equals(account.name)) {
btnAuthorize.setEnabled(false);
etUser.setEnabled(false);
tilPassword.setEnabled(false);
etRealm.setEnabled(false);
btnCheck.setEnabled(false);
btnSave.setEnabled(false);
final Snackbar snackbar = Snackbar.make(view, R.string.title_authorizing, Snackbar.LENGTH_SHORT);
snackbar.show();
am.getAuthToken(
account,
ConnectionHelper.getAuthTokenType(type),
new Bundle(),
getActivity(),
new AccountManagerCallback<Bundle>() {
@Override
public void run(AccountManagerFuture<Bundle> future) {
try {
Bundle bundle = future.getResult();
String token = bundle.getString(AccountManager.KEY_AUTHTOKEN);
Log.i("Got token");
auth_type = ConnectionHelper.AUTH_TYPE_GMAIL;
etUser.setTag(account.name);
etUser.setText(account.name);
etUser.setTag(account.name);
tilPassword.getEditText().setText(token);
etRealm.setText(null);
} catch (Throwable ex) {
Log.e(ex);
if (ex instanceof OperationCanceledException ||
ex instanceof AuthenticatorException ||
ex instanceof IOException) {
if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED))
Snackbar.make(view, Helper.formatThrowable(ex), Snackbar.LENGTH_LONG).show();
} else
Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex);
} finally {
btnAuthorize.setEnabled(true);
etUser.setEnabled(true);
tilPassword.setEnabled(auth_type == ConnectionHelper.AUTH_TYPE_PASSWORD);
tilPassword.setEndIconMode(auth_type == ConnectionHelper.AUTH_TYPE_PASSWORD
? END_ICON_PASSWORD_TOGGLE : END_ICON_NONE);
etRealm.setEnabled(auth_type == ConnectionHelper.AUTH_TYPE_PASSWORD);
btnCheck.setEnabled(true);
btnSave.setEnabled(true);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
snackbar.dismiss();
}
}, 1000);
}
}
},
null);
break;
}
}
}
private void selectAccount() {
Log.i("Select account");
EmailProvider provider = (EmailProvider) spProvider.getSelectedItem();
if (provider.type != null)
startActivityForResult(
Helper.getChooser(getContext(), newChooseAccountIntent(
null,
null,
new String[]{provider.type},
false,
null,
null,
null,
null)),
ActivitySetup.REQUEST_CHOOSE_ACCOUNT);
}
private void setColor(int color) {
this.color = color;

View File

@ -1384,8 +1384,7 @@ public class FragmentCompose extends FragmentBase {
(message.identity == null ? null : db.identity().getIdentity(message.identity));
// Build message
Properties props = MessageHelper.getSessionProperties(
ConnectionHelper.AUTH_TYPE_PASSWORD, null, false);
Properties props = MessageHelper.getSessionProperties(null, false);
Session isession = Session.getInstance(props, null);
MimeMessage imessage = new MimeMessage(isession);
MessageHelper.build(context, message, attachments, identity, imessage);

View File

@ -67,7 +67,6 @@ import java.util.List;
import java.util.Objects;
import java.util.Properties;
import javax.mail.AuthenticationFailedException;
import javax.mail.Session;
import javax.mail.Transport;
@ -126,7 +125,6 @@ public class FragmentIdentity extends FragmentBase {
private long id = -1;
private boolean saving = false;
private int auth_type = ConnectionHelper.AUTH_TYPE_PASSWORD;
private int color = Color.TRANSPARENT;
private String signature = null;
@ -213,7 +211,6 @@ public class FragmentIdentity extends FragmentBase {
adapterView.setTag(position);
EntityAccount account = (EntityAccount) adapterView.getAdapter().getItem(position);
auth_type = account.auth_type;
// Select associated provider
if (position == 0)
@ -242,12 +239,9 @@ public class FragmentIdentity extends FragmentBase {
// Copy account credentials
etEmail.setText(account.user);
etUser.setTag(auth_type == ConnectionHelper.AUTH_TYPE_PASSWORD ? null : account.user);
etUser.setText(account.user);
tilPassword.getEditText().setText(account.password);
etRealm.setText(account.realm);
tilPassword.setEnabled(auth_type == ConnectionHelper.AUTH_TYPE_PASSWORD);
etRealm.setEnabled(auth_type == ConnectionHelper.AUTH_TYPE_PASSWORD);
}
@Override
@ -255,27 +249,6 @@ public class FragmentIdentity extends FragmentBase {
}
});
etUser.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
String user = etUser.getText().toString();
if (auth_type != ConnectionHelper.AUTH_TYPE_PASSWORD && !user.equals(etUser.getTag())) {
auth_type = ConnectionHelper.AUTH_TYPE_PASSWORD;
tilPassword.getEditText().setText(null);
tilPassword.setEnabled(true);
etRealm.setEnabled(true);
}
}
@Override
public void afterTextChanged(Editable s) {
}
});
setColor(color);
btnColor.setOnClickListener(new View.OnClickListener() {
@Override
@ -529,7 +502,6 @@ public class FragmentIdentity extends FragmentBase {
args.putBoolean("read_receipt", cbReadReceipt.isChecked());
args.putBoolean("store_sent", cbStoreSent.isChecked());
args.putLong("account", account == null ? -1 : account.id);
args.putInt("auth_type", auth_type);
args.putString("host", etHost.getText().toString());
args.putBoolean("starttls", rgEncryption.getCheckedRadioButtonId() == R.id.radio_starttls);
args.putBoolean("insecure", cbInsecure.isChecked());
@ -574,7 +546,6 @@ public class FragmentIdentity extends FragmentBase {
Integer color = args.getInt("color");
String signature = args.getString("signature");
int auth_type = args.getInt("auth_type");
String host = args.getString("host");
boolean starttls = args.getBoolean("starttls");
boolean insecure = args.getBoolean("insecure");
@ -647,8 +618,6 @@ public class FragmentIdentity extends FragmentBase {
return true;
if (!Objects.equals(identity.signature, signature))
return true;
if (!Objects.equals(identity.auth_type, auth_type))
return true;
if (!Objects.equals(identity.host, host))
return true;
if (!Objects.equals(identity.starttls, starttls))
@ -690,7 +659,6 @@ public class FragmentIdentity extends FragmentBase {
String identityRealm = (identity == null ? null : identity.realm);
boolean check = (synchronize && (identity == null ||
auth_type != identity.auth_type ||
!host.equals(identity.host) || Integer.parseInt(port) != identity.port ||
!user.equals(identity.user) || !password.equals(identity.password) ||
!Objects.equals(realm, identityRealm) ||
@ -705,7 +673,7 @@ public class FragmentIdentity extends FragmentBase {
String protocol = (starttls ? "smtp" : "smtps");
// Get properties
Properties props = MessageHelper.getSessionProperties(auth_type, realm, insecure);
Properties props = MessageHelper.getSessionProperties(realm, insecure);
String haddr;
if (use_ip) {
@ -726,15 +694,7 @@ public class FragmentIdentity extends FragmentBase {
// Create transport
try (Transport itransport = isession.getTransport(protocol)) {
try {
itransport.connect(host, Integer.parseInt(port), user, password);
} catch (AuthenticationFailedException ex) {
if (auth_type == ConnectionHelper.AUTH_TYPE_GMAIL) {
password = ConnectionHelper.refreshToken(context, "com.google", user, password);
itransport.connect(host, Integer.parseInt(port), user, password);
} else
throw ex;
}
itransport.connect(host, Integer.parseInt(port), user, password);
}
}
@ -751,7 +711,7 @@ public class FragmentIdentity extends FragmentBase {
identity.color = color;
identity.signature = signature;
identity.auth_type = auth_type;
identity.auth_type = ConnectionHelper.AUTH_TYPE_PASSWORD;
identity.host = host;
identity.starttls = starttls;
identity.insecure = insecure;
@ -836,7 +796,6 @@ public class FragmentIdentity extends FragmentBase {
public void onSaveInstanceState(Bundle outState) {
outState.putInt("fair:account", spAccount.getSelectedItemPosition());
outState.putInt("fair:provider", spProvider.getSelectedItemPosition());
outState.putInt("fair:auth_type", auth_type);
outState.putString("fair:password", tilPassword.getEditText().getText().toString());
outState.putInt("fair:advanced", grpAdvanced.getVisibility());
outState.putInt("fair:color", color);
@ -860,8 +819,6 @@ public class FragmentIdentity extends FragmentBase {
@Override
protected void onExecuted(Bundle args, final EntityIdentity identity) {
if (savedInstanceState == null) {
auth_type = (identity == null ? ConnectionHelper.AUTH_TYPE_PASSWORD : identity.auth_type);
etName.setText(identity == null ? null : identity.name);
etEmail.setText(identity == null ? null : identity.email);
@ -875,7 +832,6 @@ public class FragmentIdentity extends FragmentBase {
rgEncryption.check(identity != null && identity.starttls ? R.id.radio_starttls : R.id.radio_ssl);
cbInsecure.setChecked(identity == null ? false : identity.insecure);
etPort.setText(identity == null ? null : Long.toString(identity.port));
etUser.setTag(identity == null || auth_type == ConnectionHelper.AUTH_TYPE_PASSWORD ? null : identity.user);
etUser.setText(identity == null ? null : identity.user);
tilPassword.getEditText().setText(identity == null ? null : identity.password);
etRealm.setText(identity == null ? null : identity.realm);
@ -913,7 +869,6 @@ public class FragmentIdentity extends FragmentBase {
}
}.execute(FragmentIdentity.this, new Bundle(), "identity:count");
} else {
auth_type = savedInstanceState.getInt("fair:auth_type");
tilPassword.getEditText().setText(savedInstanceState.getString("fair:password"));
grpAdvanced.setVisibility(savedInstanceState.getInt("fair:advanced"));
color = savedInstanceState.getInt("fair:color");
@ -921,9 +876,6 @@ public class FragmentIdentity extends FragmentBase {
Helper.setViewsEnabled(view, true);
tilPassword.setEnabled(auth_type == ConnectionHelper.AUTH_TYPE_PASSWORD);
etRealm.setEnabled(auth_type == ConnectionHelper.AUTH_TYPE_PASSWORD);
setColor(color);
cbPrimary.setEnabled(cbSynchronize.isChecked());
@ -980,9 +932,6 @@ public class FragmentIdentity extends FragmentBase {
if (account.id.equals((identity == null ? -1 : identity.account))) {
spAccount.setTag(pos);
spAccount.setSelection(pos);
// OAuth token could be updated
if (pos > 0 && accounts.get(pos).auth_type != ConnectionHelper.AUTH_TYPE_PASSWORD)
tilPassword.getEditText().setText(accounts.get(pos).password);
break;
}
}

View File

@ -3513,8 +3513,7 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences.
} else {
// Decode message
Properties props = MessageHelper.getSessionProperties(
ConnectionHelper.AUTH_TYPE_PASSWORD, null, false);
Properties props = MessageHelper.getSessionProperties(null, false);
Session isession = Session.getInstance(props, null);
ByteArrayInputStream is = new ByteArrayInputStream(decrypted.toByteArray());
MimeMessage imessage = new MimeMessage(isession, is);

View File

@ -19,24 +19,11 @@ package eu.faircode.email;
Copyright 2018-2019 by Marcel Bokhorst (M66B)
*/
import android.Manifest;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AccountManagerCallback;
import android.accounts.AccountManagerFuture;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.method.LinkMovementMethod;
import android.util.Patterns;
import android.view.KeyEvent;
@ -54,14 +41,12 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.Group;
import androidx.lifecycle.Lifecycle;
import com.google.android.material.snackbar.Snackbar;
import com.google.android.material.textfield.TextInputLayout;
import com.sun.mail.imap.IMAPFolder;
import com.sun.mail.imap.IMAPStore;
import java.io.IOException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Date;
@ -72,17 +57,11 @@ import javax.mail.Folder;
import javax.mail.Session;
import javax.mail.Transport;
import static android.accounts.AccountManager.newChooseAccountIntent;
import static android.app.Activity.RESULT_OK;
import static com.google.android.material.textfield.TextInputLayout.END_ICON_NONE;
import static com.google.android.material.textfield.TextInputLayout.END_ICON_PASSWORD_TOGGLE;
public class FragmentQuickSetup extends FragmentBase {
private ViewGroup view;
private EditText etName;
private EditText etEmail;
private Button btnAuthorize;
private TextInputLayout tilPassword;
private Button btnCheck;
@ -94,8 +73,6 @@ public class FragmentQuickSetup extends FragmentBase {
private Button btnSave;
private Group grpSetup;
private int auth_type = ConnectionHelper.AUTH_TYPE_PASSWORD;
@Override
@Nullable
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
@ -106,7 +83,6 @@ public class FragmentQuickSetup extends FragmentBase {
// Get controls
etName = view.findViewById(R.id.etName);
btnAuthorize = view.findViewById(R.id.btnAuthorize);
etEmail = view.findViewById(R.id.etEmail);
tilPassword = view.findViewById(R.id.tilPassword);
btnCheck = view.findViewById(R.id.btnCheck);
@ -121,54 +97,6 @@ public class FragmentQuickSetup extends FragmentBase {
// Wire controls
btnAuthorize.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!Helper.hasValidFingerprint(getContext())) {
Snackbar snackbar = Snackbar.make(view, R.string.title_no_xoauth2, Snackbar.LENGTH_LONG);
final Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(Helper.FAQ_URI + "#user-content-faq109"));
if (intent.resolveActivity(getContext().getPackageManager()) != null)
snackbar.setAction(R.string.title_info, new View.OnClickListener() {
@Override
public void onClick(View view) {
startActivity(intent);
}
});
snackbar.show();
return;
}
String permission = Manifest.permission.GET_ACCOUNTS;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O &&
!Helper.hasPermission(getContext(), permission)) {
Log.i("Requesting " + permission);
requestPermissions(new String[]{permission}, ActivitySetup.REQUEST_CHOOSE_ACCOUNT);
} else
selectAccount();
}
});
etEmail.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (auth_type != ConnectionHelper.AUTH_TYPE_PASSWORD) {
auth_type = ConnectionHelper.AUTH_TYPE_PASSWORD;
tilPassword.getEditText().setText(null);
tilPassword.setEnabled(true);
tilPassword.setEndIconMode(END_ICON_PASSWORD_TOGGLE);
}
}
@Override
public void afterTextChanged(Editable s) {
}
});
tilPassword.setHintEnabled(false);
tilPassword.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
@ -237,7 +165,6 @@ public class FragmentQuickSetup extends FragmentBase {
args.putString("name", etName.getText().toString());
args.putString("email", etEmail.getText().toString().trim());
args.putString("password", tilPassword.getEditText().getText().toString());
args.putInt("auth_type", auth_type);
args.putBoolean("check", check);
new SimpleTask<EmailProvider>() {
@ -261,7 +188,6 @@ public class FragmentQuickSetup extends FragmentBase {
String name = args.getString("name");
String email = args.getString("email");
String password = args.getString("password");
int auth_type = args.getInt("auth_type");
boolean check = args.getBoolean("check");
if (TextUtils.isEmpty(name))
@ -283,7 +209,7 @@ public class FragmentQuickSetup extends FragmentBase {
long now = new Date().getTime();
{
Properties props = MessageHelper.getSessionProperties(auth_type, null, false);
Properties props = MessageHelper.getSessionProperties(null, false);
Session isession = Session.getInstance(props, null);
isession.setDebug(true);
try (IMAPStore istore = (IMAPStore) isession.getStore(provider.imap_starttls ? "imap" : "imaps")) {
@ -326,7 +252,7 @@ public class FragmentQuickSetup extends FragmentBase {
}
{
Properties props = MessageHelper.getSessionProperties(auth_type, null, false);
Properties props = MessageHelper.getSessionProperties(null, false);
Session isession = Session.getInstance(props, null);
isession.setDebug(true);
try (Transport itransport = isession.getTransport(provider.smtp_starttls ? "smtp" : "smtps")) {
@ -345,7 +271,7 @@ public class FragmentQuickSetup extends FragmentBase {
// Create account
EntityAccount account = new EntityAccount();
account.auth_type = auth_type;
account.auth_type = ConnectionHelper.AUTH_TYPE_PASSWORD;
account.host = provider.imap_host;
account.starttls = provider.imap_starttls;
account.insecure = false;
@ -398,7 +324,7 @@ public class FragmentQuickSetup extends FragmentBase {
identity.color = null;
identity.signature = null;
identity.auth_type = auth_type;
identity.auth_type = ConnectionHelper.AUTH_TYPE_PASSWORD;
identity.host = provider.smtp_host;
identity.starttls = provider.smtp_starttls;
identity.insecure = false;
@ -466,96 +392,4 @@ public class FragmentQuickSetup extends FragmentBase {
}
}.execute(FragmentQuickSetup.this, args, "setup:quick");
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == ActivitySetup.REQUEST_CHOOSE_ACCOUNT)
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
selectAccount();
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == ActivitySetup.REQUEST_CHOOSE_ACCOUNT)
if (resultCode == RESULT_OK && data != null)
accountSelected(data);
}
private void selectAccount() {
Log.i("Select account");
startActivityForResult(
Helper.getChooser(getContext(), newChooseAccountIntent(
null,
null,
new String[]{"com.google"},
false,
null,
null,
null,
null)),
ActivitySetup.REQUEST_CHOOSE_ACCOUNT);
}
private void accountSelected(Intent data) {
Log.i("Selected account");
String name = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
String type = data.getStringExtra(AccountManager.KEY_ACCOUNT_TYPE);
AccountManager am = AccountManager.get(getContext());
Account[] accounts = am.getAccountsByType(type);
Log.i("Accounts=" + accounts.length);
for (final Account account : accounts)
if (name.equals(account.name)) {
etEmail.setEnabled(false);
tilPassword.setEnabled(false);
btnAuthorize.setEnabled(false);
btnCheck.setEnabled(false);
final Snackbar snackbar = Snackbar.make(view, R.string.title_authorizing, Snackbar.LENGTH_SHORT);
snackbar.show();
am.getAuthToken(
account,
ConnectionHelper.getAuthTokenType(type),
new Bundle(),
getActivity(),
new AccountManagerCallback<Bundle>() {
@Override
public void run(AccountManagerFuture<Bundle> future) {
try {
Bundle bundle = future.getResult();
String token = bundle.getString(AccountManager.KEY_AUTHTOKEN);
Log.i("Got token");
etEmail.setText(account.name);
tilPassword.getEditText().setText(token);
auth_type = ConnectionHelper.AUTH_TYPE_GMAIL;
} catch (Throwable ex) {
Log.e(ex);
if (ex instanceof OperationCanceledException ||
ex instanceof AuthenticatorException ||
ex instanceof IOException) {
if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED))
Snackbar.make(view, Helper.formatThrowable(ex), Snackbar.LENGTH_LONG).show();
} else
Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex);
} finally {
etEmail.setEnabled(true);
tilPassword.setEnabled(auth_type == ConnectionHelper.AUTH_TYPE_PASSWORD);
tilPassword.setEndIconMode(auth_type == ConnectionHelper.AUTH_TYPE_PASSWORD
? END_ICON_PASSWORD_TOGGLE : END_ICON_NONE);
btnAuthorize.setEnabled(true);
btnCheck.setEnabled(true);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
snackbar.dismiss();
}
}, 1000);
}
}
},
null);
break;
}
}
}

View File

@ -106,7 +106,7 @@ public class MessageHelper {
System.setProperty("mail.mime.multipart.ignoreexistingboundaryparameter", "true");
}
static Properties getSessionProperties(int auth_type, String realm, boolean insecure) {
static Properties getSessionProperties(String realm, boolean insecure) {
Properties props = new Properties();
props.put("mail.event.scope", "folder");
@ -204,15 +204,6 @@ public class MessageHelper {
System.setProperty("java.net.preferIPv4Stack", "true");
}
// https://javaee.github.io/javamail/OAuth2
Log.i("Auth type=" + auth_type);
if (auth_type == ConnectionHelper.AUTH_TYPE_GMAIL) {
props.put("mail.imaps.auth.mechanisms", "XOAUTH2");
props.put("mail.imap.auth.mechanisms", "XOAUTH2");
props.put("mail.smtps.auth.mechanisms", "XOAUTH2");
props.put("mail.smtp.auth.mechanisms", "XOAUTH2");
}
return props;
}

View File

@ -19,8 +19,6 @@ package eu.faircode.email;
Copyright 2018-2019 by Marcel Bokhorst (M66B)
*/
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
@ -51,7 +49,6 @@ import java.util.List;
import java.util.Properties;
import javax.mail.Address;
import javax.mail.AuthenticationFailedException;
import javax.mail.Message;
import javax.mail.MessageRemovedException;
import javax.mail.MessagingException;
@ -253,7 +250,7 @@ public class ServiceSend extends LifecycleService {
}
};
private void send(EntityMessage message) throws MessagingException, IOException, AuthenticatorException, OperationCanceledException {
private void send(EntityMessage message) throws MessagingException, IOException {
DB db = DB.getInstance(this);
// Mark attempt
@ -269,7 +266,7 @@ public class ServiceSend extends LifecycleService {
String protocol = ident.getProtocol();
// Get properties
Properties props = MessageHelper.getSessionProperties(ident.auth_type, ident.realm, ident.insecure);
Properties props = MessageHelper.getSessionProperties(ident.realm, ident.insecure);
String haddr;
if (ident.use_ip) {
@ -323,18 +320,7 @@ public class ServiceSend extends LifecycleService {
try (Transport itransport = isession.getTransport(protocol)) {
// Connect transport
db.identity().setIdentityState(ident.id, "connecting");
try {
itransport.connect(ident.host, ident.port, ident.user, ident.password);
} catch (AuthenticationFailedException ex) {
if (ident.auth_type == ConnectionHelper.AUTH_TYPE_GMAIL) {
EntityAccount account = db.account().getAccount(ident.account);
ident.password = ConnectionHelper.refreshToken(this, "com.google", ident.user, account.password);
DB.getInstance(this).identity().setIdentityPassword(ident.id, ident.password);
itransport.connect(ident.host, ident.port, ident.user, ident.password);
} else
throw ex;
}
itransport.connect(ident.host, ident.port, ident.user, ident.password);
db.identity().setIdentityState(ident.id, "connected");
// Send message

View File

@ -569,7 +569,7 @@ public class ServiceSynchronize extends LifecycleService {
//System.setProperty("mail.socket.debug", Boolean.toString(debug));
// Get properties
Properties props = MessageHelper.getSessionProperties(account.auth_type, account.realm, account.insecure);
Properties props = MessageHelper.getSessionProperties(account.realm, account.insecure);
if (!account.partial_fetch) {
props.put("mail.imap.partialfetch", "false");
props.put("mail.imaps.partialfetch", "false");

View File

@ -184,31 +184,6 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvPort" />
<Button
android:id="@+id/btnAuthorize"
style="@style/buttonStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:minWidth="0dp"
android:minHeight="0dp"
android:tag="disable"
android:text="@string/title_authorize"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/etPort" />
<TextView
android:id="@+id/tvAuthorizeOptional"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:text="@string/title_recommended"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textStyle="italic"
app:layout_constraintBottom_toBottomOf="@id/btnAuthorize"
app:layout_constraintStart_toEndOf="@id/btnAuthorize"
app:layout_constraintTop_toTopOf="@id/btnAuthorize" />
<!-- user -->
<TextView
@ -220,7 +195,7 @@
android:text="@string/title_user"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btnAuthorize" />
app:layout_constraintTop_toBottomOf="@id/etPort" />
<EditText
android:id="@+id/etUser"
@ -726,6 +701,16 @@
app:layout_constraintStart_toStartOf="parent"
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.Small"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvError" />
<eu.faircode.email.ContentLoadingProgressBar
android:id="@+id/pbWait"
style="@style/Base.Widget.AppCompat.ProgressBar"

View File

@ -30,31 +30,6 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btnAuthorize"
style="@style/buttonStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:minWidth="0dp"
android:minHeight="0dp"
android:tag="disable"
android:text="@string/title_authorize"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/etName" />
<TextView
android:id="@+id/tvAuthorizeOptional"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:text="@string/title_optional"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textStyle="italic"
app:layout_constraintBottom_toBottomOf="@id/btnAuthorize"
app:layout_constraintStart_toEndOf="@id/btnAuthorize"
app:layout_constraintTop_toTopOf="@id/btnAuthorize" />
<EditText
android:id="@+id/etEmail"
android:layout_width="match_parent"
@ -65,7 +40,7 @@
android:inputType="textEmailAddress"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btnAuthorize" />
app:layout_constraintTop_toBottomOf="@id/etName" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/tilPassword"

View File

@ -130,6 +130,20 @@
app:layout_constraintStart_toEndOf="@+id/ivState"
app:layout_constraintTop_toBottomOf="@id/tvHost" />
<TextView
android:id="@+id/tvAuthorize"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:layout_marginEnd="6dp"
android:text="@string/title_authorization_required"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textColor="?attr/colorWarning"
android:textIsSelectable="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/vwColor"
app:layout_constraintTop_toBottomOf="@id/tvLast" />
<TextView
android:id="@+id/tvIdentity"
android:layout_width="0dp"
@ -142,7 +156,7 @@
android:textIsSelectable="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/vwColor"
app:layout_constraintTop_toBottomOf="@id/tvLast" />
app:layout_constraintTop_toBottomOf="@id/tvAuthorize" />
<TextView
android:id="@+id/tvDrafts"

View File

@ -132,6 +132,20 @@
app:layout_constraintStart_toEndOf="@+id/ivState"
app:layout_constraintTop_toBottomOf="@id/tvHost" />
<TextView
android:id="@+id/tvAuthorize"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:layout_marginEnd="6dp"
android:text="@string/title_authorization_required"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textColor="?attr/colorWarning"
android:textIsSelectable="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/vwColor"
app:layout_constraintTop_toBottomOf="@id/tvLast" />
<TextView
android:id="@+id/tvError"
android:layout_width="0dp"
@ -144,7 +158,7 @@
android:textIsSelectable="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/vwColor"
app:layout_constraintTop_toBottomOf="@id/tvLast" />
app:layout_constraintTop_toBottomOf="@id/tvAuthorize" />
<View
android:id="@+id/marginBottom"

View File

@ -107,8 +107,7 @@
<string name="title_setup_quick_imap">IMAP server to receive messages</string>
<string name="title_setup_quick_smtp">SMTP server to send messages</string>
<string name="title_setup_go">Go</string>
<string name="title_setup_setting_gmail">Enable access for "less secure" apps</string>
<string name="title_setup_setting_yahoo">Enable access for "less secure" apps</string>
<string name="title_setup_instructions">Setup instructions</string>
<string name="title_setup_no_settings">No settings found for domain \'%1$s\'</string>
<string name="title_setup_quick_success">An account and an identity have successfully been added</string>
<string name="title_setup_quick_failed">You can try to configure an account and an identity below too</string>
@ -308,8 +307,6 @@
<string name="title_password">Password</string>
<string name="title_realm">Realm</string>
<string name="title_use_ip">Use local IP address instead of host name</string>
<string name="title_authorize">Select account</string>
<string name="title_authorizing">Authorizing account &#8230;</string>
<string name="title_synchronize_account">Synchronize (receive messages)</string>
<string name="title_synchronize_identity">Synchronize (send messages)</string>
<string name="title_primary_account">Primary (default account)</string>
@ -317,7 +314,6 @@
<string name="title_keep_alive_interval">Keep-alive/poll interval (minutes)</string>
<string name="title_partial_fetch" translatable="false">Partial fetch</string>
<string name="title_check">Check</string>
<string name="title_no_xoauth2">This feature is available in original versions only</string>
<string name="title_no_name">Name missing</string>
<string name="title_no_email">Email address missing</string>
<string name="title_email_invalid">Email address invalid</string>
@ -332,6 +328,7 @@
<string name="title_no_idle">This provider does not support push messages. This will delay reception of new messages and increase battery usage.</string>
<string name="title_no_utf8">This provider does not support UTF-8</string>
<string name="title_no_sync">Synchronization errors since %1$s</string>
<string name="title_authorization_required">Authorization required</string>
<string name="title_identity_required">An identity is required to send messages</string>
<string name="title_drafts_required">A drafts folder is required to send messages</string>
<string name="title_account_delete">Delete this account permanently?</string>