Added certificate selection

pull/174/head
M66B 4 years ago
parent 8d3ec9aa43
commit 94a9e05da5

File diff suppressed because it is too large Load Diff

@ -60,7 +60,7 @@ import io.requery.android.database.sqlite.SQLiteDatabase;
// https://developer.android.com/topic/libraries/architecture/room.html
@Database(
version = 141,
version = 142,
entities = {
EntityIdentity.class,
EntityAccount.class,
@ -1364,6 +1364,14 @@ public abstract class DB extends RoomDatabase {
db.execSQL("ALTER TABLE `account` ADD COLUMN `certificate` INTEGER NOT NULL DEFAULT 0");
db.execSQL("ALTER TABLE `identity` ADD COLUMN `certificate` INTEGER NOT NULL DEFAULT 0");
}
})
.addMigrations(new Migration(141, 142) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase db) {
Log.i("DB migration from version " + startVersion + " to " + endVersion);
db.execSQL("ALTER TABLE `account` ADD COLUMN `certificate_alias` TEXT");
db.execSQL("ALTER TABLE `identity` ADD COLUMN `certificate_alias` TEXT");
}
});
}

@ -5,6 +5,7 @@ import android.accounts.AccountManager;
import android.accounts.AuthenticatorException;
import android.content.Context;
import android.content.SharedPreferences;
import android.security.KeyChain;
import android.text.TextUtils;
import androidx.annotation.NonNull;
@ -35,10 +36,8 @@ import java.net.Socket;
import java.net.UnknownHostException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateParsingException;
@ -240,7 +239,7 @@ public class EmailService implements AutoCloseable {
account.host, account.port,
account.auth_type, account.provider,
account.user, account.password,
account.certificate, account.fingerprint);
account.certificate_alias, account.fingerprint);
if (password != null) {
DB db = DB.getInstance(context);
int count = db.account().setAccountPassword(account.id, account.password);
@ -253,7 +252,7 @@ public class EmailService implements AutoCloseable {
identity.host, identity.port,
identity.auth_type, identity.provider,
identity.user, identity.password,
identity.certificate, identity.fingerprint);
identity.certificate_alias, identity.fingerprint);
if (password != null) {
DB db = DB.getInstance(context);
int count = db.identity().setIdentityPassword(identity.id, identity.password);
@ -264,10 +263,20 @@ public class EmailService implements AutoCloseable {
public String connect(
String host, int port,
int auth, String provider, String user, String password,
boolean certificate, String fingerprint) throws MessagingException {
String certificate, String fingerprint) throws MessagingException {
SSLSocketFactoryService factory = null;
try {
factory = new SSLSocketFactoryService(host, insecure, harden, certificate, fingerprint);
X509Certificate[] certs = null;
if (certificate != null) {
Log.i("Get client certificate alias=" + certificate);
try {
certs = KeyChain.getCertificateChain(context, certificate);
} catch (Throwable ex) {
Log.w(ex);
}
}
factory = new SSLSocketFactoryService(host, insecure, harden, certs, fingerprint);
properties.put("mail." + protocol + ".ssl.socketFactory", factory);
properties.put("mail." + protocol + ".socketFactory.fallback", "false");
properties.put("mail." + protocol + ".ssl.checkserveridentity", "false");
@ -576,7 +585,7 @@ public class EmailService implements AutoCloseable {
private SSLSocketFactory factory;
private X509Certificate certificate;
SSLSocketFactoryService(String host, boolean insecure, boolean harden, boolean use_certificate, String fingerprint) throws GeneralSecurityException {
SSLSocketFactoryService(String host, boolean insecure, boolean harden, X509Certificate[] certs, String fingerprint) throws GeneralSecurityException {
this.server = host;
this.secure = !insecure;
this.harden = harden;
@ -646,19 +655,15 @@ public class EmailService implements AutoCloseable {
};
KeyManager[] km = null;
if (use_certificate)
if (certs != null)
try {
Log.i("Client certificate init");
KeyStore ca = KeyStore.getInstance("AndroidCAStore");
ca.load(null, null);
Certificate cert = ca.getCertificate(server);
if (cert == null)
throw new KeyStoreException("Certificate not found host=" + server);
Log.i("Client certificate init certs=" + certs.length);
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null, null);
ks.setCertificateEntry(server, cert);
for (int i = 0; i < certs.length; i++)
ks.setCertificateEntry(server + ":" + i, certs[i]);
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, null);

@ -78,7 +78,8 @@ public class EntityAccount extends EntityOrder implements Serializable {
@NonNull
public String password;
@NonNull
public Boolean certificate;
public Boolean certificate; // obsolete
public String certificate_alias;
public String realm;
public String fingerprint;
@ -197,7 +198,7 @@ public class EntityAccount extends EntityOrder implements Serializable {
json.put("provider", provider);
json.put("user", user);
json.put("password", password);
json.put("certificate", certificate);
json.put("certificate_alias", certificate_alias);
json.put("realm", realm);
json.put("fingerprint", fingerprint);
@ -256,7 +257,7 @@ public class EntityAccount extends EntityOrder implements Serializable {
account.provider = json.getString("provider");
account.user = json.getString("user");
account.password = json.getString("password");
account.certificate = json.optBoolean("certificate");
account.certificate_alias = json.getString("certificate_alias");
if (json.has("realm"))
account.realm = json.getString("realm");
if (json.has("fingerprint"))

@ -78,7 +78,8 @@ public class EntityIdentity {
@NonNull
public String password;
@NonNull
public boolean certificate;
public boolean certificate; // obsolete
public String certificate_alias;
public String realm;
public String fingerprint;
@NonNull
@ -175,7 +176,7 @@ public class EntityIdentity {
json.put("provider", provider);
json.put("user", user);
json.put("password", password);
json.put("certificate", certificate);
json.put("certificate_alias", certificate_alias);
json.put("realm", realm);
json.put("fingerprint", fingerprint);
json.put("use_ip", use_ip);
@ -221,7 +222,7 @@ public class EntityIdentity {
identity.provider = json.getString("provider");
identity.user = json.getString("user");
identity.password = json.getString("password");
identity.certificate = json.optBoolean("certificate");
identity.certificate_alias = json.getString("certificate_alias");
if (json.has("realm") && !json.isNull("realm"))
identity.realm = json.getString("realm");
if (json.has("fingerprint"))

@ -91,7 +91,8 @@ public class FragmentAccount extends FragmentBase {
private EditText etUser;
private TextInputLayout tilPassword;
private TextView tvCharacters;
private CheckBox cbCertificate;
private Button btnCertificate;
private TextView tvCertificate;
private Button btnOAuth;
private TextView tvOAuthSupport;
private EditText etRealm;
@ -152,6 +153,7 @@ public class FragmentAccount extends FragmentBase {
private long copy = -1;
private int auth = EmailService.AUTH_TYPE_PASSWORD;
private String provider = null;
private String certificate = null;
private boolean saving = false;
private static final int REQUEST_COLOR = 1;
@ -200,7 +202,8 @@ public class FragmentAccount extends FragmentBase {
etUser = view.findViewById(R.id.etUser);
tilPassword = view.findViewById(R.id.tilPassword);
tvCharacters = view.findViewById(R.id.tvCharacters);
cbCertificate = view.findViewById(R.id.cbCertificate);
btnCertificate = view.findViewById(R.id.btnCertificate);
tvCertificate = view.findViewById(R.id.tvCertificate);
btnOAuth = view.findViewById(R.id.btnOAuth);
tvOAuthSupport = view.findViewById(R.id.tvOAuthSupport);
etRealm = view.findViewById(R.id.etRealm);
@ -284,7 +287,8 @@ public class FragmentAccount extends FragmentBase {
etUser.setTag(null);
etUser.setText(null);
tilPassword.getEditText().setText(null);
cbCertificate.setChecked(false);
certificate = null;
tvCertificate.setText(R.string.title_optional);
btnOAuth.setEnabled(false);
etRealm.setText(null);
cbTrust.setChecked(false);
@ -339,6 +343,25 @@ public class FragmentAccount extends FragmentBase {
}
});
btnCertificate.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Helper.selectKeyAlias(getActivity(), getViewLifecycleOwner(), null, new Helper.IKeyAlias() {
@Override
public void onSelected(String alias) {
certificate = alias;
tvCertificate.setText(alias);
}
@Override
public void onNothingSelected() {
certificate = null;
tvCertificate.setText(getString(R.string.title_optional));
}
});
}
});
btnOAuth.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@ -552,7 +575,7 @@ public class FragmentAccount extends FragmentBase {
args.putString("provider", provider);
args.putString("user", etUser.getText().toString().trim());
args.putString("password", tilPassword.getEditText().getText().toString());
args.putBoolean("certificate", cbCertificate.isChecked());
args.putString("certificate", certificate);
args.putString("realm", etRealm.getText().toString());
args.putString("fingerprint", cbTrust.isChecked() ? (String) cbTrust.getTag() : null);
@ -591,7 +614,7 @@ public class FragmentAccount extends FragmentBase {
String provider = args.getString("provider");
String user = args.getString("user");
String password = args.getString("password");
boolean certificate = args.getBoolean("certificate'");
String certificate = args.getString("certificate");
String realm = args.getString("realm");
String fingerprint = args.getString("fingerprint");
@ -606,7 +629,7 @@ public class FragmentAccount extends FragmentBase {
port = (starttls ? "143" : "993");
if (TextUtils.isEmpty(user))
throw new IllegalArgumentException(context.getString(R.string.title_no_user));
if (TextUtils.isEmpty(password) && !insecure && !certificate)
if (TextUtils.isEmpty(password) && !insecure && certificate == null)
throw new IllegalArgumentException(context.getString(R.string.title_no_password));
if (TextUtils.isEmpty(realm))
@ -769,7 +792,7 @@ public class FragmentAccount extends FragmentBase {
args.putString("provider", provider);
args.putString("user", etUser.getText().toString().trim());
args.putString("password", tilPassword.getEditText().getText().toString());
args.putBoolean("certificate", cbCertificate.isChecked());
args.putString("certificate", certificate);
args.putString("realm", etRealm.getText().toString());
args.putString("fingerprint", cbTrust.isChecked() ? (String) cbTrust.getTag() : null);
@ -831,7 +854,7 @@ public class FragmentAccount extends FragmentBase {
String provider = args.getString("provider");
String user = args.getString("user").trim();
String password = args.getString("password");
boolean certificate = args.getBoolean("certificate");
String certificate = args.getString("certificate");
String realm = args.getString("realm");
String fingerprint = args.getString("fingerprint");
@ -872,7 +895,7 @@ public class FragmentAccount extends FragmentBase {
port = (starttls ? "143" : "993");
if (TextUtils.isEmpty(user) && !should)
throw new IllegalArgumentException(context.getString(R.string.title_no_user));
if (synchronize && TextUtils.isEmpty(password) && !insecure && !certificate && !should)
if (synchronize && TextUtils.isEmpty(password) && !insecure && certificate == null && !should)
throw new IllegalArgumentException(context.getString(R.string.title_no_password));
if (TextUtils.isEmpty(interval))
interval = Integer.toString(EntityAccount.DEFAULT_KEEP_ALIVE_INTERVAL);
@ -909,7 +932,7 @@ public class FragmentAccount extends FragmentBase {
return true;
if (!Objects.equals(account.password, password))
return true;
if (!Objects.equals(account.certificate, certificate))
if (!Objects.equals(account.certificate_alias, certificate))
return true;
if (!Objects.equals(account.realm, realm))
return true;
@ -982,7 +1005,7 @@ public class FragmentAccount extends FragmentBase {
!account.port.equals(Integer.parseInt(port)) ||
!account.user.equals(user) ||
!account.password.equals(password) ||
!account.certificate.equals(certificate) ||
!Objects.equals(account.certificate_alias, certificate) ||
!Objects.equals(realm, accountRealm) ||
!Objects.equals(account.fingerprint, fingerprint)));
Log.i("Account check=" + check);
@ -1045,7 +1068,7 @@ public class FragmentAccount extends FragmentBase {
account.auth_type = auth;
account.user = user;
account.password = password;
account.certificate = certificate;
account.certificate_alias = certificate;
account.provider = provider;
account.realm = realm;
account.fingerprint = fingerprint;
@ -1382,7 +1405,8 @@ public class FragmentAccount extends FragmentBase {
etUser.setText(account == null ? null : account.user);
tilPassword.getEditText().setText(account == null ? null : account.password);
cbCertificate.setChecked(account == null ? false : account.certificate);
certificate = (account == null ? null : account.certificate_alias);
tvCertificate.setText(certificate == null ? getString(R.string.title_optional) : certificate);
etRealm.setText(account == null ? null : account.realm);
if (account == null || account.fingerprint == null) {
@ -1447,7 +1471,7 @@ public class FragmentAccount extends FragmentBase {
if (auth != EmailService.AUTH_TYPE_PASSWORD) {
etUser.setEnabled(false);
tilPassword.setEnabled(false);
cbCertificate.setEnabled(false);
btnCertificate.setEnabled(false);
}
if (account == null || account.auth_type != EmailService.AUTH_TYPE_GMAIL)

@ -316,7 +316,7 @@ public class FragmentGmail extends FragmentBase {
provider.imap.host, provider.imap.port,
EmailService.AUTH_TYPE_GMAIL, null,
user, password,
false, null);
null, null);
folders = iservice.getFolders();
@ -331,7 +331,7 @@ public class FragmentGmail extends FragmentBase {
provider.smtp.host, provider.smtp.port,
EmailService.AUTH_TYPE_GMAIL, null,
user, password,
false, null);
null, null);
}
DB db = DB.getInstance(context);

@ -100,7 +100,8 @@ public class FragmentIdentity extends FragmentBase {
private EditText etUser;
private TextInputLayout tilPassword;
private TextView tvCharacters;
private CheckBox cbCertificate;
private Button btnCertificate;
private TextView tvCertificate;
private Button btnOAuth;
private EditText etRealm;
private CheckBox cbUseIp;
@ -132,6 +133,7 @@ public class FragmentIdentity extends FragmentBase {
private long account = -1;
private int auth = EmailService.AUTH_TYPE_PASSWORD;
private String provider = null;
private String certificate = null;
private boolean saving = false;
private static final int REQUEST_COLOR = 1;
@ -186,7 +188,8 @@ public class FragmentIdentity extends FragmentBase {
etUser = view.findViewById(R.id.etUser);
tilPassword = view.findViewById(R.id.tilPassword);
tvCharacters = view.findViewById(R.id.tvCharacters);
cbCertificate = view.findViewById(R.id.cbCertificate);
btnCertificate = view.findViewById(R.id.btnCertificate);
tvCertificate = view.findViewById(R.id.tvCertificate);
btnOAuth = view.findViewById(R.id.btnOAuth);
etRealm = view.findViewById(R.id.etRealm);
cbUseIp = view.findViewById(R.id.cbUseIp);
@ -268,12 +271,13 @@ public class FragmentIdentity extends FragmentBase {
etEmail.setText(account.user);
etUser.setText(account.user);
tilPassword.getEditText().setText(account.password);
cbCertificate.setChecked(account.certificate);
certificate = account.certificate_alias;
tvCertificate.setText(certificate == null ? getString(R.string.title_optional) : certificate);
etRealm.setText(account.realm);
etUser.setEnabled(auth == EmailService.AUTH_TYPE_PASSWORD);
tilPassword.setEnabled(auth == EmailService.AUTH_TYPE_PASSWORD);
cbCertificate.setEnabled(auth == EmailService.AUTH_TYPE_PASSWORD);
btnCertificate.setEnabled(auth == EmailService.AUTH_TYPE_PASSWORD);
cbTrust.setChecked(false);
}
@ -406,7 +410,7 @@ public class FragmentIdentity extends FragmentBase {
etUser.setEnabled(auth == EmailService.AUTH_TYPE_PASSWORD);
tilPassword.setEnabled(auth == EmailService.AUTH_TYPE_PASSWORD);
cbCertificate.setEnabled(auth == EmailService.AUTH_TYPE_PASSWORD);
btnCertificate.setEnabled(auth == EmailService.AUTH_TYPE_PASSWORD);
}
@Override
@ -428,6 +432,25 @@ public class FragmentIdentity extends FragmentBase {
}
});
btnCertificate.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Helper.selectKeyAlias(getActivity(), getViewLifecycleOwner(), null, new Helper.IKeyAlias() {
@Override
public void onSelected(String alias) {
certificate = alias;
tvCertificate.setText(alias);
}
@Override
public void onNothingSelected() {
certificate = null;
tvCertificate.setText(R.string.title_optional);
}
});
}
});
btnOAuth.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@ -586,7 +609,7 @@ public class FragmentIdentity extends FragmentBase {
args.putString("provider", provider);
args.putString("user", etUser.getText().toString().trim());
args.putString("password", tilPassword.getEditText().getText().toString());
args.putBoolean("certificate", cbCertificate.isChecked());
args.putString("certificate", certificate);
args.putString("realm", etRealm.getText().toString());
args.putString("fingerprint", cbTrust.isChecked() ? (String) cbTrust.getTag() : null);
args.putBoolean("use_ip", cbUseIp.isChecked());
@ -636,7 +659,7 @@ public class FragmentIdentity extends FragmentBase {
String provider = args.getString("provider");
String user = args.getString("user").trim();
String password = args.getString("password");
boolean certificate = args.getBoolean("certificate");
String certificate = args.getString("certificate");
String realm = args.getString("realm");
String fingerprint = args.getString("fingerprint");
boolean use_ip = args.getBoolean("use_ip");
@ -667,7 +690,7 @@ public class FragmentIdentity extends FragmentBase {
port = (starttls ? "587" : "465");
if (TextUtils.isEmpty(user) && !should)
throw new IllegalArgumentException(context.getString(R.string.title_no_user));
if (synchronize && TextUtils.isEmpty(password) && !insecure && !certificate && !should)
if (synchronize && TextUtils.isEmpty(password) && !insecure && certificate == null && !should)
throw new IllegalArgumentException(context.getString(R.string.title_no_password));
if (!TextUtils.isEmpty(replyto) && !should) {
@ -739,7 +762,7 @@ public class FragmentIdentity extends FragmentBase {
return true;
if (!Objects.equals(identity.password, password))
return true;
if (!Objects.equals(identity.certificate, certificate))
if (!Objects.equals(identity.certificate_alias, certificate))
return true;
if (!Objects.equals(identity.realm, realm))
return true;
@ -770,7 +793,7 @@ public class FragmentIdentity extends FragmentBase {
!identity.insecure.equals(insecure) ||
!host.equals(identity.host) || Integer.parseInt(port) != identity.port ||
!user.equals(identity.user) || !password.equals(identity.password) ||
!Objects.equals(identity.certificate, certificate) ||
!Objects.equals(identity.certificate_alias, certificate) ||
!Objects.equals(realm, identityRealm) ||
!Objects.equals(identity.fingerprint, fingerprint) ||
use_ip != identity.use_ip));
@ -823,7 +846,7 @@ public class FragmentIdentity extends FragmentBase {
identity.auth_type = auth;
identity.user = user;
identity.password = password;
identity.certificate = certificate;
identity.certificate_alias = certificate;
identity.provider = provider;
identity.realm = realm;
identity.fingerprint = fingerprint;
@ -1019,7 +1042,8 @@ public class FragmentIdentity extends FragmentBase {
etPort.setText(identity == null ? null : Long.toString(identity.port));
etUser.setText(identity == null ? null : identity.user);
tilPassword.getEditText().setText(identity == null ? null : identity.password);
cbCertificate.setChecked(identity != null && identity.certificate);
certificate = (identity == null ? null : identity.certificate_alias);
tvCertificate.setText(certificate == null ? getString(R.string.title_optional) : certificate);
etRealm.setText(identity == null ? null : identity.realm);
if (identity == null || identity.fingerprint == null) {
@ -1075,7 +1099,7 @@ public class FragmentIdentity extends FragmentBase {
if (auth != EmailService.AUTH_TYPE_PASSWORD) {
etUser.setEnabled(false);
tilPassword.setEnabled(false);
cbCertificate.setEnabled(false);
btnCertificate.setEnabled(false);
}
if (identity == null || identity.auth_type != EmailService.AUTH_TYPE_GMAIL)

@ -475,7 +475,7 @@ public class FragmentOAuth extends FragmentBase {
provider.imap.host, provider.imap.port,
EmailService.AUTH_TYPE_OAUTH, provider.id,
primaryEmail, state,
false, null);
null, null);
folders = iservice.getFolders();
@ -491,7 +491,7 @@ public class FragmentOAuth extends FragmentBase {
provider.smtp.host, provider.smtp.port,
EmailService.AUTH_TYPE_OAUTH, provider.id,
primaryEmail, state,
false, null);
null, null);
}
Log.i("OAuth passed provider=" + provider.id);

@ -329,7 +329,7 @@ public class FragmentPop extends FragmentBase {
host, Integer.parseInt(port),
EmailService.AUTH_TYPE_PASSWORD, null,
user, password,
false, null);
null, null);
}
}

@ -285,7 +285,7 @@ public class FragmentQuickSetup extends FragmentBase {
provider.imap.host, provider.imap.port,
EmailService.AUTH_TYPE_PASSWORD, null,
user, password,
false, null);
null, null);
} catch (AuthenticationFailedException ex) {
if (!user.equals(username)) {
Log.w(ex);
@ -295,7 +295,7 @@ public class FragmentQuickSetup extends FragmentBase {
provider.imap.host, provider.imap.port,
EmailService.AUTH_TYPE_PASSWORD, null,
user, password,
false, null);
null, null);
} else
throw ex;
}
@ -314,7 +314,7 @@ public class FragmentQuickSetup extends FragmentBase {
provider.smtp.host, provider.smtp.port,
EmailService.AUTH_TYPE_PASSWORD, null,
user, password,
false, null);
null, null);
}
if (check)

@ -36,6 +36,7 @@ public class TupleAccountState extends EntityAccount {
this.port.equals(other.port) &&
this.user.equals(other.user) &&
this.password.equals(other.password) &&
Objects.equals(this.certificate_alias, other.certificate_alias) &&
Objects.equals(this.realm, other.realm) &&
Objects.equals(this.fingerprint, other.fingerprint) &&
this.notify.equals(other.notify) &&

@ -255,15 +255,31 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tilPassword" />
<CheckBox
android:id="@+id/cbCertificate"
<Button
android:id="@+id/btnCertificate"
style="@style/buttonStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="@string/title_use_certificate"
android:minWidth="0dp"
android:minHeight="0dp"
android:tag="disable"
android:text="@string/title_client_certificate"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvCharacters" />
<TextView
android:id="@+id/tvCertificate"
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/btnCertificate"
app:layout_constraintStart_toEndOf="@id/btnCertificate"
app:layout_constraintTop_toTopOf="@id/btnCertificate" />
<Button
android:id="@+id/btnOAuth"
style="@style/buttonStyleSmall"
@ -275,7 +291,7 @@
android:tag="disable"
android:text="@string/title_setup_authentication"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/cbCertificate" />
app:layout_constraintTop_toBottomOf="@id/btnCertificate" />
<TextView
android:id="@+id/tvOAuthSupport"
@ -915,7 +931,7 @@
android:layout_width="0dp"
android:layout_height="0dp"
app:constraint_referenced_ids="
tvUser,etUser,tvPassword,tilPassword,cbCertificate,btnOAuth,tvOAuthSupport,tvRealm,etRealm,
tvUser,etUser,tvPassword,tilPassword,btnCertificate,tvCertificate,btnOAuth,tvOAuthSupport,tvRealm,etRealm,
tvName,tvNameRemark,etName,
tvColor,btnColor,tvColorHint,tvColorPro" />

@ -442,15 +442,31 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tilPassword" />
<CheckBox
android:id="@+id/cbCertificate"
<Button
android:id="@+id/btnCertificate"
style="@style/buttonStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="@string/title_use_certificate"
android:minWidth="0dp"
android:minHeight="0dp"
android:tag="disable"
android:text="@string/title_client_certificate"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvCharacters" />
<TextView
android:id="@+id/tvCertificate"
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/btnCertificate"
app:layout_constraintStart_toEndOf="@id/btnCertificate"
app:layout_constraintTop_toTopOf="@id/btnCertificate" />
<Button
android:id="@+id/btnOAuth"
style="@style/buttonStyleSmall"
@ -462,7 +478,7 @@
android:tag="disable"
android:text="@string/title_setup_authentication"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/cbCertificate" />
app:layout_constraintTop_toBottomOf="@id/btnCertificate" />
<TextView
android:id="@+id/tvRealm"
@ -723,7 +739,7 @@
tvProvider,spProvider,
tvDomain,tvDomainHint,etDomain,btnAutoConfig,
tvSmtp,tvHost,etHost,rgEncryption,cbInsecure,tvInsecureRemark,tvPort,etPort,
tvUser,etUser,tvPassword,tilPassword,cbCertificate,btnOAuth,
tvUser,etUser,tvPassword,tilPassword,btnCertificate,tvCertificate,btnOAuth,
tvRealm,etRealm,
cbUseIp,tvUseIpHint,
cbSynchronize,cbPrimary,

@ -517,7 +517,7 @@
<string name="title_port">Port number</string>
<string name="title_user">User name</string>
<string name="title_password">Password</string>
<string name="title_use_certificate">Use client certificate</string>
<string name="title_client_certificate">Client certificate</string>
<string name="title_realm">Realm</string>
<string name="title_use_ip">Use local IP address instead of host name</string>
<string name="title_primary_account">Primary (default account)</string>

Loading…
Cancel
Save