]> git.ipfire.org Git - thirdparty/strongswan.git/blob - src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnStateFragment.java
android: Avoid IllegalStateException in state fragments
[thirdparty/strongswan.git] / src / frontends / android / app / src / main / java / org / strongswan / android / ui / VpnStateFragment.java
1 /*
2 * Copyright (C) 2012-2018 Tobias Brunner
3 * Copyright (C) 2012 Giuliano Grassi
4 * Copyright (C) 2012 Ralf Sager
5 * HSR 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 android.app.Service;
21 import android.content.ComponentName;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.ServiceConnection;
25 import android.os.Bundle;
26 import android.os.IBinder;
27 import android.support.v4.app.Fragment;
28 import android.support.v4.content.ContextCompat;
29 import android.view.LayoutInflater;
30 import android.view.View;
31 import android.view.View.OnClickListener;
32 import android.view.ViewGroup;
33 import android.widget.Button;
34 import android.widget.LinearLayout;
35 import android.widget.ProgressBar;
36 import android.widget.TextView;
37
38 import org.strongswan.android.R;
39 import org.strongswan.android.data.VpnProfile;
40 import org.strongswan.android.logic.VpnStateService;
41 import org.strongswan.android.logic.VpnStateService.ErrorState;
42 import org.strongswan.android.logic.VpnStateService.State;
43 import org.strongswan.android.logic.VpnStateService.VpnStateListener;
44
45 public class VpnStateFragment extends Fragment implements VpnStateListener
46 {
47 private static final String KEY_ERROR_CONNECTION_ID = "error_connection_id";
48
49 private boolean mVisible;
50 private TextView mProfileNameView;
51 private TextView mProfileView;
52 private TextView mStateView;
53 private int mColorStateBase;
54 private int mColorStateError;
55 private int mColorStateSuccess;
56 private Button mActionButton;
57 private ProgressBar mProgress;
58 private LinearLayout mErrorView;
59 private TextView mErrorText;
60 private Button mErrorRetry;
61 private Button mShowLog;
62 private long mErrorConnectionID;
63 private VpnStateService mService;
64 private final ServiceConnection mServiceConnection = new ServiceConnection()
65 {
66 @Override
67 public void onServiceDisconnected(ComponentName name)
68 {
69 mService = null;
70 }
71
72 @Override
73 public void onServiceConnected(ComponentName name, IBinder service)
74 {
75 mService = ((VpnStateService.LocalBinder)service).getService();
76 if (mVisible)
77 {
78 mService.registerListener(VpnStateFragment.this);
79 updateView();
80 }
81 }
82 };
83 private OnClickListener mDisconnectListener = new OnClickListener()
84 {
85 @Override
86 public void onClick(View v)
87 {
88 if (mService != null)
89 {
90 mService.disconnect();
91 }
92 }
93 };
94
95 @Override
96 public void onCreate(Bundle savedInstanceState)
97 {
98 super.onCreate(savedInstanceState);
99
100 mColorStateError = ContextCompat.getColor(getActivity(), R.color.error_text);
101 mColorStateSuccess = ContextCompat.getColor(getActivity(), R.color.success_text);
102
103 /* bind to the service only seems to work from the ApplicationContext */
104 Context context = getActivity().getApplicationContext();
105 context.bindService(new Intent(context, VpnStateService.class),
106 mServiceConnection, Service.BIND_AUTO_CREATE);
107
108 mErrorConnectionID = 0;
109 if (savedInstanceState != null && savedInstanceState.containsKey(KEY_ERROR_CONNECTION_ID))
110 {
111 mErrorConnectionID = (Long)savedInstanceState.getSerializable(KEY_ERROR_CONNECTION_ID);
112 }
113 }
114
115 @Override
116 public void onSaveInstanceState(Bundle outState)
117 {
118 super.onSaveInstanceState(outState);
119
120 outState.putSerializable(KEY_ERROR_CONNECTION_ID, mErrorConnectionID);
121 }
122
123 @Override
124 public View onCreateView(LayoutInflater inflater, ViewGroup container,
125 Bundle savedInstanceState)
126 {
127 View view = inflater.inflate(R.layout.vpn_state_fragment, null);
128
129 mActionButton = (Button)view.findViewById(R.id.action);
130 mActionButton.setOnClickListener(v -> clearError());
131 enableActionButton(null);
132
133 mErrorView = view.findViewById(R.id.vpn_error);
134 mErrorText = view.findViewById(R.id.vpn_error_text);
135 mErrorRetry = view.findViewById(R.id.retry);
136 mShowLog = view.findViewById(R.id.show_log);
137 mProgress = (ProgressBar)view.findViewById(R.id.progress);
138 mStateView = (TextView)view.findViewById(R.id.vpn_state);
139 mColorStateBase = mStateView.getCurrentTextColor();
140 mProfileView = (TextView)view.findViewById(R.id.vpn_profile_label);
141 mProfileNameView = (TextView)view.findViewById(R.id.vpn_profile_name);
142
143 mErrorRetry.setOnClickListener(v -> {
144 if (mService != null)
145 {
146 mService.reconnect();
147 }
148 });
149 mShowLog.setOnClickListener(v -> {
150 Intent intent = new Intent(getActivity(), LogActivity.class);
151 startActivity(intent);
152 });
153
154 return view;
155 }
156
157 @Override
158 public void onStart()
159 {
160 super.onStart();
161 mVisible = true;
162 if (mService != null)
163 {
164 mService.registerListener(this);
165 updateView();
166 }
167 }
168
169 @Override
170 public void onStop()
171 {
172 super.onStop();
173 mVisible = false;
174 if (mService != null)
175 {
176 mService.unregisterListener(this);
177 }
178 }
179
180 @Override
181 public void onDestroy()
182 {
183 super.onDestroy();
184 if (mService != null)
185 {
186 getActivity().getApplicationContext().unbindService(mServiceConnection);
187 }
188 }
189
190 @Override
191 public void stateChanged()
192 {
193 updateView();
194 }
195
196 public void updateView()
197 {
198 long connectionID = mService.getConnectionID();
199 VpnProfile profile = mService.getProfile();
200 State state = mService.getState();
201 ErrorState error = mService.getErrorState();
202 String name = "";
203
204 if (getActivity() == null)
205 {
206 return;
207 }
208
209 if (profile != null)
210 {
211 name = profile.getName();
212 }
213
214 if (reportError(connectionID, name, error))
215 {
216 return;
217 }
218
219 mProfileNameView.setText(name);
220 mProgress.setIndeterminate(true);
221
222 switch (state)
223 {
224 case DISABLED:
225 showProfile(false);
226 mProgress.setVisibility(View.GONE);
227 enableActionButton(null);
228 mStateView.setText(R.string.state_disabled);
229 mStateView.setTextColor(mColorStateBase);
230 break;
231 case CONNECTING:
232 showProfile(true);
233 mProgress.setVisibility(View.VISIBLE);
234 enableActionButton(getString(android.R.string.cancel));
235 mStateView.setText(R.string.state_connecting);
236 mStateView.setTextColor(mColorStateBase);
237 break;
238 case CONNECTED:
239 showProfile(true);
240 mProgress.setVisibility(View.GONE);
241 enableActionButton(getString(R.string.disconnect));
242 mStateView.setText(R.string.state_connected);
243 mStateView.setTextColor(mColorStateSuccess);
244 break;
245 case DISCONNECTING:
246 showProfile(true);
247 mProgress.setVisibility(View.VISIBLE);
248 enableActionButton(null);
249 mStateView.setText(R.string.state_disconnecting);
250 mStateView.setTextColor(mColorStateBase);
251 break;
252 }
253 }
254
255 private boolean reportError(long connectionID, String name, ErrorState error)
256 {
257 if (error == ErrorState.NO_ERROR)
258 {
259 mErrorView.setVisibility(View.GONE);
260 return false;
261 }
262 mErrorConnectionID = connectionID;
263 mProfileNameView.setText(name);
264 showProfile(true);
265 mStateView.setText(R.string.state_error);
266 mStateView.setTextColor(mColorStateError);
267 enableActionButton(getString(android.R.string.cancel));
268
269 int retry = mService.getRetryIn();
270 if (retry > 0)
271 {
272 mProgress.setIndeterminate(false);
273 mProgress.setMax(mService.getRetryTimeout());
274 mProgress.setProgress(retry);
275 mProgress.setVisibility(View.VISIBLE);
276 mStateView.setText(getResources().getQuantityString(R.plurals.retry_in, retry, retry));
277 }
278 else if (mService.getRetryTimeout() <= 0)
279 {
280 mProgress.setVisibility(View.GONE);
281 }
282
283 String text = getString(R.string.error_format, getString(mService.getErrorText()));
284 mErrorText.setText(text);
285 mErrorView.setVisibility(View.VISIBLE);
286 return true;
287 }
288
289 private void showProfile(boolean show)
290 {
291 mProfileView.setVisibility(show ? View.VISIBLE : View.GONE);
292 mProfileNameView.setVisibility(show ? View.VISIBLE : View.GONE);
293 }
294
295 private void enableActionButton(String text)
296 {
297 mActionButton.setText(text);
298 mActionButton.setEnabled(text != null);
299 mActionButton.setVisibility(text != null ? View.VISIBLE : View.GONE);
300 }
301
302 private void clearError()
303 {
304 if (mService != null)
305 {
306 mService.disconnect();
307 if (mService.getConnectionID() == mErrorConnectionID)
308 {
309 mService.setError(ErrorState.NO_ERROR);
310 }
311 }
312 }
313 }