Show an error dialog when errors occur while establishing the VPN
[strongswan.git] / src / frontends / android / src / org / strongswan / android / ui / VpnProfileDetailActivity.java
1 /*
2 * Copyright (C) 2012 Tobias Brunner
3 * Copyright (C) 2012 Giuliano Grassi
4 * Copyright (C) 2012 Ralf Sager
5 * Hochschule fuer Technik Rapperswil
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 * for more details.
16 */
17
18 package org.strongswan.android.ui;
19
20 import java.security.cert.X509Certificate;
21 import java.util.Hashtable;
22
23 import org.strongswan.android.R;
24 import org.strongswan.android.data.VpnProfile;
25 import org.strongswan.android.data.VpnProfileDataSource;
26 import org.strongswan.android.logic.TrustedCertificateManager;
27 import org.strongswan.android.ui.adapter.TrustedCertificateAdapter;
28
29 import android.app.Activity;
30 import android.app.AlertDialog;
31 import android.content.DialogInterface;
32 import android.content.Intent;
33 import android.os.AsyncTask;
34 import android.os.Bundle;
35 import android.util.Log;
36 import android.view.Menu;
37 import android.view.MenuInflater;
38 import android.view.MenuItem;
39 import android.view.View;
40 import android.view.Window;
41 import android.widget.AdapterView;
42 import android.widget.AdapterView.OnItemSelectedListener;
43 import android.widget.CheckBox;
44 import android.widget.CompoundButton;
45 import android.widget.CompoundButton.OnCheckedChangeListener;
46 import android.widget.EditText;
47 import android.widget.Spinner;
48
49 public class VpnProfileDetailActivity extends Activity
50 {
51 private VpnProfileDataSource mDataSource;
52 private Long mId;
53 private VpnProfile mProfile;
54 private boolean mCertsLoaded;
55 private String mCertAlias;
56 private Spinner mCertSpinner;
57 private TrustedCertificateAdapter.CertEntry mSelectedCert;
58 private EditText mName;
59 private EditText mGateway;
60 private EditText mUsername;
61 private EditText mPassword;
62 private CheckBox mCheckAll;
63 private CheckBox mCheckAuto;
64
65 @Override
66 public void onCreate(Bundle savedInstanceState)
67 {
68 super.onCreate(savedInstanceState);
69 requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
70
71 /* the title is set when we load the profile, if any */
72 getActionBar().setDisplayHomeAsUpEnabled(true);
73
74 mDataSource = new VpnProfileDataSource(this);
75 mDataSource.open();
76
77 setContentView(R.layout.profile_detail_view);
78
79 mName = (EditText)findViewById(R.id.name);
80 mPassword = (EditText)findViewById(R.id.password);
81 mGateway = (EditText)findViewById(R.id.gateway);
82 mUsername = (EditText)findViewById(R.id.username);
83
84 mCheckAll = (CheckBox)findViewById(R.id.ca_show_all);
85 mCheckAuto = (CheckBox)findViewById(R.id.ca_auto);
86 mCertSpinner = (Spinner)findViewById(R.id.ca_spinner);
87
88 mCheckAuto.setOnCheckedChangeListener(new OnCheckedChangeListener() {
89 @Override
90 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
91 {
92 updateCertSpinner();
93 }
94 });
95
96 mCheckAll.setOnCheckedChangeListener(new OnCheckedChangeListener() {
97 @Override
98 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
99 {
100 Hashtable<String, X509Certificate> certs;
101 certs = isChecked ? TrustedCertificateManager.getInstance().getAllCACertificates()
102 : TrustedCertificateManager.getInstance().getUserCACertificates();
103 mCertSpinner.setAdapter(new TrustedCertificateAdapter(VpnProfileDetailActivity.this, certs));
104 mSelectedCert = (TrustedCertificateAdapter.CertEntry)mCertSpinner.getSelectedItem();
105 }
106 });
107
108 mCertSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
109 @Override
110 public void onItemSelected(AdapterView<?> parent, View view,
111 int pos, long id)
112 {
113 mSelectedCert = (TrustedCertificateAdapter.CertEntry)parent.getSelectedItem();
114 }
115
116 @Override
117 public void onNothingSelected(AdapterView<?> arg0)
118 {
119 mSelectedCert = null;
120 }
121 });
122
123 mId = savedInstanceState == null ? null : savedInstanceState.getLong(VpnProfileDataSource.KEY_ID);
124 if (mId == null)
125 {
126 Bundle extras = getIntent().getExtras();
127 mId = extras == null ? null : extras.getLong(VpnProfileDataSource.KEY_ID);
128 }
129
130 loadProfileData();
131
132 new CertificateLoadTask().execute();
133 }
134
135 @Override
136 protected void onDestroy()
137 {
138 super.onDestroy();
139 mDataSource.close();
140 }
141
142 @Override
143 protected void onSaveInstanceState(Bundle outState)
144 {
145 super.onSaveInstanceState(outState);
146 outState.putLong(VpnProfileDataSource.KEY_ID, mId);
147 }
148
149 @Override
150 public boolean onCreateOptionsMenu(Menu menu)
151 {
152 MenuInflater inflater = getMenuInflater();
153 inflater.inflate(R.menu.profile_edit, menu);
154 return true;
155 }
156
157 @Override
158 public boolean onOptionsItemSelected(MenuItem item)
159 {
160 switch (item.getItemId())
161 {
162 case android.R.id.home:
163 case R.id.menu_cancel:
164 finish();
165 return true;
166 case R.id.menu_accept:
167 saveProfile();
168 return true;
169 default:
170 return super.onOptionsItemSelected(item);
171 }
172 }
173
174 /**
175 * Show an alert in case the previously selected certificate is not found anymore
176 * or the user did not select a certificate in the spinner.
177 */
178 private void showCertificateAlert()
179 {
180 AlertDialog.Builder adb = new AlertDialog.Builder(VpnProfileDetailActivity.this);
181 adb.setTitle(R.string.alert_text_nocertfound_title);
182 adb.setMessage(R.string.alert_text_nocertfound);
183 adb.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
184 @Override
185 public void onClick(DialogInterface dialog, int id)
186 {
187 dialog.cancel();
188 }
189 });
190 adb.show();
191 }
192
193 /**
194 * Asynchronously executed task which confirms that the certificates are loaded.
195 * They are loaded from the main Activity already but might not be ready yet, or
196 * unloaded again.
197 *
198 * Once loaded the CA certificate spinner and checkboxes are updated
199 * accordingly.
200 */
201 private class CertificateLoadTask extends AsyncTask<Void, Void, TrustedCertificateManager>
202 {
203 @Override
204 protected void onPreExecute()
205 {
206 setProgressBarIndeterminateVisibility(true);
207 }
208
209 @Override
210 protected TrustedCertificateManager doInBackground(Void... params)
211 {
212 return TrustedCertificateManager.getInstance().load();
213 }
214
215 @Override
216 protected void onPostExecute(TrustedCertificateManager result)
217 {
218 TrustedCertificateAdapter adapter;
219 if (mCertAlias != null && mCertAlias.startsWith("system:"))
220 {
221 mCheckAll.setChecked(true);
222 adapter = new TrustedCertificateAdapter(VpnProfileDetailActivity.this,
223 result.getAllCACertificates());
224 }
225 else
226 {
227 mCheckAll.setChecked(false);
228 adapter = new TrustedCertificateAdapter(VpnProfileDetailActivity.this,
229 result.getUserCACertificates());
230 }
231 mCertSpinner.setAdapter(adapter);
232
233 if (mCertAlias != null)
234 {
235 int position = adapter.getItemPosition(mCertAlias);
236 if (position == -1)
237 { /* previously selected certificate is not here anymore */
238 showCertificateAlert();
239 }
240 else
241 {
242 mCertSpinner.setSelection(position);
243 }
244 }
245
246 mSelectedCert = (TrustedCertificateAdapter.CertEntry)mCertSpinner.getSelectedItem();
247
248 setProgressBarIndeterminateVisibility(false);
249 mCertsLoaded = true;
250 updateCertSpinner();
251 }
252 }
253
254 /**
255 * Update the CA certificate selection UI depending on whether the
256 * certificate should be automatically selected or not.
257 */
258 private void updateCertSpinner()
259 {
260 if (!mCheckAuto.isChecked())
261 {
262 if (mCertsLoaded)
263 {
264 mCertSpinner.setEnabled(true);
265 mCertSpinner.setVisibility(View.VISIBLE);
266 mCheckAll.setEnabled(true);
267 mCheckAll.setVisibility(View.VISIBLE);
268 }
269 }
270 else
271 {
272 mCertSpinner.setEnabled(false);
273 mCertSpinner.setVisibility(View.GONE);
274 mCheckAll.setEnabled(false);
275 mCheckAll.setVisibility(View.GONE);
276 }
277 }
278
279 /**
280 * Save or update the profile depending on whether we actually have a
281 * profile object or not (this was created in updateProfileData)
282 */
283 private void saveProfile()
284 {
285 if (verifyInput())
286 {
287 if (mProfile != null)
288 {
289 updateProfileData();
290 mDataSource.updateVpnProfile(mProfile);
291 }
292 else
293 {
294 mProfile = new VpnProfile();
295 updateProfileData();
296 mDataSource.insertProfile(mProfile);
297 }
298 setResult(RESULT_OK, new Intent().putExtra(VpnProfileDataSource.KEY_ID, mProfile.getId()));
299 finish();
300 }
301 }
302
303 /**
304 * Verify the user input and display error messages.
305 * @return true if the input is valid
306 */
307 private boolean verifyInput()
308 {
309 boolean valid = true;
310 if (mGateway.getText().toString().trim().isEmpty())
311 {
312 mGateway.setError(getString(R.string.alert_text_no_input_gateway));
313 valid = false;
314 }
315 if (mUsername.getText().toString().trim().isEmpty())
316 {
317 mUsername.setError(getString(R.string.alert_text_no_input_username));
318 valid = false;
319 }
320 if (!mCheckAuto.isChecked() && mSelectedCert == null)
321 {
322 showCertificateAlert();
323 valid = false;
324 }
325 return valid;
326 }
327
328 /**
329 * Update the profile object with the data entered by the user
330 */
331 private void updateProfileData()
332 {
333 /* the name is optional, we default to the gateway if none is given */
334 String name = mName.getText().toString().trim();
335 String gateway = mGateway.getText().toString().trim();
336 mProfile.setName(name.isEmpty() ? gateway : name);
337 mProfile.setGateway(gateway);
338 mProfile.setUsername(mUsername.getText().toString().trim());
339 String password = mPassword.getText().toString().trim();
340 password = password.isEmpty() ? null : password;
341 mProfile.setPassword(password);
342 String certAlias = mCheckAuto.isChecked() ? null : mSelectedCert.mAlias;
343 mProfile.setCertificateAlias(certAlias);
344 }
345
346 /**
347 * Load an existing profile if we got an ID
348 */
349 private void loadProfileData()
350 {
351 getActionBar().setTitle(R.string.add_profile);
352 if (mId != null)
353 {
354 mProfile = mDataSource.getVpnProfile(mId);
355 if (mProfile != null)
356 {
357 mName.setText(mProfile.getName());
358 mGateway.setText(mProfile.getGateway());
359 mUsername.setText(mProfile.getUsername());
360 mPassword.setText(mProfile.getPassword());
361 mCertAlias = mProfile.getCertificateAlias();
362 getActionBar().setTitle(mProfile.getName());
363 }
364 else
365 {
366 Log.e(VpnProfileDetailActivity.class.getSimpleName(),
367 "VPN profile with id " + mId + " not found");
368 finish();
369 }
370 }
371 mCheckAll.setChecked(false);
372 mCheckAuto.setChecked(mCertAlias == null);
373 updateCertSpinner();
374 }
375 }