Merge "Introduce a cert selector in exchange settings UI."

This commit is contained in:
Ben Komalo 2011-06-15 11:17:40 -07:00 committed by Android (Google) Code Review
commit b40bdb17df
6 changed files with 248 additions and 18 deletions

View File

@ -87,12 +87,6 @@
android:layout_width="match_parent"
android:layout_height="1px"
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
android:id="@+id/account_trust_certificates"
android:layout_marginTop="16dip"
@ -108,6 +102,16 @@
android:layout_height="1px"
android:background="@color/account_setup_divider_color"
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>
<TableRow
android:paddingTop="32dip" >

View File

@ -69,6 +69,10 @@
android:layout_height="wrap_content"
android:layout_width="match_parent"
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
android:text="@string/account_setup_exchange_device_id_label"
android:layout_height="wrap_content"

View File

@ -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>

View File

@ -690,6 +690,10 @@ save attachment.</string>
<string name="account_setup_exchange_ssl_label">Use secure connection (SSL)</string>
<!-- On "Exchange" setup screen, the trust ssl certificates checkbox label -->
<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] -->
<string name="account_setup_exchange_device_id_label">Mobile Device ID</string>

View File

@ -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

View File

@ -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();
}
}
}