From 631d321206b0b0baa38e6efddfe03d52f7756731 Mon Sep 17 00:00:00 2001 From: Revertron Date: Fri, 18 Nov 2022 14:11:43 +0100 Subject: [PATCH] Added saving of enabled state, added some fixes and refactorings. --- app/src/main/AndroidManifest.xml | 1 + .../eu/neilalexander/yggdrasil/DnsActivity.kt | 3 +- .../yggdrasil/GlobalApplication.kt | 54 ++++++++++++++++++- .../neilalexander/yggdrasil/MainActivity.kt | 6 ++- .../yggdrasil/NetworkStateCallback.kt | 49 ++++++++++------- .../yggdrasil/PacketTunnelProvider.kt | 26 ++++++--- .../neilalexander/yggdrasil/YggTileService.kt | 18 ++++--- app/src/main/res/values-ru/strings.xml | 8 +-- app/src/main/res/values/strings.xml | 8 +-- 9 files changed, 133 insertions(+), 40 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5a2f858..9cf3229 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,6 +4,7 @@ + = Build.VERSION_CODES.O) { + val name = context.getString(R.string.channel_name) + val descriptionText = context.getString(R.string.channel_description) + val importance = NotificationManager.IMPORTANCE_MIN + val channel = NotificationChannel(channelId, name, importance).apply { + description = descriptionText + } + // Register the channel with the system + val notificationManager: NotificationManager = + context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + notificationManager.createNotificationChannel(channel) + } + + val intent = Intent(context, MainActivity::class.java).apply { + this.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + } + val pendingIntent: PendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT) + + val text = when (state) { + State.Disabled -> context.getText(R.string.tile_disabled) + State.Enabled -> context.getText(R.string.tile_enabled) + State.Connected -> context.getText(R.string.tile_connected) + else -> context.getText(R.string.tile_disabled) + } + + return NotificationCompat.Builder(context, channelId) + .setContentTitle(context.getText(R.string.app_name)) + .setContentText(text) + .setSmallIcon(R.drawable.ic_tile_icon) + .setContentIntent(pendingIntent) + .setPriority(NotificationCompat.PRIORITY_MIN) + .build() } \ No newline at end of file diff --git a/app/src/main/java/eu/neilalexander/yggdrasil/MainActivity.kt b/app/src/main/java/eu/neilalexander/yggdrasil/MainActivity.kt index 613d254..0d849d4 100644 --- a/app/src/main/java/eu/neilalexander/yggdrasil/MainActivity.kt +++ b/app/src/main/java/eu/neilalexander/yggdrasil/MainActivity.kt @@ -11,7 +11,9 @@ import android.widget.TextView import android.widget.Toast import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.edit import androidx.localbroadcastmanager.content.LocalBroadcastManager +import androidx.preference.PreferenceManager import eu.neilalexander.yggdrasil.PacketTunnelProvider.Companion.STATE_INTENT import mobile.Mobile import org.json.JSONArray @@ -76,6 +78,8 @@ class MainActivity : AppCompatActivity() { startService(intent) } } + val preferences = PreferenceManager.getDefaultSharedPreferences(this.baseContext) + preferences.edit(commit = true) { putBoolean(PREF_KEY_ENABLED, isChecked) } } val enableYggdrasilPanel = findViewById(R.id.enableYggdrasilPanel) @@ -123,7 +127,7 @@ class MainActivity : AppCompatActivity() { LocalBroadcastManager.getInstance(this).registerReceiver( receiver, IntentFilter(STATE_INTENT) ) - val preferences = androidx.preference.PreferenceManager.getDefaultSharedPreferences(this.baseContext) + val preferences = PreferenceManager.getDefaultSharedPreferences(this.baseContext) val serverString = preferences.getString(KEY_DNS_SERVERS, "") if (serverString!!.isNotEmpty()) { val servers = serverString.split(",") diff --git a/app/src/main/java/eu/neilalexander/yggdrasil/NetworkStateCallback.kt b/app/src/main/java/eu/neilalexander/yggdrasil/NetworkStateCallback.kt index 404fd23..7775308 100644 --- a/app/src/main/java/eu/neilalexander/yggdrasil/NetworkStateCallback.kt +++ b/app/src/main/java/eu/neilalexander/yggdrasil/NetworkStateCallback.kt @@ -3,14 +3,43 @@ package eu.neilalexander.yggdrasil import android.content.Context import android.content.Intent import android.net.* +import android.os.Build import android.util.Log +import androidx.preference.PreferenceManager private const val TAG = "Network" class NetworkStateCallback(val context: Context) : ConnectivityManager.NetworkCallback() { - init { + override fun onAvailable(network: Network) { + super.onAvailable(network) + Log.d(TAG, "onAvailable") + + val preferences = PreferenceManager.getDefaultSharedPreferences(context) + if (preferences.getBoolean(PREF_KEY_ENABLED, false)) { + Thread { + // The message often arrives before the connection is fully established + Thread.sleep(1000) + val intent = Intent(context, PacketTunnelProvider::class.java) + intent.action = PacketTunnelProvider.ACTION_CONNECT + try { + context.startService(intent) + } catch (e: IllegalStateException) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + context.startForegroundService(intent) + } + } + }.start() + } + } + + override fun onLost(network: Network) { + super.onLost(network) + Log.d(TAG, "onLost") + } + + fun register() { val request = NetworkRequest.Builder() .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) @@ -20,22 +49,4 @@ class NetworkStateCallback(val context: Context) : ConnectivityManager.NetworkCa val manager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager manager.registerNetworkCallback(request, this) } - - override fun onAvailable(network: Network) { - super.onAvailable(network) - Log.d(TAG, "onAvailable") - - Thread { - // The message often arrives before the connection is fully established - Thread.sleep(1000) - val intent = Intent(context, PacketTunnelProvider::class.java) - intent.action = PacketTunnelProvider.ACTION_CONNECT - context.startService(intent) - }.start() - } - - override fun onLost(network: Network) { - super.onLost(network) - Log.d(TAG, "onLost") - } } \ No newline at end of file diff --git a/app/src/main/java/eu/neilalexander/yggdrasil/PacketTunnelProvider.kt b/app/src/main/java/eu/neilalexander/yggdrasil/PacketTunnelProvider.kt index e58f131..443dff6 100644 --- a/app/src/main/java/eu/neilalexander/yggdrasil/PacketTunnelProvider.kt +++ b/app/src/main/java/eu/neilalexander/yggdrasil/PacketTunnelProvider.kt @@ -2,10 +2,12 @@ package eu.neilalexander.yggdrasil import android.content.* import android.net.VpnService +import android.os.Build import android.os.ParcelFileDescriptor import android.system.OsConstants import android.util.Log import androidx.localbroadcastmanager.content.LocalBroadcastManager +import androidx.preference.PreferenceManager import eu.neilalexander.yggdrasil.YggStateReceiver.Companion.YGG_STATE_INTENT import mobile.Yggdrasil import org.json.JSONArray @@ -16,6 +18,7 @@ import kotlin.concurrent.thread private const val TAG = "PacketTunnelProvider" +const val SERVICE_NOTIFICATION_ID = 1000 class PacketTunnelProvider: VpnService() { companion object { @@ -55,6 +58,11 @@ class PacketTunnelProvider: VpnService() { Log.d(TAG, "Intent is null") return START_NOT_STICKY } + val preferences = PreferenceManager.getDefaultSharedPreferences(this.baseContext) + if (!preferences.getBoolean(PREF_KEY_ENABLED, false)) { + Log.d(TAG, "Service is disabled") + return START_NOT_STICKY + } return when (intent.action ?: ACTION_STOP) { ACTION_STOP -> { Log.d(TAG, "Stopping...") @@ -89,6 +97,9 @@ class PacketTunnelProvider: VpnService() { return } + val notification = createServiceNotification(this, State.Enabled) + startForeground(SERVICE_NOTIFICATION_ID, notification) + Log.d(TAG, config.getJSON().toString()) yggdrasil.startJSON(config.getJSONByteArray()) @@ -112,11 +123,11 @@ class PacketTunnelProvider: VpnService() { // If we don't set metered status of VPN it is considered as metered. // If we set it to false, then it will inherit this status from underlying network. // See: https://developer.android.com/reference/android/net/VpnService.Builder#setMetered(boolean) - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { builder.setMetered(false) } - val preferences = androidx.preference.PreferenceManager.getDefaultSharedPreferences(this.baseContext) + val preferences = PreferenceManager.getDefaultSharedPreferences(this.baseContext) val serverString = preferences.getString(KEY_DNS_SERVERS, "") if (serverString!!.isNotEmpty()) { val servers = serverString.split(",") @@ -207,6 +218,7 @@ class PacketTunnelProvider: VpnService() { intent.putExtra("state", STATE_DISABLED) LocalBroadcastManager.getInstance(this).sendBroadcast(intent) + stopForeground(true) stopSelf() } @@ -236,10 +248,12 @@ class PacketTunnelProvider: VpnService() { val intent = Intent(YGG_STATE_INTENT) var state = STATE_ENABLED val dht = yggdrasil.dhtjson - val dhtState = JSONArray(dht) - val count = dhtState.length() - if (count > 1) - state = STATE_CONNECTED + if (dht != null && dht != "null") { + val dhtState = JSONArray(dht) + val count = dhtState.length() + if (count > 1) + state = STATE_CONNECTED + } intent.putExtra("state", state) LocalBroadcastManager.getInstance(this).sendBroadcast(intent) lastStateUpdate = curTime diff --git a/app/src/main/java/eu/neilalexander/yggdrasil/YggTileService.kt b/app/src/main/java/eu/neilalexander/yggdrasil/YggTileService.kt index e997ff9..4725bd4 100644 --- a/app/src/main/java/eu/neilalexander/yggdrasil/YggTileService.kt +++ b/app/src/main/java/eu/neilalexander/yggdrasil/YggTileService.kt @@ -8,6 +8,8 @@ import android.service.quicksettings.Tile import android.service.quicksettings.TileService import android.util.Log import androidx.annotation.RequiresApi +import androidx.core.content.edit +import androidx.preference.PreferenceManager private const val TAG = "TileService" @@ -63,6 +65,11 @@ class YggTileService: TileService(), YggStateReceiver.StateReceiver { override fun onClick() { super.onClick() + // Saving new state + val preferences = PreferenceManager.getDefaultSharedPreferences(this.baseContext) + val enabled = preferences.getBoolean(PREF_KEY_ENABLED, false) + preferences.edit(commit = true) { putBoolean(PREF_KEY_ENABLED, !enabled) } + // Starting or stopping VPN service val intent = Intent(this, PacketTunnelProvider::class.java) intent.action = PacketTunnelProvider.ACTION_TOGGLE startService(intent) @@ -71,12 +78,11 @@ class YggTileService: TileService(), YggStateReceiver.StateReceiver { private fun updateTileState(state: State) { val tile = qsTile ?: return val oldState = tile.state - tile.state = when (state) { - State.Unknown -> Tile.STATE_UNAVAILABLE - State.Disabled -> Tile.STATE_INACTIVE - State.Enabled -> Tile.STATE_ACTIVE - State.Connected -> Tile.STATE_ACTIVE - State.Reconnecting -> Tile.STATE_ACTIVE + val preferences = PreferenceManager.getDefaultSharedPreferences(this.baseContext) + val enabled = preferences.getBoolean(PREF_KEY_ENABLED, false) + tile.state = when (enabled) { + false -> Tile.STATE_INACTIVE + true -> Tile.STATE_ACTIVE } var changed = oldState != tile.state if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 546e80d..1d0dc23 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -22,8 +22,8 @@ 1 сервер %d сервера/серверов Убрать %s? - Нет подключения - Включено + Включено (Нет подключения) + Подключено Выключено Нет пиров 1 пир @@ -66,10 +66,12 @@ Сбросить настройки Сброс создаст полностью новые настройки. Это изменит ваш публичный ключ и адрес IP. Выключено - Включено + Включено (Нет подключения) Подключено Амстердам, Нидерланды Прага, Чехия Братислава, Словакия Баффало, США + Сервис VPN + Главный канал нотификаций сервиса \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index db791e0..20696bd 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -22,8 +22,8 @@ 1 server %d server Remove %s? - No connectivity - Enabled + Enabled (No connectivity) + Connected Not enabled No peers 1 peer @@ -66,10 +66,12 @@ Reset configuration Resetting will overwrite with newly generated configuration. Your public keys and IP address on the network will change. Disabled - Enabled + Enabled (No connectivity) Connected Amsterdam, NL Prague, CZ Bratislava, SK Buffalo, US + VPN Service + Main channel for foreground notification \ No newline at end of file