android: Simplified handling of error dialog that is displayed if VpnService API...
[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 new VpnNotSupportedError().show(getFragmentManager(), "ErrorDialog");
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.getPassword() == null)
147 {
148 LoginDialog login = new LoginDialog();
149 login.setArguments(profileInfo);
150 login.show(getFragmentManager(), "LoginDialog");
151 }
152 else
153 {
154 profileInfo.putString(VpnProfileDataSource.KEY_PASSWORD, profile.getPassword());
155 prepareVpnService(profileInfo);
156 }
157 }
158
159 /**
160 * Class that loads or reloads the cached CA certificates.
161 */
162 private class CertificateLoadTask extends AsyncTask<Boolean, Void, TrustedCertificateManager>
163 {
164 @Override
165 protected void onPreExecute()
166 {
167 setProgressBarIndeterminateVisibility(true);
168 }
169 @Override
170 protected TrustedCertificateManager doInBackground(Boolean... params)
171 {
172 if (params.length > 0 && params[0])
173 { /* force a reload of the certificates */
174 return TrustedCertificateManager.getInstance().reload();
175 }
176 return TrustedCertificateManager.getInstance().load();
177 }
178 @Override
179 protected void onPostExecute(TrustedCertificateManager result)
180 {
181 setProgressBarIndeterminateVisibility(false);
182 }
183 }
184
185 /**
186 * Class that displays a login dialog and initiates the selected VPN
187 * profile if the user confirms the dialog.
188 */
189 public static class LoginDialog extends DialogFragment
190 {
191 @Override
192 public Dialog onCreateDialog(Bundle savedInstanceState)
193 {
194 final Bundle profileInfo = getArguments();
195 LayoutInflater inflater = getActivity().getLayoutInflater();
196 View view = inflater.inflate(R.layout.login_dialog, null);
197 EditText username = (EditText)view.findViewById(R.id.username);
198 username.setText(profileInfo.getString(VpnProfileDataSource.KEY_USERNAME));
199 final EditText password = (EditText)view.findViewById(R.id.password);
200
201 Builder adb = new AlertDialog.Builder(getActivity());
202 adb.setView(view);
203 adb.setTitle(getString(R.string.login_title));
204 adb.setPositiveButton(R.string.login_confirm, new DialogInterface.OnClickListener() {
205 @Override
206 public void onClick(DialogInterface dialog, int whichButton)
207 {
208 MainActivity activity = (MainActivity)getActivity();
209 profileInfo.putString(VpnProfileDataSource.KEY_PASSWORD, password.getText().toString().trim());
210 activity.prepareVpnService(profileInfo);
211 }
212 });
213 adb.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
214 @Override
215 public void onClick(DialogInterface dialog, int which)
216 {
217 dismiss();
218 }
219 });
220 return adb.create();
221 }
222 }
223
224 /**
225 * Class representing an error message which is displayed if VpnService is
226 * not supported on the current device.
227 */
228 public static class VpnNotSupportedError extends DialogFragment
229 {
230 @Override
231 public Dialog onCreateDialog(Bundle savedInstanceState)
232 {
233 return new AlertDialog.Builder(getActivity())
234 .setTitle(R.string.vpn_not_supported_title)
235 .setMessage(getString(R.string.vpn_not_supported))
236 .setCancelable(false)
237 .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
238 @Override
239 public void onClick(DialogInterface dialog, int id)
240 {
241 dialog.dismiss();
242 }
243 }).create();
244 }
245 }
246 }