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