b02c609029dd11671bcd1aefb06edf2d5aaacddf
[strongswan.git] / src / frontends / android / src / org / strongswan / android / ui / MainActivity.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 org.strongswan.android.R;
21 import org.strongswan.android.data.VpnProfile;
22 import org.strongswan.android.data.VpnProfileDataSource;
23 import org.strongswan.android.logic.CharonVpnService;
24 import org.strongswan.android.logic.TrustedCertificateManager;
25 import org.strongswan.android.ui.VpnProfileListFragment.OnVpnProfileSelectedListener;
26
27 import android.app.ActionBar;
28 import android.app.Activity;
29 import android.app.AlertDialog;
30 import android.app.AlertDialog.Builder;
31 import android.app.Dialog;
32 import android.app.DialogFragment;
33 import android.content.ActivityNotFoundException;
34 import android.content.DialogInterface;
35 import android.content.Intent;
36 import android.net.VpnService;
37 import android.os.AsyncTask;
38 import android.os.Bundle;
39 import android.view.LayoutInflater;
40 import android.view.Menu;
41 import android.view.MenuItem;
42 import android.view.View;
43 import android.view.Window;
44 import android.widget.EditText;
45
46 public class MainActivity extends Activity implements OnVpnProfileSelectedListener
47 {
48 public static final String CONTACT_EMAIL = "android@strongswan.org";
49 private static final String SHOW_ERROR_DIALOG = "errordialog";
50 private static final int PREPARE_VPN_SERVICE = 0;
51
52 private AlertDialog mErrorDialog;
53 private Bundle mProfileInfo;
54
55 @Override
56 public void onCreate(Bundle savedInstanceState)
57 {
58 super.onCreate(savedInstanceState);
59 requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
60 setContentView(R.layout.main);
61
62 ActionBar bar = getActionBar();
63 bar.setDisplayShowTitleEnabled(false);
64
65 if (savedInstanceState != null && savedInstanceState.getBoolean(SHOW_ERROR_DIALOG))
66 {
67 showVpnNotSupportedError();
68 }
69
70 /* load CA certificates in a background task */
71 new CertificateLoadTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, false);
72 }
73
74 @Override
75 protected void onSaveInstanceState(Bundle outState)
76 {
77 super.onSaveInstanceState(outState);
78 outState.putBoolean(SHOW_ERROR_DIALOG, mErrorDialog != null);
79 }
80
81 @Override
82 protected void onDestroy()
83 {
84 super.onDestroy();
85 if (mErrorDialog != null)
86 { /* avoid any errors about leaked windows */
87 mErrorDialog.dismiss();
88 }
89 }
90
91 @Override
92 public boolean onCreateOptionsMenu(Menu menu)
93 {
94 getMenuInflater().inflate(R.menu.main, menu);
95 return true;
96 }
97
98 @Override
99 public boolean onOptionsItemSelected(MenuItem item)
100 {
101 switch (item.getItemId())
102 {
103 case R.id.menu_reload_certs:
104 new CertificateLoadTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, true);
105 return true;
106 case R.id.menu_show_log:
107 Intent logIntent = new Intent(this, LogActivity.class);
108 startActivity(logIntent);
109 return true;
110 default:
111 return super.onOptionsItemSelected(item);
112 }
113 }
114
115 /**
116 * Prepare the VpnService. If this succeeds the current VPN profile is
117 * started.
118 * @param profileInfo a bundle containing the information about the profile to be started
119 */
120 protected void prepareVpnService(Bundle profileInfo)
121 {
122 Intent intent = VpnService.prepare(this);
123 /* store profile info until the user grants us permission */
124 mProfileInfo = profileInfo;
125 if (intent != null)
126 {
127 try
128 {
129 startActivityForResult(intent, PREPARE_VPN_SERVICE);
130 }
131 catch (ActivityNotFoundException ex)
132 {
133 /* it seems some devices, even though they come with Android 4,
134 * don't have the VPN components built into the system image.
135 * com.android.vpndialogs/com.android.vpndialogs.ConfirmDialog
136 * will not be found then */
137 showVpnNotSupportedError();
138 }
139 }
140 else
141 { /* user already granted permission to use VpnService */
142 onActivityResult(PREPARE_VPN_SERVICE, RESULT_OK, null);
143 }
144 }
145
146 @Override
147 protected void onActivityResult(int requestCode, int resultCode, Intent data)
148 {
149 switch (requestCode)
150 {
151 case PREPARE_VPN_SERVICE:
152 if (resultCode == RESULT_OK && mProfileInfo != null)
153 {
154 Intent intent = new Intent(this, CharonVpnService.class);
155 intent.putExtras(mProfileInfo);
156 this.startService(intent);
157 }
158 break;
159 default:
160 super.onActivityResult(requestCode, resultCode, data);
161 }
162 }
163
164 @Override
165 public void onVpnProfileSelected(VpnProfile profile)
166 {
167 Bundle profileInfo = new Bundle();
168 profileInfo.putLong(VpnProfileDataSource.KEY_ID, profile.getId());
169 profileInfo.putString(VpnProfileDataSource.KEY_USERNAME, profile.getUsername());
170 if (profile.getPassword() == null)
171 {
172 LoginDialog login = new LoginDialog();
173 login.setArguments(profileInfo);
174 login.show(getFragmentManager(), "LoginDialog");
175 }
176 else
177 {
178 profileInfo.putString(VpnProfileDataSource.KEY_PASSWORD, profile.getPassword());
179 prepareVpnService(profileInfo);
180 }
181 }
182
183 /**
184 * Show an error dialog if case the device lacks VPN support.
185 */
186 private void showVpnNotSupportedError()
187 {
188 mErrorDialog = new AlertDialog.Builder(this)
189 .setTitle(R.string.vpn_not_supported_title)
190 .setMessage(getString(R.string.vpn_not_supported))
191 .setCancelable(false)
192 .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
193 @Override
194 public void onClick(DialogInterface dialog, int id)
195 {
196 mErrorDialog = null;
197 dialog.dismiss();
198 }
199 }).show();
200 }
201
202 /**
203 * Class that loads or reloads the cached CA certificates.
204 */
205 private class CertificateLoadTask extends AsyncTask<Boolean, Void, TrustedCertificateManager>
206 {
207 @Override
208 protected void onPreExecute()
209 {
210 setProgressBarIndeterminateVisibility(true);
211 }
212 @Override
213 protected TrustedCertificateManager doInBackground(Boolean... params)
214 {
215 if (params.length > 0 && params[0])
216 { /* force a reload of the certificates */
217 return TrustedCertificateManager.getInstance().reload();
218 }
219 return TrustedCertificateManager.getInstance().load();
220 }
221 @Override
222 protected void onPostExecute(TrustedCertificateManager result)
223 {
224 setProgressBarIndeterminateVisibility(false);
225 }
226 }
227
228 /**
229 * Class that displays a login dialog and initiates the selected VPN
230 * profile if the user confirms the dialog.
231 */
232 public static class LoginDialog extends DialogFragment
233 {
234 @Override
235 public Dialog onCreateDialog(Bundle savedInstanceState)
236 {
237 final Bundle profileInfo = getArguments();
238 LayoutInflater inflater = getActivity().getLayoutInflater();
239 View view = inflater.inflate(R.layout.login_dialog, null);
240 EditText username = (EditText)view.findViewById(R.id.username);
241 username.setText(profileInfo.getString(VpnProfileDataSource.KEY_USERNAME));
242 final EditText password = (EditText)view.findViewById(R.id.password);
243
244 Builder adb = new AlertDialog.Builder(getActivity());
245 adb.setView(view);
246 adb.setTitle(getString(R.string.login_title));
247 adb.setPositiveButton(R.string.login_confirm, new DialogInterface.OnClickListener() {
248 @Override
249 public void onClick(DialogInterface dialog, int whichButton)
250 {
251 MainActivity activity = (MainActivity)getActivity();
252 profileInfo.putString(VpnProfileDataSource.KEY_PASSWORD, password.getText().toString().trim());
253 activity.prepareVpnService(profileInfo);
254 }
255 });
256 adb.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
257 @Override
258 public void onClick(DialogInterface dialog, int which)
259 {
260 dismiss();
261 }
262 });
263 return adb.create();
264 }
265 }
266 }