diff --git a/src/frontends/android/res/layout/profile_detail_view.xml b/src/frontends/android/res/layout/profile_detail_view.xml
index 56a50655f..4952ebaa5 100644
--- a/src/frontends/android/res/layout/profile_detail_view.xml
+++ b/src/frontends/android/res/layout/profile_detail_view.xml
@@ -79,6 +79,31 @@
android:inputType="textPassword|textNoSuggestions"
android:hint="@string/profile_password_hint" />
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/frontends/android/res/values/strings.xml b/src/frontends/android/res/values/strings.xml
index 8c171db65..f2db4a676 100644
--- a/src/frontends/android/res/values/strings.xml
+++ b/src/frontends/android/res/values/strings.xml
@@ -41,8 +41,13 @@
Username:
Password:
(prompt when needed)
+ CA certificate:
+ Select automatically
+ Show all certificates
Please enter the gateway address here
Please enter your username here
+ No CA certificate selected
+ Please select one or activate Select automatically
diff --git a/src/frontends/android/src/org/strongswan/android/ui/VpnProfileDetailActivity.java b/src/frontends/android/src/org/strongswan/android/ui/VpnProfileDetailActivity.java
index 56eef15ac..05ba5e8b3 100644
--- a/src/frontends/android/src/org/strongswan/android/ui/VpnProfileDetailActivity.java
+++ b/src/frontends/android/src/org/strongswan/android/ui/VpnProfileDetailActivity.java
@@ -17,34 +17,56 @@
package org.strongswan.android.ui;
+import java.security.cert.X509Certificate;
+import java.util.Hashtable;
+
import org.strongswan.android.R;
import org.strongswan.android.data.VpnProfile;
import org.strongswan.android.data.VpnProfileDataSource;
+import org.strongswan.android.logic.TrustedCertificateManager;
+import org.strongswan.android.ui.adapter.TrustedCertificateAdapter;
import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
import android.content.Intent;
+import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
+import android.view.View;
import android.view.Window;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.EditText;
+import android.widget.Spinner;
public class VpnProfileDetailActivity extends Activity
{
private VpnProfileDataSource mDataSource;
private Long mId;
private VpnProfile mProfile;
+ private boolean mCertsLoaded;
+ private String mCertAlias;
+ private Spinner mCertSpinner;
+ private TrustedCertificateAdapter.CertEntry mSelectedCert;
private EditText mName;
private EditText mGateway;
private EditText mUsername;
private EditText mPassword;
+ private CheckBox mCheckAll;
+ private CheckBox mCheckAuto;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
+ requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
/* the title is set when we load the profile, if any */
getActionBar().setDisplayHomeAsUpEnabled(true);
@@ -59,6 +81,45 @@ public class VpnProfileDetailActivity extends Activity
mGateway = (EditText)findViewById(R.id.gateway);
mUsername = (EditText)findViewById(R.id.username);
+ mCheckAll = (CheckBox)findViewById(R.id.ca_show_all);
+ mCheckAuto = (CheckBox)findViewById(R.id.ca_auto);
+ mCertSpinner = (Spinner)findViewById(R.id.ca_spinner);
+
+ mCheckAuto.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
+ {
+ updateCertSpinner();
+ }
+ });
+
+ mCheckAll.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
+ {
+ Hashtable certs;
+ certs = isChecked ? TrustedCertificateManager.getInstance().getAllCACertificates()
+ : TrustedCertificateManager.getInstance().getUserCACertificates();
+ mCertSpinner.setAdapter(new TrustedCertificateAdapter(VpnProfileDetailActivity.this, certs));
+ mSelectedCert = (TrustedCertificateAdapter.CertEntry)mCertSpinner.getSelectedItem();
+ }
+ });
+
+ mCertSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView> parent, View view,
+ int pos, long id)
+ {
+ mSelectedCert = (TrustedCertificateAdapter.CertEntry)parent.getSelectedItem();
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> arg0)
+ {
+ mSelectedCert = null;
+ }
+ });
+
mId = savedInstanceState == null ? null : savedInstanceState.getLong(VpnProfileDataSource.KEY_ID);
if (mId == null)
{
@@ -67,6 +128,8 @@ public class VpnProfileDetailActivity extends Activity
}
loadProfileData();
+
+ new CertificateLoadTask().execute();
}
@Override
@@ -108,6 +171,111 @@ public class VpnProfileDetailActivity extends Activity
}
}
+ /**
+ * Show an alert in case the previously selected certificate is not found anymore
+ * or the user did not select a certificate in the spinner.
+ */
+ private void showCertificateAlert()
+ {
+ AlertDialog.Builder adb = new AlertDialog.Builder(VpnProfileDetailActivity.this);
+ adb.setTitle(R.string.alert_text_nocertfound_title);
+ adb.setMessage(R.string.alert_text_nocertfound);
+ adb.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id)
+ {
+ dialog.cancel();
+ }
+ });
+ adb.show();
+ }
+
+ /**
+ * Asynchronously executed task which confirms that the certificates are loaded.
+ * They are loaded from the main Activity already but might not be ready yet, or
+ * unloaded again.
+ *
+ * Once loaded the CA certificate spinner and checkboxes are updated
+ * accordingly.
+ */
+ private class CertificateLoadTask extends AsyncTask
+ {
+ @Override
+ protected void onPreExecute()
+ {
+ setProgressBarIndeterminateVisibility(true);
+ }
+
+ @Override
+ protected TrustedCertificateManager doInBackground(Void... params)
+ {
+ return TrustedCertificateManager.getInstance().load();
+ }
+
+ @Override
+ protected void onPostExecute(TrustedCertificateManager result)
+ {
+ TrustedCertificateAdapter adapter;
+ if (mCertAlias != null && mCertAlias.startsWith("system:"))
+ {
+ mCheckAll.setChecked(true);
+ adapter = new TrustedCertificateAdapter(VpnProfileDetailActivity.this,
+ result.getAllCACertificates());
+ }
+ else
+ {
+ mCheckAll.setChecked(false);
+ adapter = new TrustedCertificateAdapter(VpnProfileDetailActivity.this,
+ result.getUserCACertificates());
+ }
+ mCertSpinner.setAdapter(adapter);
+
+ if (mCertAlias != null)
+ {
+ int position = adapter.getItemPosition(mCertAlias);
+ if (position == -1)
+ { /* previously selected certificate is not here anymore */
+ showCertificateAlert();
+ }
+ else
+ {
+ mCertSpinner.setSelection(position);
+ }
+ }
+
+ mSelectedCert = (TrustedCertificateAdapter.CertEntry)mCertSpinner.getSelectedItem();
+
+ setProgressBarIndeterminateVisibility(false);
+ mCertsLoaded = true;
+ updateCertSpinner();
+ }
+ }
+
+ /**
+ * Update the CA certificate selection UI depending on whether the
+ * certificate should be automatically selected or not.
+ */
+ private void updateCertSpinner()
+ {
+ if (!mCheckAuto.isChecked())
+ {
+ if (mCertsLoaded)
+ {
+ mCertSpinner.setEnabled(true);
+ mCertSpinner.setVisibility(View.VISIBLE);
+ mCheckAll.setEnabled(true);
+ mCheckAll.setVisibility(View.VISIBLE);
+ }
+ }
+ else
+ {
+ mCertSpinner.setEnabled(false);
+ mCertSpinner.setVisibility(View.GONE);
+ mCheckAll.setEnabled(false);
+ mCheckAll.setVisibility(View.GONE);
+ }
+ }
+
/**
* Save or update the profile depending on whether we actually have a
* profile object or not (this was created in updateProfileData)
@@ -149,6 +317,11 @@ public class VpnProfileDetailActivity extends Activity
mUsername.setError(getString(R.string.alert_text_no_input_username));
valid = false;
}
+ if (!mCheckAuto.isChecked() && mSelectedCert == null)
+ {
+ showCertificateAlert();
+ valid = false;
+ }
return valid;
}
@@ -166,6 +339,8 @@ public class VpnProfileDetailActivity extends Activity
String password = mPassword.getText().toString().trim();
password = password.isEmpty() ? null : password;
mProfile.setPassword(password);
+ String certAlias = mCheckAuto.isChecked() ? null : mSelectedCert.mAlias;
+ mProfile.setCertificateAlias(certAlias);
}
/**
@@ -183,6 +358,7 @@ public class VpnProfileDetailActivity extends Activity
mGateway.setText(mProfile.getGateway());
mUsername.setText(mProfile.getUsername());
mPassword.setText(mProfile.getPassword());
+ mCertAlias = mProfile.getCertificateAlias();
getActionBar().setTitle(mProfile.getName());
}
else
@@ -192,5 +368,8 @@ public class VpnProfileDetailActivity extends Activity
finish();
}
}
+ mCheckAll.setChecked(false);
+ mCheckAuto.setChecked(mCertAlias == null);
+ updateCertSpinner();
}
}