diff --git a/src/frontends/android/app/src/main/AndroidManifest.xml b/src/frontends/android/app/src/main/AndroidManifest.xml index 5d827a34d..6b0db3782 100644 --- a/src/frontends/android/app/src/main/AndroidManifest.xml +++ b/src/frontends/android/app/src/main/AndroidManifest.xml @@ -36,6 +36,9 @@ + + + + + + + + . + * + * 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(); + } +} diff --git a/src/frontends/android/app/src/main/res/drawable-hdpi/ic_notification_disconnected.png b/src/frontends/android/app/src/main/res/drawable-hdpi/ic_notification_disconnected.png new file mode 100755 index 000000000..039877b01 Binary files /dev/null and b/src/frontends/android/app/src/main/res/drawable-hdpi/ic_notification_disconnected.png differ diff --git a/src/frontends/android/app/src/main/res/drawable-mdpi/ic_notification_disconnected.png b/src/frontends/android/app/src/main/res/drawable-mdpi/ic_notification_disconnected.png new file mode 100755 index 000000000..1adedbe10 Binary files /dev/null and b/src/frontends/android/app/src/main/res/drawable-mdpi/ic_notification_disconnected.png differ diff --git a/src/frontends/android/app/src/main/res/drawable-xhdpi/ic_notification_disconnected.png b/src/frontends/android/app/src/main/res/drawable-xhdpi/ic_notification_disconnected.png new file mode 100755 index 000000000..e5ddb244e Binary files /dev/null and b/src/frontends/android/app/src/main/res/drawable-xhdpi/ic_notification_disconnected.png differ diff --git a/src/frontends/android/app/src/main/res/values-de/strings.xml b/src/frontends/android/app/src/main/res/values-de/strings.xml index 97df885f8..564a6cc2e 100644 --- a/src/frontends/android/app/src/main/res/values-de/strings.xml +++ b/src/frontends/android/app/src/main/res/values-de/strings.xml @@ -194,4 +194,9 @@ Dies trennt die aktuelle VPN Verbindung! Verbinden + + VPN umschalten + VPN verbinden + VPN trennen + diff --git a/src/frontends/android/app/src/main/res/values-pl/strings.xml b/src/frontends/android/app/src/main/res/values-pl/strings.xml index e9c52f14a..8441c0a7a 100644 --- a/src/frontends/android/app/src/main/res/values-pl/strings.xml +++ b/src/frontends/android/app/src/main/res/values-pl/strings.xml @@ -194,4 +194,9 @@ This will disconnect the active VPN connection! Połącz + + Toggle VPN + Connect VPN + Disconnect VPN + diff --git a/src/frontends/android/app/src/main/res/values-ru/strings.xml b/src/frontends/android/app/src/main/res/values-ru/strings.xml index 5f16eecc8..20259ef88 100644 --- a/src/frontends/android/app/src/main/res/values-ru/strings.xml +++ b/src/frontends/android/app/src/main/res/values-ru/strings.xml @@ -191,4 +191,9 @@ This will disconnect the active VPN connection! Соединить + + Toggle VPN + Connect VPN + Disconnect VPN + diff --git a/src/frontends/android/app/src/main/res/values-ua/strings.xml b/src/frontends/android/app/src/main/res/values-ua/strings.xml index 53bcf7164..1fc1c2926 100644 --- a/src/frontends/android/app/src/main/res/values-ua/strings.xml +++ b/src/frontends/android/app/src/main/res/values-ua/strings.xml @@ -192,4 +192,9 @@ This will disconnect the active VPN connection! Підключити + + Toggle VPN + Connect VPN + Disconnect VPN + diff --git a/src/frontends/android/app/src/main/res/values-zh-rCN/strings.xml b/src/frontends/android/app/src/main/res/values-zh-rCN/strings.xml index 72098cc47..8a886c5d8 100644 --- a/src/frontends/android/app/src/main/res/values-zh-rCN/strings.xml +++ b/src/frontends/android/app/src/main/res/values-zh-rCN/strings.xml @@ -191,4 +191,9 @@ This will disconnect the active VPN connection! 连接 + + Toggle VPN + Connect VPN + Disconnect VPN + diff --git a/src/frontends/android/app/src/main/res/values-zh-rTW/strings.xml b/src/frontends/android/app/src/main/res/values-zh-rTW/strings.xml index ba9fdd044..f2529a519 100644 --- a/src/frontends/android/app/src/main/res/values-zh-rTW/strings.xml +++ b/src/frontends/android/app/src/main/res/values-zh-rTW/strings.xml @@ -191,4 +191,9 @@ This will disconnect the active VPN connection! 連線 + + Toggle VPN + Connect VPN + Disconnect VPN + diff --git a/src/frontends/android/app/src/main/res/values/strings.xml b/src/frontends/android/app/src/main/res/values/strings.xml index cb507beae..584a2d9a2 100644 --- a/src/frontends/android/app/src/main/res/values/strings.xml +++ b/src/frontends/android/app/src/main/res/values/strings.xml @@ -194,4 +194,9 @@ This will disconnect the active VPN connection! Connect + + Toggle VPN + Connect VPN + Disconnect VPN +