diff --git a/res/layout-sw600dp/account_setup_exchange_fragment.xml b/res/layout-sw600dp/account_setup_exchange_fragment.xml
index 17433ff4c..2291a8284 100644
--- a/res/layout-sw600dp/account_setup_exchange_fragment.xml
+++ b/res/layout-sw600dp/account_setup_exchange_fragment.xml
@@ -87,12 +87,6 @@
android:layout_width="match_parent"
android:layout_height="1px"
android:background="@color/account_setup_divider_color" />
-
-
-
+
+
diff --git a/res/layout/account_setup_exchange_fragment.xml b/res/layout/account_setup_exchange_fragment.xml
index 060e6b5f4..e0e86da07 100644
--- a/res/layout/account_setup_exchange_fragment.xml
+++ b/res/layout/account_setup_exchange_fragment.xml
@@ -69,6 +69,10 @@
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:text="@string/account_setup_exchange_trust_certificates_label" />
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 22b20f91a..fdcaf918f 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -690,6 +690,10 @@ save attachment.
Use secure connection (SSL)
Accept all SSL certificates
+
+ Use client certificate
+
+ Remove
Mobile Device ID
diff --git a/src/com/android/email/activity/setup/AccountSetupExchangeFragment.java b/src/com/android/email/activity/setup/AccountSetupExchangeFragment.java
index 27fe60015..4ea9dce7e 100644
--- a/src/com/android/email/activity/setup/AccountSetupExchangeFragment.java
+++ b/src/com/android/email/activity/setup/AccountSetupExchangeFragment.java
@@ -22,6 +22,7 @@ import com.android.email.R;
import com.android.email.activity.UiUtilities;
import com.android.email.mail.Store;
import com.android.email.provider.AccountBackupRestore;
+import com.android.email.view.CertificateSelector;
import com.android.emailcommon.Device;
import com.android.emailcommon.Logging;
import com.android.emailcommon.provider.Account;
@@ -63,6 +64,7 @@ public class AccountSetupExchangeFragment extends AccountServerBaseFragment
private EditText mServerView;
private CheckBox mSslSecurityView;
private CheckBox mTrustCertificatesView;
+ private CertificateSelector mClientCertificateSelector;
// Support for lifecycle
private boolean mStarted;
@@ -100,13 +102,13 @@ public class AccountSetupExchangeFragment extends AccountServerBaseFragment
View view = inflater.inflate(layoutId, container, false);
Context context = getActivity();
- mUsernameView = (EditText) UiUtilities.getView(view, R.id.account_username);
- mPasswordView = (EditText) UiUtilities.getView(view, R.id.account_password);
- mServerView = (EditText) UiUtilities.getView(view, R.id.account_server);
- mSslSecurityView = (CheckBox) UiUtilities.getView(view, R.id.account_ssl);
+ mUsernameView = UiUtilities.getView(view, R.id.account_username);
+ mPasswordView = UiUtilities.getView(view, R.id.account_password);
+ mServerView = UiUtilities.getView(view, R.id.account_server);
+ mSslSecurityView = UiUtilities.getView(view, R.id.account_ssl);
mSslSecurityView.setOnCheckedChangeListener(this);
- mTrustCertificatesView = (CheckBox) UiUtilities.getView(view,
- R.id.account_trust_certificates);
+ mTrustCertificatesView = UiUtilities.getView(view, R.id.account_trust_certificates);
+ mClientCertificateSelector = UiUtilities.getView(view, R.id.client_certificate_selector);
// Calls validateFields() which enables or disables the Next button
// based on the fields' validity.
@@ -146,6 +148,7 @@ public class AccountSetupExchangeFragment extends AccountServerBaseFragment
Log.d(Logging.LOG_TAG, "AccountSetupExchangeFragment onActivityCreated");
}
super.onActivityCreated(savedInstanceState);
+ mClientCertificateSelector.setActivity(getActivity());
}
/**
@@ -275,7 +278,10 @@ public class AccountSetupExchangeFragment extends AccountServerBaseFragment
boolean trustCertificates = 0 != (hostAuth.mFlags & HostAuth.FLAG_TRUST_ALL);
mSslSecurityView.setChecked(ssl);
mTrustCertificatesView.setChecked(trustCertificates);
- showTrustCertificates(ssl);
+ if (hostAuth.mClientCertAlias != null) {
+ mClientCertificateSelector.setCertificate(hostAuth.mClientCertAlias);
+ }
+ onUseSslChanged(ssl);
mLoadedRecvAuth = hostAuth;
mLoaded = true;
@@ -304,16 +310,19 @@ public class AccountSetupExchangeFragment extends AccountServerBaseFragment
return enabled;
}
+ @Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (buttonView.getId() == R.id.account_ssl) {
- showTrustCertificates(isChecked);
+ onUseSslChanged(isChecked);
}
}
- public void showTrustCertificates(boolean visible) {
- int mode = visible ? View.VISIBLE : View.GONE;
+ public void onUseSslChanged(boolean useSsl) {
+ int mode = useSsl ? View.VISIBLE : View.GONE;
mTrustCertificatesView.setVisibility(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()) {
flags |= HostAuth.FLAG_TRUST_ALL;
}
+ String certAlias = mClientCertificateSelector.getCertificate();
String serverAddress = mServerView.getText().toString().trim();
int port = mSslSecurityView.isChecked() ? 443 : 80;
HostAuth sendAuth = account.getOrCreateHostAuthSend(mContext);
sendAuth.setLogin(userName, userPassword);
- sendAuth.setConnection(mBaseScheme, serverAddress, port, flags);
+ sendAuth.setConnection(mBaseScheme, serverAddress, port, flags, certAlias);
sendAuth.mDomain = null;
HostAuth recvAuth = account.getOrCreateHostAuthRecv(mContext);
recvAuth.setLogin(userName, userPassword);
- recvAuth.setConnection(mBaseScheme, serverAddress, port, flags);
+ recvAuth.setConnection(mBaseScheme, serverAddress, port, flags, certAlias);
recvAuth.mDomain = null;
// Check for a duplicate account (requires async DB work) and if OK, proceed with check
diff --git a/src/com/android/email/view/CertificateSelector.java b/src/com/android/email/view/CertificateSelector.java
new file mode 100644
index 000000000..d9d9e3ebe
--- /dev/null
+++ b/src/com/android/email/view/CertificateSelector.java
@@ -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 CREATOR
+ = new Parcelable.Creator() {
+ 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();
+ }
+ }
+}