android: Add Quick Settings tile to toggle VPN state
Only if there is no currently active (or previously active) profile does this currently operate on the configured (or stored most recently used) profile. This way it's possible to use a different connection and quickly disable and re-enable it again. When unlocked the profile name is shown, when locked a generic text is used (this detection doesn't seem to work 100% reliably). To disconnect, the user is forced to unlock the device, connecting is possible without, if the credentials are available and no fatal error occurs (it even works with the system credential store, at least on Android 8.1). Note that the tile is not available right after a reboot. It seems that the system has to be unlocked once to activate third-party tiles (will be interesting to see how this works together with Always-on VPN).
This commit is contained in:
parent
08c79d5112
commit
64b7a6d622
|
@ -36,6 +36,9 @@
|
|||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.VpnProfileControlActivity"
|
||||
|
@ -156,6 +159,15 @@
|
|||
<action android:name="android.net.VpnService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
<service
|
||||
android:name=".ui.VpnTileService"
|
||||
android:label="@string/tile_default"
|
||||
android:icon="@drawable/ic_notification"
|
||||
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
|
||||
<intent-filter>
|
||||
<action android:name="android.service.quicksettings.action.QS_TILE" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<provider
|
||||
android:name=".data.LogContentProvider"
|
||||
|
|
|
@ -0,0 +1,232 @@
|
|||
/*
|
||||
* Copyright (C) 2018 Tobias Brunner
|
||||
* HSR Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
package org.strongswan.android.ui;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Service;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.service.quicksettings.Tile;
|
||||
import android.service.quicksettings.TileService;
|
||||
|
||||
import org.strongswan.android.R;
|
||||
import org.strongswan.android.data.VpnProfile;
|
||||
import org.strongswan.android.data.VpnProfileDataSource;
|
||||
import org.strongswan.android.logic.VpnStateService;
|
||||
import org.strongswan.android.utils.Constants;
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.N)
|
||||
public class VpnTileService extends TileService implements VpnStateService.VpnStateListener
|
||||
{
|
||||
private boolean mListening;
|
||||
private VpnProfileDataSource mDataSource;
|
||||
private VpnStateService mService;
|
||||
private final ServiceConnection mServiceConnection = new ServiceConnection()
|
||||
{
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name)
|
||||
{
|
||||
mService = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service)
|
||||
{
|
||||
mService = ((VpnStateService.LocalBinder)service).getService();
|
||||
if (mListening)
|
||||
{
|
||||
mService.registerListener(VpnTileService.this);
|
||||
updateTile();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onCreate()
|
||||
{
|
||||
super.onCreate();
|
||||
|
||||
Context context = getApplicationContext();
|
||||
context.bindService(new Intent(context, VpnStateService.class),
|
||||
mServiceConnection, Service.BIND_AUTO_CREATE);
|
||||
|
||||
mDataSource = new VpnProfileDataSource(this);
|
||||
mDataSource.open();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy()
|
||||
{
|
||||
super.onDestroy();
|
||||
if (mService != null)
|
||||
{
|
||||
getApplicationContext().unbindService(mServiceConnection);
|
||||
}
|
||||
mDataSource.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartListening()
|
||||
{
|
||||
super.onStartListening();
|
||||
mListening = true;
|
||||
if (mService != null)
|
||||
{
|
||||
mService.registerListener(this);
|
||||
updateTile();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopListening()
|
||||
{
|
||||
super.onStopListening();
|
||||
mListening = false;
|
||||
if (mService != null)
|
||||
{
|
||||
mService.unregisterListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
private VpnProfile getProfile()
|
||||
{
|
||||
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
String uuid = pref.getString(Constants.PREF_DEFAULT_VPN_PROFILE, null);
|
||||
if (uuid == null || uuid.equals(Constants.PREF_DEFAULT_VPN_PROFILE_MRU))
|
||||
{
|
||||
uuid = pref.getString(Constants.PREF_MRU_VPN_PROFILE, null);
|
||||
}
|
||||
|
||||
return mDataSource.getVpnProfile(uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick()
|
||||
{
|
||||
if (mService != null)
|
||||
{
|
||||
/* we operate on the current/most recently used profile, but fall back to configuration */
|
||||
VpnProfile profile = mService.getProfile();
|
||||
if (profile == null)
|
||||
{
|
||||
profile = getProfile();
|
||||
}
|
||||
|
||||
/* open the main activity in case of an error. since the state is still CONNECTING
|
||||
* there is a popup confirmation dialog if we connect again, disconnect would work
|
||||
* but doing two operations is not ideal */
|
||||
if (mService.getErrorState() == VpnStateService.ErrorState.NO_ERROR)
|
||||
{
|
||||
switch (mService.getState())
|
||||
{
|
||||
case CONNECTING:
|
||||
case CONNECTED:
|
||||
Runnable disconnect = new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
mService.disconnect();
|
||||
}
|
||||
};
|
||||
if (isLocked())
|
||||
{
|
||||
unlockAndRun(disconnect);
|
||||
}
|
||||
else
|
||||
{
|
||||
disconnect.run();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (profile != null)
|
||||
{
|
||||
Intent intent = new Intent(this, VpnProfileControlActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.setAction(VpnProfileControlActivity.START_PROFILE);
|
||||
intent.putExtra(VpnProfileControlActivity.EXTRA_VPN_PROFILE_ID, profile.getUUID().toString());
|
||||
startActivity(intent);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
Intent intent = new Intent(this, MainActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
startActivityAndCollapse(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stateChanged()
|
||||
{
|
||||
updateTile();
|
||||
}
|
||||
|
||||
private void updateTile()
|
||||
{
|
||||
VpnProfile profile = mService.getProfile();
|
||||
VpnStateService.State state = mService.getState();
|
||||
VpnStateService.ErrorState error = mService.getErrorState();
|
||||
|
||||
/* same as above, only use the configured profile if we have no active profile */
|
||||
if (profile == null)
|
||||
{
|
||||
profile = getProfile();
|
||||
}
|
||||
|
||||
Tile tile = getQsTile();
|
||||
|
||||
if (error != VpnStateService.ErrorState.NO_ERROR)
|
||||
{
|
||||
tile.setState(Tile.STATE_INACTIVE);
|
||||
tile.setIcon(Icon.createWithResource(this, R.drawable.ic_notification_warning));
|
||||
tile.setLabel(getString(R.string.tile_connect));
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case DISCONNECTING:
|
||||
case DISABLED:
|
||||
tile.setState(Tile.STATE_INACTIVE);
|
||||
tile.setIcon(Icon.createWithResource(this, R.drawable.ic_notification_disconnected));
|
||||
tile.setLabel(getString(R.string.tile_connect));
|
||||
break;
|
||||
case CONNECTING:
|
||||
tile.setState(Tile.STATE_ACTIVE);
|
||||
tile.setIcon(Icon.createWithResource(this, R.drawable.ic_notification_connecting));
|
||||
tile.setLabel(getString(R.string.tile_disconnect));
|
||||
break;
|
||||
case CONNECTED:
|
||||
tile.setState(Tile.STATE_ACTIVE);
|
||||
tile.setIcon(Icon.createWithResource(this, R.drawable.ic_notification));
|
||||
tile.setLabel(getString(R.string.tile_disconnect));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (profile != null && !isSecure())
|
||||
{
|
||||
tile.setLabel(profile.getName());
|
||||
}
|
||||
tile.updateTile();
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 474 B |
Binary file not shown.
After Width: | Height: | Size: 364 B |
Binary file not shown.
After Width: | Height: | Size: 573 B |
|
@ -194,4 +194,9 @@
|
|||
<string name="disconnect_active_connection">Dies trennt die aktuelle VPN Verbindung!</string>
|
||||
<string name="connect">Verbinden</string>
|
||||
|
||||
<!-- Quick Settings tile -->
|
||||
<string name="tile_default">VPN umschalten</string>
|
||||
<string name="tile_connect">VPN verbinden</string>
|
||||
<string name="tile_disconnect">VPN trennen</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -194,4 +194,9 @@
|
|||
<string name="disconnect_active_connection">This will disconnect the active VPN connection!</string>
|
||||
<string name="connect">Połącz</string>
|
||||
|
||||
<!-- Quick Settings tile -->
|
||||
<string name="tile_default">Toggle VPN</string>
|
||||
<string name="tile_connect">Connect VPN</string>
|
||||
<string name="tile_disconnect">Disconnect VPN</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -191,4 +191,9 @@
|
|||
<string name="disconnect_active_connection">This will disconnect the active VPN connection!</string>
|
||||
<string name="connect">Соединить</string>
|
||||
|
||||
<!-- Quick Settings tile -->
|
||||
<string name="tile_default">Toggle VPN</string>
|
||||
<string name="tile_connect">Connect VPN</string>
|
||||
<string name="tile_disconnect">Disconnect VPN</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -192,4 +192,9 @@
|
|||
<string name="disconnect_active_connection">This will disconnect the active VPN connection!</string>
|
||||
<string name="connect">Підключити</string>
|
||||
|
||||
<!-- Quick Settings tile -->
|
||||
<string name="tile_default">Toggle VPN</string>
|
||||
<string name="tile_connect">Connect VPN</string>
|
||||
<string name="tile_disconnect">Disconnect VPN</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -191,4 +191,9 @@
|
|||
<string name="disconnect_active_connection">This will disconnect the active VPN connection!</string>
|
||||
<string name="connect">连接</string>
|
||||
|
||||
<!-- Quick Settings tile -->
|
||||
<string name="tile_default">Toggle VPN</string>
|
||||
<string name="tile_connect">Connect VPN</string>
|
||||
<string name="tile_disconnect">Disconnect VPN</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -191,4 +191,9 @@
|
|||
<string name="disconnect_active_connection">This will disconnect the active VPN connection!</string>
|
||||
<string name="connect">連線</string>
|
||||
|
||||
<!-- Quick Settings tile -->
|
||||
<string name="tile_default">Toggle VPN</string>
|
||||
<string name="tile_connect">Connect VPN</string>
|
||||
<string name="tile_disconnect">Disconnect VPN</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -194,4 +194,9 @@
|
|||
<string name="disconnect_active_connection">This will disconnect the active VPN connection!</string>
|
||||
<string name="connect">Connect</string>
|
||||
|
||||
<!-- Quick Settings tile -->
|
||||
<string name="tile_default">Toggle VPN</string>
|
||||
<string name="tile_connect">Connect VPN</string>
|
||||
<string name="tile_disconnect">Disconnect VPN</string>
|
||||
|
||||
</resources>
|
||||
|
|
Loading…
Reference in New Issue