Merge "Introduce a cert selector in exchange settings UI."
This commit is contained in:
commit
b40bdb17df
|
@ -87,12 +87,6 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="1px"
|
android:layout_height="1px"
|
||||||
android:background="@color/account_setup_divider_color" />
|
android:background="@color/account_setup_divider_color" />
|
||||||
</LinearLayout>
|
|
||||||
<!-- Note, this row is not a TableRow, and it will span the entire table - no columns -->
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical" >
|
|
||||||
<CheckBox
|
<CheckBox
|
||||||
android:id="@+id/account_trust_certificates"
|
android:id="@+id/account_trust_certificates"
|
||||||
android:layout_marginTop="16dip"
|
android:layout_marginTop="16dip"
|
||||||
|
@ -108,6 +102,16 @@
|
||||||
android:layout_height="1px"
|
android:layout_height="1px"
|
||||||
android:background="@color/account_setup_divider_color"
|
android:background="@color/account_setup_divider_color"
|
||||||
android:visibility="gone" />
|
android:visibility="gone" />
|
||||||
|
<include
|
||||||
|
android:id="@+id/client_certificate_selector"
|
||||||
|
layout="@layout/client_certificate_selector"
|
||||||
|
android:visibility="gone" />
|
||||||
|
<View
|
||||||
|
android:id="@+id/client_certificate_divider"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1px"
|
||||||
|
android:background="@color/account_setup_divider_color"
|
||||||
|
android:visibility="gone" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
<TableRow
|
<TableRow
|
||||||
android:paddingTop="32dip" >
|
android:paddingTop="32dip" >
|
||||||
|
|
|
@ -69,6 +69,10 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:text="@string/account_setup_exchange_trust_certificates_label" />
|
android:text="@string/account_setup_exchange_trust_certificates_label" />
|
||||||
|
<include
|
||||||
|
android:id="@+id/client_certificate_selector"
|
||||||
|
layout="@layout/client_certificate_selector"
|
||||||
|
android:visibility="gone" />
|
||||||
<TextView
|
<TextView
|
||||||
android:text="@string/account_setup_exchange_device_id_label"
|
android:text="@string/account_setup_exchange_device_id_label"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Copyright (C) 2011 The Android Open Source Project
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- A layout to select a certificate, akin to a file selector on web pages. -->
|
||||||
|
<!-- Extends LinearLayout -->
|
||||||
|
<com.android.email.view.CertificateSelector
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dip"
|
||||||
|
android:layout_marginBottom="8dip"
|
||||||
|
android:layout_marginLeft="32dip"
|
||||||
|
android:layout_marginRight="32dip"
|
||||||
|
android:orientation="horizontal" >
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/certificate_alias"
|
||||||
|
android:layout_width="0dip"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone" />
|
||||||
|
<Button
|
||||||
|
android:id="@+id/select_button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/account_setup_exchange_use_certificate" />
|
||||||
|
|
||||||
|
</com.android.email.view.CertificateSelector>
|
|
@ -690,6 +690,10 @@ save attachment.</string>
|
||||||
<string name="account_setup_exchange_ssl_label">Use secure connection (SSL)</string>
|
<string name="account_setup_exchange_ssl_label">Use secure connection (SSL)</string>
|
||||||
<!-- On "Exchange" setup screen, the trust ssl certificates checkbox label -->
|
<!-- On "Exchange" setup screen, the trust ssl certificates checkbox label -->
|
||||||
<string name="account_setup_exchange_trust_certificates_label">Accept all SSL certificates</string>
|
<string name="account_setup_exchange_trust_certificates_label">Accept all SSL certificates</string>
|
||||||
|
<!-- On "Exchange" setup screen, a button label to include a client certificate [CHAR LIMIT=35] -->
|
||||||
|
<string name="account_setup_exchange_use_certificate">Use client certificate</string>
|
||||||
|
<!-- On "Exchange" setup screen, a button label to remove the currently used client certificate [CHAR LIMIT=35] -->
|
||||||
|
<string name="account_setup_exchange_remove_certificate">Remove</string>
|
||||||
<!-- On "Exchange" setup screen, the exchange device-id label [CHAR LIMIT=30] -->
|
<!-- On "Exchange" setup screen, the exchange device-id label [CHAR LIMIT=30] -->
|
||||||
<string name="account_setup_exchange_device_id_label">Mobile Device ID</string>
|
<string name="account_setup_exchange_device_id_label">Mobile Device ID</string>
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ import com.android.email.R;
|
||||||
import com.android.email.activity.UiUtilities;
|
import com.android.email.activity.UiUtilities;
|
||||||
import com.android.email.mail.Store;
|
import com.android.email.mail.Store;
|
||||||
import com.android.email.provider.AccountBackupRestore;
|
import com.android.email.provider.AccountBackupRestore;
|
||||||
|
import com.android.email.view.CertificateSelector;
|
||||||
import com.android.emailcommon.Device;
|
import com.android.emailcommon.Device;
|
||||||
import com.android.emailcommon.Logging;
|
import com.android.emailcommon.Logging;
|
||||||
import com.android.emailcommon.provider.Account;
|
import com.android.emailcommon.provider.Account;
|
||||||
|
@ -63,6 +64,7 @@ public class AccountSetupExchangeFragment extends AccountServerBaseFragment
|
||||||
private EditText mServerView;
|
private EditText mServerView;
|
||||||
private CheckBox mSslSecurityView;
|
private CheckBox mSslSecurityView;
|
||||||
private CheckBox mTrustCertificatesView;
|
private CheckBox mTrustCertificatesView;
|
||||||
|
private CertificateSelector mClientCertificateSelector;
|
||||||
|
|
||||||
// Support for lifecycle
|
// Support for lifecycle
|
||||||
private boolean mStarted;
|
private boolean mStarted;
|
||||||
|
@ -100,13 +102,13 @@ public class AccountSetupExchangeFragment extends AccountServerBaseFragment
|
||||||
View view = inflater.inflate(layoutId, container, false);
|
View view = inflater.inflate(layoutId, container, false);
|
||||||
Context context = getActivity();
|
Context context = getActivity();
|
||||||
|
|
||||||
mUsernameView = (EditText) UiUtilities.getView(view, R.id.account_username);
|
mUsernameView = UiUtilities.getView(view, R.id.account_username);
|
||||||
mPasswordView = (EditText) UiUtilities.getView(view, R.id.account_password);
|
mPasswordView = UiUtilities.getView(view, R.id.account_password);
|
||||||
mServerView = (EditText) UiUtilities.getView(view, R.id.account_server);
|
mServerView = UiUtilities.getView(view, R.id.account_server);
|
||||||
mSslSecurityView = (CheckBox) UiUtilities.getView(view, R.id.account_ssl);
|
mSslSecurityView = UiUtilities.getView(view, R.id.account_ssl);
|
||||||
mSslSecurityView.setOnCheckedChangeListener(this);
|
mSslSecurityView.setOnCheckedChangeListener(this);
|
||||||
mTrustCertificatesView = (CheckBox) UiUtilities.getView(view,
|
mTrustCertificatesView = UiUtilities.getView(view, R.id.account_trust_certificates);
|
||||||
R.id.account_trust_certificates);
|
mClientCertificateSelector = UiUtilities.getView(view, R.id.client_certificate_selector);
|
||||||
|
|
||||||
// Calls validateFields() which enables or disables the Next button
|
// Calls validateFields() which enables or disables the Next button
|
||||||
// based on the fields' validity.
|
// based on the fields' validity.
|
||||||
|
@ -146,6 +148,7 @@ public class AccountSetupExchangeFragment extends AccountServerBaseFragment
|
||||||
Log.d(Logging.LOG_TAG, "AccountSetupExchangeFragment onActivityCreated");
|
Log.d(Logging.LOG_TAG, "AccountSetupExchangeFragment onActivityCreated");
|
||||||
}
|
}
|
||||||
super.onActivityCreated(savedInstanceState);
|
super.onActivityCreated(savedInstanceState);
|
||||||
|
mClientCertificateSelector.setActivity(getActivity());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -275,7 +278,10 @@ public class AccountSetupExchangeFragment extends AccountServerBaseFragment
|
||||||
boolean trustCertificates = 0 != (hostAuth.mFlags & HostAuth.FLAG_TRUST_ALL);
|
boolean trustCertificates = 0 != (hostAuth.mFlags & HostAuth.FLAG_TRUST_ALL);
|
||||||
mSslSecurityView.setChecked(ssl);
|
mSslSecurityView.setChecked(ssl);
|
||||||
mTrustCertificatesView.setChecked(trustCertificates);
|
mTrustCertificatesView.setChecked(trustCertificates);
|
||||||
showTrustCertificates(ssl);
|
if (hostAuth.mClientCertAlias != null) {
|
||||||
|
mClientCertificateSelector.setCertificate(hostAuth.mClientCertAlias);
|
||||||
|
}
|
||||||
|
onUseSslChanged(ssl);
|
||||||
|
|
||||||
mLoadedRecvAuth = hostAuth;
|
mLoadedRecvAuth = hostAuth;
|
||||||
mLoaded = true;
|
mLoaded = true;
|
||||||
|
@ -304,16 +310,19 @@ public class AccountSetupExchangeFragment extends AccountServerBaseFragment
|
||||||
return enabled;
|
return enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||||
if (buttonView.getId() == R.id.account_ssl) {
|
if (buttonView.getId() == R.id.account_ssl) {
|
||||||
showTrustCertificates(isChecked);
|
onUseSslChanged(isChecked);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showTrustCertificates(boolean visible) {
|
public void onUseSslChanged(boolean useSsl) {
|
||||||
int mode = visible ? View.VISIBLE : View.GONE;
|
int mode = useSsl ? View.VISIBLE : View.GONE;
|
||||||
mTrustCertificatesView.setVisibility(mode);
|
mTrustCertificatesView.setVisibility(mode);
|
||||||
UiUtilities.setVisibilitySafe(getView(), R.id.account_trust_certificates_divider, mode);
|
UiUtilities.setVisibilitySafe(getView(), R.id.account_trust_certificates_divider, mode);
|
||||||
|
mClientCertificateSelector.setVisibility(mode);
|
||||||
|
UiUtilities.setVisibilitySafe(getView(), R.id.client_certificate_divider, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -383,17 +392,18 @@ public class AccountSetupExchangeFragment extends AccountServerBaseFragment
|
||||||
if (mTrustCertificatesView.isChecked()) {
|
if (mTrustCertificatesView.isChecked()) {
|
||||||
flags |= HostAuth.FLAG_TRUST_ALL;
|
flags |= HostAuth.FLAG_TRUST_ALL;
|
||||||
}
|
}
|
||||||
|
String certAlias = mClientCertificateSelector.getCertificate();
|
||||||
String serverAddress = mServerView.getText().toString().trim();
|
String serverAddress = mServerView.getText().toString().trim();
|
||||||
|
|
||||||
int port = mSslSecurityView.isChecked() ? 443 : 80;
|
int port = mSslSecurityView.isChecked() ? 443 : 80;
|
||||||
HostAuth sendAuth = account.getOrCreateHostAuthSend(mContext);
|
HostAuth sendAuth = account.getOrCreateHostAuthSend(mContext);
|
||||||
sendAuth.setLogin(userName, userPassword);
|
sendAuth.setLogin(userName, userPassword);
|
||||||
sendAuth.setConnection(mBaseScheme, serverAddress, port, flags);
|
sendAuth.setConnection(mBaseScheme, serverAddress, port, flags, certAlias);
|
||||||
sendAuth.mDomain = null;
|
sendAuth.mDomain = null;
|
||||||
|
|
||||||
HostAuth recvAuth = account.getOrCreateHostAuthRecv(mContext);
|
HostAuth recvAuth = account.getOrCreateHostAuthRecv(mContext);
|
||||||
recvAuth.setLogin(userName, userPassword);
|
recvAuth.setLogin(userName, userPassword);
|
||||||
recvAuth.setConnection(mBaseScheme, serverAddress, port, flags);
|
recvAuth.setConnection(mBaseScheme, serverAddress, port, flags, certAlias);
|
||||||
recvAuth.mDomain = null;
|
recvAuth.mDomain = null;
|
||||||
|
|
||||||
// Check for a duplicate account (requires async DB work) and if OK, proceed with check
|
// Check for a duplicate account (requires async DB work) and if OK, proceed with check
|
||||||
|
|
|
@ -0,0 +1,167 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
package com.android.email.view;
|
||||||
|
|
||||||
|
import com.android.email.R;
|
||||||
|
import com.android.email.activity.UiUtilities;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
import android.security.KeyChain;
|
||||||
|
import android.security.KeyChainAliasCallback;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.View.OnClickListener;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple view that can be used to select a certificate from the system {@link KeyChain}.
|
||||||
|
*
|
||||||
|
* Host activities must register themselves view {@link #setActivity} for this selector to work,
|
||||||
|
* since it requires firing system {@link Intent}s.
|
||||||
|
*/
|
||||||
|
public class CertificateSelector extends LinearLayout implements
|
||||||
|
OnClickListener, KeyChainAliasCallback {
|
||||||
|
|
||||||
|
/** Button to select or remove the certificate. */
|
||||||
|
private Button mSelectButton;
|
||||||
|
private TextView mAliasText;
|
||||||
|
|
||||||
|
/** The host activity. */
|
||||||
|
private Activity mActivity;
|
||||||
|
|
||||||
|
|
||||||
|
public CertificateSelector(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
public CertificateSelector(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
}
|
||||||
|
public CertificateSelector(Context context, AttributeSet attrs, int defStyle) {
|
||||||
|
super(context, attrs, defStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setActivity(Activity activity) {
|
||||||
|
mActivity = activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onFinishInflate() {
|
||||||
|
super.onFinishInflate();
|
||||||
|
|
||||||
|
mAliasText = UiUtilities.getView(this, R.id.certificate_alias);
|
||||||
|
mSelectButton = UiUtilities.getView(this, R.id.select_button);
|
||||||
|
mSelectButton.setOnClickListener(this);
|
||||||
|
setCertificate(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCertificate(String alias) {
|
||||||
|
mAliasText.setText(alias);
|
||||||
|
mAliasText.setVisibility((alias == null) ? View.GONE : View.VISIBLE);
|
||||||
|
mSelectButton.setText(getResources().getString(
|
||||||
|
(alias == null)
|
||||||
|
? R.string.account_setup_exchange_use_certificate
|
||||||
|
: R.string.account_setup_exchange_remove_certificate));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasCertificate() {
|
||||||
|
return mAliasText.getVisibility() == View.VISIBLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the alias for the currently selected certificate, or null if one is not selected.
|
||||||
|
*/
|
||||||
|
public String getCertificate() {
|
||||||
|
return hasCertificate() ? mAliasText.getText().toString() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(View target) {
|
||||||
|
if (target == mSelectButton && mActivity != null) {
|
||||||
|
if (hasCertificate()) {
|
||||||
|
// Handle the click on the button when it says "Remove"
|
||||||
|
setCertificate(null);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// We don't restrict the chooser for certificate types since 95% of the time the
|
||||||
|
// user will probably only have one certificate installed and it'll be the right
|
||||||
|
// "type". Just let them fail and select a different one if it doesn't match.
|
||||||
|
KeyChain.choosePrivateKeyAlias(
|
||||||
|
mActivity, this,
|
||||||
|
null /* keytypes */, null /* issuers */, null /* host */, -1 /* port */);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyChainAliasCallback
|
||||||
|
@Override
|
||||||
|
public void alias(String alias) {
|
||||||
|
if (alias != null) {
|
||||||
|
setCertificate(alias);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
protected void onRestoreInstanceState(Parcelable parcel) {
|
||||||
|
SavedState savedState = (SavedState) parcel;
|
||||||
|
super.onRestoreInstanceState(savedState.getSuperState());
|
||||||
|
setCertificate(savedState.mValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Parcelable onSaveInstanceState() {
|
||||||
|
return new SavedState(super.onSaveInstanceState(), getCertificate());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SavedState extends BaseSavedState {
|
||||||
|
final String mValue;
|
||||||
|
|
||||||
|
SavedState(Parcelable superState, String value) {
|
||||||
|
super(superState);
|
||||||
|
mValue = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(Parcel out, int flags) {
|
||||||
|
super.writeToParcel(out, flags);
|
||||||
|
out.writeString(mValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("hiding")
|
||||||
|
public static final Parcelable.Creator<SavedState> CREATOR
|
||||||
|
= new Parcelable.Creator<SavedState>() {
|
||||||
|
public SavedState createFromParcel(Parcel in) {
|
||||||
|
return new SavedState(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SavedState[] newArray(int size) {
|
||||||
|
return new SavedState[size];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private SavedState(Parcel in) {
|
||||||
|
super(in);
|
||||||
|
mValue = in.readString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue