]> git.ipfire.org Git - thirdparty/strongswan.git/blob - src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnTileService.java
android: Add Quick Settings tile to toggle VPN state
[thirdparty/strongswan.git] / src / frontends / android / app / src / main / java / org / strongswan / android / ui / VpnTileService.java
1 /*
2 * Copyright (C) 2018 Tobias Brunner
3 * HSR Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 package org.strongswan.android.ui;
17
18 import android.annotation.TargetApi;
19 import android.app.Service;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.ServiceConnection;
24 import android.content.SharedPreferences;
25 import android.graphics.drawable.Icon;
26 import android.os.Build;
27 import android.os.IBinder;
28 import android.preference.PreferenceManager;
29 import android.service.quicksettings.Tile;
30 import android.service.quicksettings.TileService;
31
32 import org.strongswan.android.R;
33 import org.strongswan.android.data.VpnProfile;
34 import org.strongswan.android.data.VpnProfileDataSource;
35 import org.strongswan.android.logic.VpnStateService;
36 import org.strongswan.android.utils.Constants;
37
38 @TargetApi(Build.VERSION_CODES.N)
39 public class VpnTileService extends TileService implements VpnStateService.VpnStateListener
40 {
41 private boolean mListening;
42 private VpnProfileDataSource mDataSource;
43 private VpnStateService mService;
44 private final ServiceConnection mServiceConnection = new ServiceConnection()
45 {
46 @Override
47 public void onServiceDisconnected(ComponentName name)
48 {
49 mService = null;
50 }
51
52 @Override
53 public void onServiceConnected(ComponentName name, IBinder service)
54 {
55 mService = ((VpnStateService.LocalBinder)service).getService();
56 if (mListening)
57 {
58 mService.registerListener(VpnTileService.this);
59 updateTile();
60 }
61 }
62 };
63
64 @Override
65 public void onCreate()
66 {
67 super.onCreate();
68
69 Context context = getApplicationContext();
70 context.bindService(new Intent(context, VpnStateService.class),
71 mServiceConnection, Service.BIND_AUTO_CREATE);
72
73 mDataSource = new VpnProfileDataSource(this);
74 mDataSource.open();
75 }
76
77 @Override
78 public void onDestroy()
79 {
80 super.onDestroy();
81 if (mService != null)
82 {
83 getApplicationContext().unbindService(mServiceConnection);
84 }
85 mDataSource.close();
86 }
87
88 @Override
89 public void onStartListening()
90 {
91 super.onStartListening();
92 mListening = true;
93 if (mService != null)
94 {
95 mService.registerListener(this);
96 updateTile();
97 }
98 }
99
100 @Override
101 public void onStopListening()
102 {
103 super.onStopListening();
104 mListening = false;
105 if (mService != null)
106 {
107 mService.unregisterListener(this);
108 }
109 }
110
111 private VpnProfile getProfile()
112 {
113 SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
114 String uuid = pref.getString(Constants.PREF_DEFAULT_VPN_PROFILE, null);
115 if (uuid == null || uuid.equals(Constants.PREF_DEFAULT_VPN_PROFILE_MRU))
116 {
117 uuid = pref.getString(Constants.PREF_MRU_VPN_PROFILE, null);
118 }
119
120 return mDataSource.getVpnProfile(uuid);
121 }
122
123 @Override
124 public void onClick()
125 {
126 if (mService != null)
127 {
128 /* we operate on the current/most recently used profile, but fall back to configuration */
129 VpnProfile profile = mService.getProfile();
130 if (profile == null)
131 {
132 profile = getProfile();
133 }
134
135 /* open the main activity in case of an error. since the state is still CONNECTING
136 * there is a popup confirmation dialog if we connect again, disconnect would work
137 * but doing two operations is not ideal */
138 if (mService.getErrorState() == VpnStateService.ErrorState.NO_ERROR)
139 {
140 switch (mService.getState())
141 {
142 case CONNECTING:
143 case CONNECTED:
144 Runnable disconnect = new Runnable()
145 {
146 @Override
147 public void run()
148 {
149 mService.disconnect();
150 }
151 };
152 if (isLocked())
153 {
154 unlockAndRun(disconnect);
155 }
156 else
157 {
158 disconnect.run();
159 }
160 return;
161 }
162 if (profile != null)
163 {
164 Intent intent = new Intent(this, VpnProfileControlActivity.class);
165 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
166 intent.setAction(VpnProfileControlActivity.START_PROFILE);
167 intent.putExtra(VpnProfileControlActivity.EXTRA_VPN_PROFILE_ID, profile.getUUID().toString());
168 startActivity(intent);
169 return;
170 }
171 }
172 }
173 Intent intent = new Intent(this, MainActivity.class);
174 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
175 startActivityAndCollapse(intent);
176 }
177
178 @Override
179 public void stateChanged()
180 {
181 updateTile();
182 }
183
184 private void updateTile()
185 {
186 VpnProfile profile = mService.getProfile();
187 VpnStateService.State state = mService.getState();
188 VpnStateService.ErrorState error = mService.getErrorState();
189
190 /* same as above, only use the configured profile if we have no active profile */
191 if (profile == null)
192 {
193 profile = getProfile();
194 }
195
196 Tile tile = getQsTile();
197
198 if (error != VpnStateService.ErrorState.NO_ERROR)
199 {
200 tile.setState(Tile.STATE_INACTIVE);
201 tile.setIcon(Icon.createWithResource(this, R.drawable.ic_notification_warning));
202 tile.setLabel(getString(R.string.tile_connect));
203 }
204 else
205 {
206 switch (state)
207 {
208 case DISCONNECTING:
209 case DISABLED:
210 tile.setState(Tile.STATE_INACTIVE);
211 tile.setIcon(Icon.createWithResource(this, R.drawable.ic_notification_disconnected));
212 tile.setLabel(getString(R.string.tile_connect));
213 break;
214 case CONNECTING:
215 tile.setState(Tile.STATE_ACTIVE);
216 tile.setIcon(Icon.createWithResource(this, R.drawable.ic_notification_connecting));
217 tile.setLabel(getString(R.string.tile_disconnect));
218 break;
219 case CONNECTED:
220 tile.setState(Tile.STATE_ACTIVE);
221 tile.setIcon(Icon.createWithResource(this, R.drawable.ic_notification));
222 tile.setLabel(getString(R.string.tile_disconnect));
223 break;
224 }
225 }
226 if (profile != null && !isSecure())
227 {
228 tile.setLabel(profile.getName());
229 }
230 tile.updateTile();
231 }
232 }