Add an Activity that shows the log fragment
[strongswan.git] / src / frontends / android / src / org / strongswan / android / ui / VpnStateFragment.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.logic.VpnStateService;
23 import org.strongswan.android.logic.VpnStateService.ErrorState;
24 import org.strongswan.android.logic.VpnStateService.State;
25 import org.strongswan.android.logic.VpnStateService.VpnStateListener;
26
27 import android.app.AlertDialog;
28 import android.app.Fragment;
29 import android.app.ProgressDialog;
30 import android.app.Service;
31 import android.content.ComponentName;
32 import android.content.Context;
33 import android.content.DialogInterface;
34 import android.content.Intent;
35 import android.content.ServiceConnection;
36 import android.os.Bundle;
37 import android.os.IBinder;
38 import android.view.LayoutInflater;
39 import android.view.View;
40 import android.view.View.OnClickListener;
41 import android.view.ViewGroup;
42 import android.widget.Button;
43 import android.widget.TextView;
44
45 public class VpnStateFragment extends Fragment implements VpnStateListener
46 {
47 private static final String KEY_ERROR = "error";
48 private static final String KEY_NAME = "name";
49
50 private TextView mProfileNameView;
51 private TextView mProfileView;
52 private TextView mStateView;
53 private int stateBaseColor;
54 private Button mActionButton;
55 private ProgressDialog mProgressDialog;
56 private State mState;
57 private AlertDialog mErrorDialog;
58 private ErrorState mError;
59 private String mErrorProfileName;
60 private VpnStateService mService;
61 private final ServiceConnection mServiceConnection = new ServiceConnection() {
62 @Override
63 public void onServiceDisconnected(ComponentName name)
64 {
65 mService = null;
66 }
67
68 @Override
69 public void onServiceConnected(ComponentName name, IBinder service)
70 {
71 mService = ((VpnStateService.LocalBinder)service).getService();
72 mService.registerListener(VpnStateFragment.this);
73 updateView();
74 }
75 };
76
77 @Override
78 public void onCreate(Bundle savedInstanceState)
79 {
80 super.onCreate(savedInstanceState);
81
82 /* bind to the service only seems to work from the ApplicationContext */
83 Context context = getActivity().getApplicationContext();
84 context.bindService(new Intent(context, VpnStateService.class),
85 mServiceConnection, Service.BIND_AUTO_CREATE);
86
87 mError = ErrorState.NO_ERROR;
88 if (savedInstanceState != null && savedInstanceState.containsKey(KEY_ERROR))
89 {
90 mError = (ErrorState)savedInstanceState.getSerializable(KEY_ERROR);
91 mErrorProfileName = savedInstanceState.getString(KEY_NAME);
92 }
93 }
94
95 @Override
96 public void onSaveInstanceState(Bundle outState)
97 {
98 super.onSaveInstanceState(outState);
99
100 outState.putSerializable(KEY_ERROR, mError);
101 outState.putString(KEY_NAME, mErrorProfileName);
102 }
103
104 @Override
105 public View onCreateView(LayoutInflater inflater, ViewGroup container,
106 Bundle savedInstanceState)
107 {
108 View view = inflater.inflate(R.layout.vpn_state_fragment, null);
109
110 mActionButton = (Button)view.findViewById(R.id.action);
111 mActionButton.setOnClickListener(new OnClickListener() {
112 @Override
113 public void onClick(View v)
114 {
115 if (mService != null)
116 {
117 mService.disconnect();
118 }
119 }
120 });
121 enableActionButton(false);
122
123 mStateView = (TextView)view.findViewById(R.id.vpn_state);
124 stateBaseColor = mStateView.getCurrentTextColor();
125 mProfileView = (TextView)view.findViewById(R.id.vpn_profile_label);
126 mProfileNameView = (TextView)view.findViewById(R.id.vpn_profile_name);
127
128 return view;
129 }
130
131 @Override
132 public void onStart()
133 {
134 super.onStart();
135 if (mService != null)
136 {
137 updateView();
138 }
139 }
140
141 @Override
142 public void onStop()
143 {
144 super.onStop();
145 hideErrorDialog();
146 hideProgressDialog();
147 }
148
149 @Override
150 public void onDestroy()
151 {
152 super.onDestroy();
153 if (mService != null)
154 {
155 mService.unregisterListener(this);
156 getActivity().getApplicationContext().unbindService(mServiceConnection);
157 }
158 }
159
160 @Override
161 public void stateChanged()
162 {
163 updateView();
164 }
165
166 public void updateView()
167 {
168 State state = mService.getState();
169 ErrorState error = ErrorState.NO_ERROR;
170 String name = "", gateway = "";
171
172 if (state != State.DISABLED)
173 {
174 VpnProfile profile = mService.getProfile();
175 if (profile != null)
176 {
177 name = profile.getName();
178 gateway = profile.getGateway();
179 }
180 error = mService.getErrorState();
181 }
182
183 if (reportError(name, state, error))
184 {
185 return;
186 }
187
188 if (state == mState)
189 { /* avoid unnecessary updates */
190 return;
191 }
192
193 hideProgressDialog();
194 enableActionButton(false);
195 mProfileNameView.setText(name);
196 mState = state;
197
198 switch (state)
199 {
200 case DISABLED:
201 showProfile(false);
202 mStateView.setText(R.string.state_disabled);
203 mStateView.setTextColor(stateBaseColor);
204 break;
205 case CONNECTING:
206 showProfile(true);
207 showConnectDialog(name, gateway);
208 mStateView.setText(R.string.state_connecting);
209 mStateView.setTextColor(stateBaseColor);
210 break;
211 case CONNECTED:
212 showProfile(true);
213 enableActionButton(true);
214 mStateView.setText(R.string.state_connected);
215 mStateView.setTextColor(getResources().getColor(R.color.success_text));
216 break;
217 case DISCONNECTING:
218 showProfile(true);
219 showDisconnectDialog(name);
220 mStateView.setText(R.string.state_disconnecting);
221 mStateView.setTextColor(stateBaseColor);
222 break;
223 }
224 }
225
226 private boolean reportError(String name, State state, ErrorState error)
227 {
228 if (mError != ErrorState.NO_ERROR)
229 { /* we are currently reporting an error which was not yet dismissed */
230 error = mError;
231 name = mErrorProfileName;
232 }
233 else if (error != ErrorState.NO_ERROR && (state == State.CONNECTING || state == State.CONNECTED))
234 { /* while initiating we report errors */
235 mError = error;
236 mErrorProfileName = name;
237 }
238 else
239 { /* ignore all other errors */
240 error = ErrorState.NO_ERROR;
241 }
242 if (error == ErrorState.NO_ERROR)
243 {
244 hideErrorDialog();
245 return false;
246 }
247 else if (mErrorDialog != null)
248 { /* we already show the dialog */
249 return true;
250 }
251 hideProgressDialog();
252 mProfileNameView.setText(name);
253 showProfile(true);
254 enableActionButton(false);
255 mStateView.setText(R.string.state_error);
256 mStateView.setTextColor(getResources().getColor(R.color.error_text));
257 switch (error)
258 {
259 case AUTH_FAILED:
260 showErrorDialog(R.string.error_auth_failed);
261 break;
262 case PEER_AUTH_FAILED:
263 showErrorDialog(R.string.error_peer_auth_failed);
264 break;
265 case LOOKUP_FAILED:
266 showErrorDialog(R.string.error_lookup_failed);
267 break;
268 case UNREACHABLE:
269 showErrorDialog(R.string.error_unreachable);
270 break;
271 default:
272 showErrorDialog(R.string.error_generic);
273 break;
274 }
275 return true;
276 }
277
278 private void showProfile(boolean show)
279 {
280 mProfileView.setVisibility(show ? View.VISIBLE : View.GONE);
281 mProfileNameView.setVisibility(show ? View.VISIBLE : View.GONE);
282 }
283
284 private void enableActionButton(boolean enable)
285 {
286 mActionButton.setEnabled(enable);
287 mActionButton.setVisibility(enable ? View.VISIBLE : View.GONE);
288 }
289
290 private void hideProgressDialog()
291 {
292 if (mProgressDialog != null)
293 {
294 mProgressDialog.dismiss();
295 mProgressDialog = null;
296 }
297 }
298
299 private void hideErrorDialog()
300 {
301 if (mErrorDialog != null)
302 {
303 mErrorDialog.dismiss();
304 mErrorDialog = null;
305 }
306 }
307
308 private void showConnectDialog(String profile, String gateway)
309 {
310 mProgressDialog = new ProgressDialog(getActivity());
311 mProgressDialog.setTitle(String.format(getString(R.string.connecting_title), profile));
312 mProgressDialog.setMessage(String.format(getString(R.string.connecting_message), gateway));
313 mProgressDialog.setIndeterminate(true);
314 mProgressDialog.setCancelable(false);
315 mProgressDialog.setButton(getString(android.R.string.cancel),
316 new DialogInterface.OnClickListener()
317 {
318 @Override
319 public void onClick(DialogInterface dialog, int which)
320 {
321 if (mService != null)
322 {
323 mService.disconnect();
324 }
325 }
326 });
327 mProgressDialog.show();
328 }
329
330 private void showDisconnectDialog(String profile)
331 {
332 mProgressDialog = new ProgressDialog(getActivity());
333 mProgressDialog.setMessage(getString(R.string.state_disconnecting));
334 mProgressDialog.setIndeterminate(true);
335 mProgressDialog.setCancelable(false);
336 mProgressDialog.show();
337 }
338
339 private void showErrorDialog(int textid)
340 {
341 mErrorDialog = new AlertDialog.Builder(getActivity())
342 .setMessage(getString(R.string.error_introduction) + " " + getString(textid))
343 .setCancelable(false)
344 .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
345 @Override
346 public void onClick(DialogInterface dialog, int id)
347 { /* clear the error */
348 mError = ErrorState.NO_ERROR;
349 mErrorDialog = null;
350 updateView();
351 dialog.dismiss();
352 }
353 }).create();
354 mErrorDialog.show();
355 }
356 }