diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 25d575d..a42d733 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 0000000..d5d93a3 --- /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 39cd44f..f7305ef 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 ebfff9f..e56968f 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 68d6aab..77bcb3f 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 dcb204e..73af37d 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 de8f802..e66e843 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 6044634..de88650 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 a72dd95..f7c4672 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 59d53c4..58a6eed 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 50ca691..5c19c08 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 03a81e3..c2f75e9 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 0000000..d3687e1 --- /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 630199c..acb883d 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"