From 34d71a609605a00af4051b6d2600f3ef66845052 Mon Sep 17 00:00:00 2001 From: Zane Schepke Date: Tue, 5 Dec 2023 01:14:19 -0500 Subject: [PATCH] feat: mobile data only auto-tunneling Added support for configuring auto-tunneling to only tunnel on mobile data with no location permissions necessary. Improved UI on support screen and updated support resource links. Fixed UI bug where analytics expansion could show on deactivated tunnels. --- .github/workflows/android.yml | 2 +- .../3.json | 133 +++++++++++++ .../wireguardautotunnel/Constants.kt | 1 + .../repository/AppDatabase.kt | 4 +- .../repository/model/Settings.kt | 1 + .../WireGuardConnectivityWatcherService.kt | 6 + .../ui/screens/main/MainScreen.kt | 4 +- .../ui/screens/settings/SettingsScreen.kt | 177 ++++++++++-------- .../ui/screens/settings/SettingsViewModel.kt | 6 + .../ui/screens/support/SupportScreen.kt | 104 ++++++++-- app/src/main/res/values/strings.xml | 29 ++- buildSrc/src/main/kotlin/Constants.kt | 4 +- .../android/en-US/changelogs/32300.txt | 5 + gradle/libs.versions.toml | 8 +- 14 files changed, 374 insertions(+), 110 deletions(-) create mode 100644 app/schemas/com.zaneschepke.wireguardautotunnel.repository.AppDatabase/3.json create mode 100644 fastlane/metadata/android/en-US/changelogs/32300.txt diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 25d575dd..a42d7337 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -70,7 +70,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: # fix hardcode changelog file name - body_path: ${{ github.workspace }}/fastlane/metadata/android/en-US/changelogs/32200.txt + body_path: ${{ github.workspace }}/fastlane/metadata/android/en-US/changelogs/32300.txt tag_name: ${{ github.ref_name }} name: Release ${{ github.ref_name }} draft: false diff --git a/app/schemas/com.zaneschepke.wireguardautotunnel.repository.AppDatabase/3.json b/app/schemas/com.zaneschepke.wireguardautotunnel.repository.AppDatabase/3.json new file mode 100644 index 00000000..d5d93a3f --- /dev/null +++ b/app/schemas/com.zaneschepke.wireguardautotunnel.repository.AppDatabase/3.json @@ -0,0 +1,133 @@ +{ + "formatVersion": 1, + "database": { + "version": 3, + "identityHash": "6b30daba29bb95f8ddc4d26206329d4f", + "entities": [ + { + "tableName": "Settings", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_tunnel_enabled` INTEGER NOT NULL, `is_tunnel_on_mobile_data_enabled` INTEGER NOT NULL, `trusted_network_ssids` TEXT NOT NULL, `default_tunnel` TEXT, `is_always_on_vpn_enabled` INTEGER NOT NULL, `is_tunnel_on_ethernet_enabled` INTEGER NOT NULL, `is_shortcuts_enabled` INTEGER NOT NULL DEFAULT false, `is_battery_saver_enabled` INTEGER NOT NULL DEFAULT false, `is_tunnel_on_wifi_enabled` INTEGER NOT NULL DEFAULT false)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isAutoTunnelEnabled", + "columnName": "is_tunnel_enabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isTunnelOnMobileDataEnabled", + "columnName": "is_tunnel_on_mobile_data_enabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "trustedNetworkSSIDs", + "columnName": "trusted_network_ssids", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "defaultTunnel", + "columnName": "default_tunnel", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isAlwaysOnVpnEnabled", + "columnName": "is_always_on_vpn_enabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isTunnelOnEthernetEnabled", + "columnName": "is_tunnel_on_ethernet_enabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isShortcutsEnabled", + "columnName": "is_shortcuts_enabled", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "false" + }, + { + "fieldPath": "isBatterySaverEnabled", + "columnName": "is_battery_saver_enabled", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "false" + }, + { + "fieldPath": "isTunnelOnWifiEnabled", + "columnName": "is_tunnel_on_wifi_enabled", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "false" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TunnelConfig", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `wg_quick` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "wgQuick", + "columnName": "wg_quick", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_TunnelConfig_name", + "unique": true, + "columnNames": [ + "name" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_TunnelConfig_name` ON `${TABLE_NAME}` (`name`)" + } + ], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '6b30daba29bb95f8ddc4d26206329d4f')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/Constants.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/Constants.kt index 39cd44f6..f7305ef1 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/Constants.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/Constants.kt @@ -16,4 +16,5 @@ object Constants { const val ALLOWED_FILE_TYPES = "*/*" const val GOOGLE_TV_EXPLORER_STUB = "com.google.android.tv.frameworkpackagestubs" const val ANDROID_TV_EXPLORER_STUB = "com.android.tv.frameworkpackagestubs" + const val EMAIL_MIME_TYPE = "message/rfc822" } \ No newline at end of file diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/repository/AppDatabase.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/repository/AppDatabase.kt index ebfff9fa..e56968fc 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/repository/AppDatabase.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/repository/AppDatabase.kt @@ -7,8 +7,8 @@ import androidx.room.TypeConverters import com.zaneschepke.wireguardautotunnel.repository.model.Settings import com.zaneschepke.wireguardautotunnel.repository.model.TunnelConfig -@Database(entities = [Settings::class, TunnelConfig::class], version = 2, autoMigrations = [ - AutoMigration(from = 1, to = 2) +@Database(entities = [Settings::class, TunnelConfig::class], version = 3, autoMigrations = [ + AutoMigration(from = 1, to = 2), AutoMigration(from = 2, to = 3) ], exportSchema = true) @TypeConverters(DatabaseListConverters::class) abstract class AppDatabase : RoomDatabase() { diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/repository/model/Settings.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/repository/model/Settings.kt index 68d6aab6..77bcb3f0 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/repository/model/Settings.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/repository/model/Settings.kt @@ -15,6 +15,7 @@ data class Settings( @ColumnInfo(name = "is_tunnel_on_ethernet_enabled") var isTunnelOnEthernetEnabled : Boolean = false, @ColumnInfo(name = "is_shortcuts_enabled", defaultValue = "false") var isShortcutsEnabled : Boolean = false, @ColumnInfo(name = "is_battery_saver_enabled", defaultValue = "false") var isBatterySaverEnabled : Boolean = false, + @ColumnInfo(name = "is_tunnel_on_wifi_enabled", defaultValue = "false") var isTunnelOnWifiEnabled : Boolean = false, ) { fun isTunnelConfigDefault(tunnelConfig: TunnelConfig) : Boolean { return if (defaultTunnel != null) { diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/WireGuardConnectivityWatcherService.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/WireGuardConnectivityWatcherService.kt index dcb204e2..73af37d5 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/WireGuardConnectivityWatcherService.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/WireGuardConnectivityWatcherService.kt @@ -261,6 +261,7 @@ class WireGuardConnectivityWatcherService : ForegroundService() { ServiceManager.stopVpnService(this) } else if (!isEthernetConnected && isWifiConnected && !setting.trustedNetworkSSIDs.contains(currentNetworkSSID) && + setting.isTunnelOnWifiEnabled && (vpnService.getState() != Tunnel.State.UP) ) { ServiceManager.startVpnService(this, tunnelConfig) @@ -269,6 +270,11 @@ class WireGuardConnectivityWatcherService : ForegroundService() { (vpnService.getState() == Tunnel.State.UP) ) { ServiceManager.stopVpnService(this) + } else if (!isEthernetConnected && (isWifiConnected && + !setting.isTunnelOnWifiEnabled && + (vpnService.getState() == Tunnel.State.UP) + )) { + ServiceManager.stopVpnService(this) } delay(Constants.VPN_CONNECTIVITY_CHECK_INTERVAL) } diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/MainScreen.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/MainScreen.kt index de8f8026..e66e8430 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/MainScreen.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/MainScreen.kt @@ -440,10 +440,12 @@ fun MainScreen( } } } else { + val checked = state == Tunnel.State.UP && tunnel.name == tunnelName + if(!checked) expanded.value = false @Composable fun TunnelSwitch() = Switch( modifier = Modifier.focusRequester(focusRequester), - checked = (state == Tunnel.State.UP && tunnel.name == tunnelName), + checked = checked, onCheckedChange = { checked -> if(!checked) expanded.value = false onTunnelToggle(checked, tunnel) diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/SettingsScreen.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/SettingsScreen.kt index 6044634b..de886508 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/SettingsScreen.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/SettingsScreen.kt @@ -5,6 +5,7 @@ import android.content.Intent import android.net.Uri import android.os.Build import android.provider.Settings +import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement @@ -104,6 +105,8 @@ fun SettingsScreen( var showAuthPrompt by remember { mutableStateOf(false) } var didExportFiles by remember { mutableStateOf(false) } + + val screenPadding = 5.dp val fillMaxWidth = .85f @@ -276,8 +279,11 @@ fun SettingsScreen( modifier = (if (WireGuardAutoTunnel.isRunningOnAndroidTv(context)) Modifier .height(IntrinsicSize.Min) - .fillMaxWidth(fillMaxWidth).padding(top = 10.dp) - else Modifier.fillMaxWidth(fillMaxWidth).padding(top = 60.dp)).padding(bottom = 25.dp) + .fillMaxWidth(fillMaxWidth) + .padding(top = 10.dp) + else Modifier + .fillMaxWidth(fillMaxWidth) + .padding(top = 60.dp)).padding(bottom = 25.dp) ) { Column( horizontalAlignment = Alignment.Start, @@ -285,66 +291,77 @@ fun SettingsScreen( modifier = Modifier.padding(15.dp) ) { SectionTitle(title = stringResource(id = R.string.auto_tunneling), padding = screenPadding) - Text( - stringResource(R.string.trusted_ssid), - textAlign = TextAlign.Center, - modifier = Modifier.padding(screenPadding, bottom = 5.dp, top = 5.dp) + ConfigurationToggle( + stringResource(id = R.string.tunnel_on_wifi), + enabled = !(settings.isAutoTunnelEnabled || settings.isAlwaysOnVpnEnabled), + checked = settings.isTunnelOnWifiEnabled, + padding = screenPadding, + onCheckChanged = { + scope.launch { + viewModel.onToggleTunnelOnWifi() + } + }, + modifier = Modifier.focusRequester(focusRequester) ) - val focus = Modifier.focusRequester(focusRequester) - FlowRow( - modifier = (if(trustedSSIDs.isEmpty()) Modifier else - focus).padding(screenPadding), - horizontalArrangement = Arrangement.spacedBy(8.dp), - verticalArrangement = Arrangement.SpaceEvenly - ) { - trustedSSIDs.forEach { ssid -> - ClickableIconButton( - onIconClick = { - scope.launch { - viewModel.onDeleteTrustedSSID(ssid) + AnimatedVisibility(visible = settings.isTunnelOnWifiEnabled) { + Column { + FlowRow( + modifier = Modifier.padding(screenPadding), + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalArrangement = Arrangement.SpaceEvenly + ) { + trustedSSIDs.forEach { ssid -> + ClickableIconButton( + onIconClick = { + scope.launch { + viewModel.onDeleteTrustedSSID(ssid) + } + }, + text = ssid, + icon = Icons.Filled.Close, + enabled = !(settings.isAutoTunnelEnabled || settings.isAlwaysOnVpnEnabled) + ) + } + if(trustedSSIDs.isEmpty()) { + Text(stringResource(R.string.none), fontStyle = FontStyle.Italic, color = Color.Gray) + } + } + OutlinedTextField( + enabled = !(settings.isAutoTunnelEnabled || settings.isAlwaysOnVpnEnabled), + value = currentText, + onValueChange = { currentText = it }, + label = { Text(stringResource(R.string.add_trusted_ssid)) }, + modifier = Modifier + .padding(start = screenPadding, top = 5.dp, bottom = 10.dp) + .onFocusChanged { + if (WireGuardAutoTunnel.isRunningOnAndroidTv(context)) { + keyboardController?.hide() + } + }, + maxLines = 1, + keyboardOptions = KeyboardOptions( + capitalization = KeyboardCapitalization.None, + imeAction = ImeAction.Done + ), + keyboardActions = KeyboardActions( + onDone = { + saveTrustedSSID() + } + ), + trailingIcon = { + IconButton(onClick = { saveTrustedSSID() }) { + Icon( + imageVector = Icons.Outlined.Add, + contentDescription = if (currentText == "") stringResource(id = R.string.trusted_ssid_empty_description) else stringResource( + id = R.string.trusted_ssid_value_description + ), + tint = if (currentText == "") Color.Transparent else MaterialTheme.colorScheme.primary + ) } }, - text = ssid, - icon = Icons.Filled.Close, - enabled = !(settings.isAutoTunnelEnabled || settings.isAlwaysOnVpnEnabled) ) } - if(trustedSSIDs.isEmpty()) { - Text(stringResource(R.string.none), fontStyle = FontStyle.Italic, color = Color.Gray) - } } - OutlinedTextField( - enabled = !(settings.isAutoTunnelEnabled || settings.isAlwaysOnVpnEnabled), - value = currentText, - onValueChange = { currentText = it }, - label = { Text(stringResource(R.string.add_trusted_ssid)) }, - modifier = (if(trustedSSIDs.isEmpty()) focus else Modifier).padding(start = screenPadding, top = 5.dp).onFocusChanged { - if(WireGuardAutoTunnel.isRunningOnAndroidTv(context)) { - keyboardController?.hide() - } - }, - maxLines = 1, - keyboardOptions = KeyboardOptions( - capitalization = KeyboardCapitalization.None, - imeAction = ImeAction.Done - ), - keyboardActions = KeyboardActions( - onDone = { - saveTrustedSSID() - } - ), - trailingIcon = { - IconButton(onClick = { saveTrustedSSID() }) { - Icon( - imageVector = Icons.Outlined.Add, - contentDescription = if (currentText == "") stringResource(id = R.string.trusted_ssid_empty_description) else stringResource( - id = R.string.trusted_ssid_value_description - ), - tint = if (currentText == "") Color.Transparent else MaterialTheme.colorScheme.primary - ) - } - }, - ) ConfigurationToggle(stringResource(R.string.tunnel_mobile_data), enabled = !(settings.isAutoTunnelEnabled || settings.isAlwaysOnVpnEnabled), checked = settings.isTunnelOnMobileDataEnabled, @@ -376,27 +393,36 @@ fun SettingsScreen( } } ) - ConfigurationToggle(stringResource(R.string.enable_auto_tunnel), - enabled = !settings.isAlwaysOnVpnEnabled, - checked = settings.isAutoTunnelEnabled, - padding = screenPadding, - onCheckChanged = { - if(!isAllAutoTunnelPermissionsEnabled()) { - val message = if(viewModel.isLocationServicesNeeded()){ - context.getString(R.string.location_services_required) - } else if(!isBackgroundLocationGranted){ - context.getString(R.string.background_location_required) - } else { - context.getString(R.string.precise_location_required) + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxSize() + .padding(top = 5.dp), + horizontalArrangement = Arrangement.Center + ) { + TextButton( + enabled = !settings.isAlwaysOnVpnEnabled, + onClick = { + //TODO fix logic for mobile only + if(!isAllAutoTunnelPermissionsEnabled() && settings.isTunnelOnWifiEnabled) { + val message = if(!isBackgroundLocationGranted) { + context.getString(R.string.background_location_required) + } else if(viewModel.isLocationServicesNeeded()) { + context.getString(R.string.location_services_required) + } else { + context.getString(R.string.precise_location_required) + } + showSnackbarMessage(message) + } else scope.launch { + viewModel.toggleAutoTunnel() } - showSnackbarMessage(message) - } else scope.launch { - viewModel.toggleAutoTunnel() - } + }) { + val autoTunnelButtonText = if(settings.isAutoTunnelEnabled) stringResource(R.string.disable_auto_tunnel) + else stringResource(id = R.string.enable_auto_tunnel) + Text(autoTunnelButtonText) } - ) + } } - } if(!WireGuardAutoTunnel.isRunningOnAndroidTv(context)) { Surface( @@ -404,7 +430,8 @@ fun SettingsScreen( shadowElevation = 2.dp, shape = RoundedCornerShape(12.dp), color = MaterialTheme.colorScheme.surface, - modifier = Modifier.fillMaxWidth(fillMaxWidth) + modifier = Modifier + .fillMaxWidth(fillMaxWidth) .height(IntrinsicSize.Min) .padding(bottom = 180.dp) ) { diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/SettingsViewModel.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/SettingsViewModel.kt index a72dd95d..f7c46726 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/SettingsViewModel.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/SettingsViewModel.kt @@ -137,4 +137,10 @@ class SettingsViewModel @Inject constructor(private val application : Applicatio isBatterySaverEnabled = !_settings.value.isBatterySaverEnabled )) } + + suspend fun onToggleTunnelOnWifi() { + settingsRepo.save(_settings.value.copy( + isTunnelOnWifiEnabled = !_settings.value.isTunnelOnWifiEnabled + )) + } } \ No newline at end of file diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/support/SupportScreen.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/support/SupportScreen.kt index 59d53c49..58a6eed5 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/support/SupportScreen.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/support/SupportScreen.kt @@ -1,22 +1,34 @@ package com.zaneschepke.wireguardautotunnel.ui.screens.support import android.content.Intent +import android.content.Intent.createChooser import android.net.Uri import androidx.compose.foundation.clickable import androidx.compose.foundation.focusable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.IntrinsicSize import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.ArrowForward +import androidx.compose.material.icons.rounded.Book +import androidx.compose.material.icons.rounded.Mail +import androidx.compose.material3.Divider import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -31,18 +43,32 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.core.content.ContextCompat.startActivity +import com.zaneschepke.wireguardautotunnel.BuildConfig +import com.zaneschepke.wireguardautotunnel.Constants import com.zaneschepke.wireguardautotunnel.R +import com.zaneschepke.wireguardautotunnel.WireGuardAutoTunnel @Composable fun SupportScreen(padding : PaddingValues, focusRequester: FocusRequester) { val context = LocalContext.current + val fillMaxWidth = .85f fun openWebPage(url: String) { val webpage: Uri = Uri.parse(url) val intent = Intent(Intent.ACTION_VIEW, webpage) context.startActivity(intent) } + + fun launchEmail() { + val intent = Intent(Intent.ACTION_SEND).apply { + type = Constants.EMAIL_MIME_TYPE + putExtra(Intent.EXTRA_EMAIL, context.getString(R.string.my_email)) + putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.email_subject)) + } + startActivity(context,createChooser(intent, context.getString(R.string.email_chooser)),null) + } Column(horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Top, @@ -51,23 +77,65 @@ fun SupportScreen(padding : PaddingValues, focusRequester: FocusRequester) { .verticalScroll(rememberScrollState()) .focusable() .padding(padding)) { - Text(stringResource(R.string.support_text), textAlign = TextAlign.Center, modifier = Modifier.padding(30.dp), fontSize = 15.sp) - Row( - modifier = Modifier - .fillMaxWidth() - .padding(14.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceEvenly + Surface( + tonalElevation = 2.dp, + shadowElevation = 2.dp, + shape = RoundedCornerShape(12.dp), + color = MaterialTheme.colorScheme.surface, + modifier = (if (WireGuardAutoTunnel.isRunningOnAndroidTv(context)) + Modifier + .height(IntrinsicSize.Min) + .fillMaxWidth(fillMaxWidth) + .padding(top = 10.dp) + else Modifier + .fillMaxWidth(fillMaxWidth) + .padding(top = 20.dp)).padding(bottom = 25.dp) ) { - IconButton(onClick = { - openWebPage(context.resources.getString(R.string.discord_url)) - }) { - Icon(imageVector = ImageVector.vectorResource(R.drawable.discord), "Discord") - } - IconButton(modifier = Modifier.focusRequester(focusRequester),onClick = { - openWebPage(context.resources.getString(R.string.github_url)) - }) { - Icon(imageVector = ImageVector.vectorResource(R.drawable.github), "Github") + Column(modifier = Modifier.padding(20.dp)) { + Text(stringResource(R.string.thank_you), textAlign = TextAlign.Start, modifier = Modifier.padding(bottom = 20.dp), fontSize = 16.sp) + Text(stringResource(id = R.string.support_help_text), textAlign = TextAlign.Start, fontSize = 16.sp, modifier = Modifier.padding(bottom = 20.dp)) + TextButton(onClick = { openWebPage(context.resources.getString(R.string.docs_url)) }, modifier = Modifier.padding(vertical = 5.dp).focusRequester(focusRequester)) { + Row(horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) { + Row { + Icon(Icons.Rounded.Book, stringResource(id = R.string.docs)) + Text(stringResource(id = R.string.docs_description), textAlign = TextAlign.Justify, modifier = Modifier.padding(start = 10.dp)) + } + Icon(Icons.Rounded.ArrowForward, stringResource(id = R.string.go)) + } + } + Divider(color = MaterialTheme.colorScheme.onBackground, thickness = 0.5.dp) + TextButton(onClick = { openWebPage(context.resources.getString(R.string.discord_url)) }, modifier = Modifier.padding(vertical = 5.dp)) { + Row(horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) { + Row { + Icon(imageVector = ImageVector.vectorResource(R.drawable.discord), stringResource( + id = R.string.discord), Modifier.size(25.dp)) + Text(stringResource(id = R.string.discord_description), textAlign = TextAlign.Justify, modifier = Modifier.padding(start = 10.dp)) + } + Icon(Icons.Rounded.ArrowForward, stringResource(id = R.string.go)) + } + } + Divider(color = MaterialTheme.colorScheme.onBackground, thickness = 0.5.dp) + TextButton(onClick = { openWebPage(context.resources.getString(R.string.github_url)) }, modifier = Modifier.padding(vertical = 5.dp)) { + Row(horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) { + Row { + Icon(imageVector = ImageVector.vectorResource(R.drawable.github), stringResource( + id = R.string.github + ), Modifier.size(25.dp)) + Text("Open an issue", textAlign = TextAlign.Justify, modifier = Modifier.padding(start = 10.dp)) + } + Icon(Icons.Rounded.ArrowForward, stringResource(id = R.string.go)) + } + } + Divider(color = MaterialTheme.colorScheme.onBackground, thickness = 0.5.dp) + TextButton(onClick = { launchEmail() }, modifier = Modifier.padding(vertical = 5.dp)) { + Row(horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) { + Row { + Icon(Icons.Rounded.Mail, stringResource(id = R.string.email)) + Text(stringResource(id = R.string.email_description), textAlign = TextAlign.Justify, modifier = Modifier.padding(start = 10.dp)) + } + Icon(Icons.Rounded.ArrowForward, stringResource(id = R.string.go)) + } + } } } Spacer(modifier = Modifier.weight(1f)) @@ -75,6 +143,6 @@ fun SupportScreen(padding : PaddingValues, focusRequester: FocusRequester) { modifier = Modifier.clickable { openWebPage(context.resources.getString(R.string.privacy_policy_url)) }) - Text("App version: ${com.zaneschepke.wireguardautotunnel.BuildConfig.VERSION_NAME}", Modifier.padding(25.dp)) + Text("App version: ${BuildConfig.VERSION_NAME}", Modifier.padding(25.dp)) } } \ 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 50ca6911..5c19c083 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -6,8 +6,9 @@ Watcher Channel Watcher Notification Channel FOREGROUND_FILE - https://github.com/zaneschepke/wgtunnel - https://zaneschepke.github.io/wgtunnel/ + https://github.com/zaneschepke/wgtunnel/issues + https://zaneschepke.com/wgtunnel-docs/overview.html + https://zaneschepke.com/wgtunnel-docs/privacypolicy.html File is not a .conf or .zip Turn off tunnel before editing No tunnels added yet! @@ -20,10 +21,10 @@ VPN permission is required for the app to work properly. If this permission is not launching, please disable \"Always-on VPN\" in your phone settings for the official WireGuard mobile app and try again. Notifications permission is required for the app to work properly. Open Settings - Add Trusted SSID - Trusted SSIDs + Add trusted wifi name Tunnels - Enable auto-tunneling + Start auto-tunneling + Stop auto-tunneling Tunnel on mobile data \"Allow all the time\" location permission is required for retrieving Wi-Fi SSID in the background. Permission is needed for this feature. Location permission is required for this feature to work properly. @@ -34,7 +35,7 @@ Tunnel on ethernet This feature requires background location permission to enable Wi-Fi SSID monitoring even while the application is closed. For more details, please see the Privacy Policy linked on the Support screen. Background Location Disclosure - Thank you for using WG Tunnel! If you are experiencing issues with the app, please reach out on Discord or create an issue on GitHub. I will try to address the issue as quickly as possible. Thank you! + Thank you for using WG Tunnel! Enter SSID Submit SSID [Interface] @@ -92,7 +93,7 @@ wg-tunnel-db Scanning for QR QR scan failed - None + No trusted wifi names Never Failed to open file stream. An unknown error occurred. @@ -138,4 +139,18 @@ Exported configs to downloads No file explorer installed status + Tunnel on untrusted wifi + zanecschepke@gmail.com + WG Tunnel Support + Send an email… + go + Read the docs (WIP) + Join the community + Discord + Docs + GitHub + Email + Send me an email + If you are experiencing issues, have improvement ideas, or just want to engage, the following resources are available: + \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/Constants.kt b/buildSrc/src/main/kotlin/Constants.kt index 03a81e3c..c2f75e9b 100644 --- a/buildSrc/src/main/kotlin/Constants.kt +++ b/buildSrc/src/main/kotlin/Constants.kt @@ -1,7 +1,7 @@ object Constants { - const val VERSION_NAME = "3.2.2" + const val VERSION_NAME = "3.2.3" const val JVM_TARGET = "17" - const val VERSION_CODE = 32200 + const val VERSION_CODE = 32300 const val TARGET_SDK = 34 const val MIN_SDK = 26 const val APP_ID = "com.zaneschepke.wireguardautotunnel" diff --git a/fastlane/metadata/android/en-US/changelogs/32300.txt b/fastlane/metadata/android/en-US/changelogs/32300.txt new file mode 100644 index 00000000..d3687e1d --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/32300.txt @@ -0,0 +1,5 @@ +Enhancements: +- Add support for mobile data only auto-tunneling +- Improve support screen UI +- Update resource links +- Various other bug fixes \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 630199cd..acb883dc 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,18 +10,18 @@ desugar_jdk_libs = "2.0.4" espressoCore = "3.5.1" firebase-crashlytics-gradle = "2.9.9" google-services = "4.4.0" -hiltAndroid = "2.48.1" +hiltAndroid = "2.49" hiltNavigationCompose = "1.1.0" junit = "4.13.2" -kotlinx-serialization-json = "1.6.0" +kotlinx-serialization-json = "1.6.2" lifecycle-runtime-compose = "2.6.2" material-icons-extended = "1.5.4" material3 = "1.1.2" navigationCompose = "2.7.5" -roomVersion = "2.6.0" +roomVersion = "2.6.1" timber = "5.0.1" tunnel = "1.0.20230706" -androidGradlePlugin = "8.2.0-rc03" +androidGradlePlugin = "8.2.0" kotlin="1.9.10" ksp="1.9.10-1.0.13" composeBom="2023.10.01"