diff --git a/.github/workflows/pre-release.yml b/.github/workflows/pre-release.yml index c8815f6..11cf56b 100644 --- a/.github/workflows/pre-release.yml +++ b/.github/workflows/pre-release.yml @@ -19,6 +19,8 @@ jobs: SIGNING_STORE_PASSWORD: ${{ secrets.SIGNING_STORE_PASSWORD }} KEY_STORE_FILE: 'android_keystore.jks' KEY_STORE_LOCATION: ${{ github.workspace }}/app/keystore/ + GH_USER: ${{ secrets.GH_USER }} + GH_TOKEN: ${{ secrets.GH_TOKEN }} steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5c1b92e..1f26d03 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,6 +20,8 @@ jobs: SIGNING_STORE_PASSWORD: ${{ secrets.SIGNING_STORE_PASSWORD }} KEY_STORE_FILE: 'android_keystore.jks' KEY_STORE_LOCATION: ${{ github.workspace }}/app/keystore/ + GH_USER: ${{ secrets.GH_USER }} + GH_TOKEN: ${{ secrets.GH_TOKEN }} steps: - uses: actions/checkout@v4 diff --git a/README.md b/README.md index ef56ab8..96dcb45 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,31 @@ The repository for these docs can be found [here](https://github.com/zaneschepke ``` $ git clone https://github.com/zaneschepke/wgtunnel $ cd wgtunnel +``` + +Create a personal access token (classic) in GitHuv to be able to pull the wireguard-android github dependencies from GitHub packages +as documented [here](https://docs.github.com/en/packages/learn-github-packages/introduction-to-github-packages#authenticating-to-github-packages). + +Alternatively, you can clone [wireguard-android](https://github.com/zaneschepke/wireguard-android) and run the following command to publish the dependency to your local maven repository (requires you have maven installed). This is the ideal approach +if you intent to make changes to this lib. + +``` +$ git clone https://github.com/zaneschepke/wireguard-android +$ cd wireguard-android +$ brew install maven +$ ./gradlew publishToMavenLocal +``` + +The [wireguard-android](https://github.com/zaneschepke/wireguard-android) dependency is a fork of the official [wireguard-android](https://github.com/WireGuard/wireguard-android) library. + +Add the following lines to local.properties file: +``` +GH_USER= +GH_TOKEN= +``` + +And then build the app: +``` $ ./gradlew assembleDebug ``` diff --git a/app/build.gradle.kts b/app/build.gradle.kts index bf10316..04eaa6b 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -156,13 +156,14 @@ dependencies { debugImplementation(libs.androidx.compose.ui.tooling) debugImplementation(libs.androidx.compose.manifest) - // wg + // get tunnel lib from github packages or mavenLocal implementation(libs.tunnel) coreLibraryDesugaring(libs.desugar.jdk.libs) // logging implementation(libs.timber) + // compose navigation implementation(libs.androidx.navigation.compose) implementation(libs.androidx.hilt.navigation.compose) @@ -172,7 +173,6 @@ dependencies { ksp(libs.hilt.android.compiler) // accompanist - implementation(libs.accompanist.systemuicontroller) implementation(libs.accompanist.permissions) implementation(libs.accompanist.flowlayout) implementation(libs.accompanist.drawablepainter) diff --git a/app/src/androidTest/java/com/zaneschepke/wireguardautotunnel/MigrationTest.kt b/app/src/androidTest/java/com/zaneschepke/wireguardautotunnel/MigrationTest.kt index 1ce565f..e461527 100644 --- a/app/src/androidTest/java/com/zaneschepke/wireguardautotunnel/MigrationTest.kt +++ b/app/src/androidTest/java/com/zaneschepke/wireguardautotunnel/MigrationTest.kt @@ -4,6 +4,7 @@ import androidx.room.testing.MigrationTestHelper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import com.zaneschepke.wireguardautotunnel.data.AppDatabase +import com.zaneschepke.wireguardautotunnel.data.Queries import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -26,33 +27,7 @@ class MigrationTest { helper.createDatabase(dbName, 4).apply { // Database has schema version 1. Insert some data using SQL queries. // You can't use DAO classes because they expect the latest schema. - execSQL( - "INSERT INTO Settings (is_tunnel_enabled," + - "is_tunnel_on_mobile_data_enabled," + - "trusted_network_ssids," + - "default_tunnel," + - "is_always_on_vpn_enabled," + - "is_tunnel_on_ethernet_enabled," + - "is_shortcuts_enabled," + - "is_battery_saver_enabled," + - "is_tunnel_on_wifi_enabled," + - "is_kernel_enabled," + - "is_restore_on_boot_enabled," + - "is_multi_tunnel_enabled)" + - " VALUES " + - "('false'," + - "'false'," + - "'[trustedSSID1,trustedSSID2]'," + - "'defaultTunnel'," + - "'false'," + - "'false'," + - "'false'," + - "'false'," + - "'false'," + - "'false'," + - "'false'," + - "'false')", - ) + execSQL(Queries.createDefaultSettings()) execSQL( "INSERT INTO TunnelConfig (name, wg_quick)" + " VALUES ('hello', 'hello')", ) diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/DatabaseCallback.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/DatabaseCallback.kt new file mode 100644 index 0000000..a620dcd --- /dev/null +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/DatabaseCallback.kt @@ -0,0 +1,21 @@ +package com.zaneschepke.wireguardautotunnel.data + +import androidx.room.RoomDatabase +import androidx.sqlite.db.SupportSQLiteDatabase +import timber.log.Timber + +class DatabaseCallback : RoomDatabase.Callback() { + override fun onCreate(db: SupportSQLiteDatabase) = db.run { + // Notice non-ui thread is here + beginTransaction() + try { + execSQL(Queries.createDefaultSettings()) + Timber.i("Bootstrapping settings data") + setTransactionSuccessful() + } catch (e : Exception) { + Timber.e(e) + } finally { + endTransaction() + } + } +} diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/Queries.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/Queries.kt new file mode 100644 index 0000000..62f164e --- /dev/null +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/Queries.kt @@ -0,0 +1,33 @@ +package com.zaneschepke.wireguardautotunnel.data + +object Queries { + fun createDefaultSettings() : String { + return """ + INSERT INTO Settings (is_tunnel_enabled, + is_tunnel_on_mobile_data_enabled, + trusted_network_ssids, + default_tunnel, + is_always_on_vpn_enabled, + is_tunnel_on_ethernet_enabled, + is_shortcuts_enabled, + is_battery_saver_enabled, + is_tunnel_on_wifi_enabled, + is_kernel_enabled, + is_restore_on_boot_enabled, + is_multi_tunnel_enabled) + VALUES + ('false', + 'false', + '[trustedSSID1,trustedSSID2]', + NULL, + 'false', + 'false', + 'false', + 'false', + 'false', + 'false', + 'false', + 'false') + """.trimIndent() + } +} diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/module/DatabaseModule.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/module/DatabaseModule.kt index 3ca9695..c0f4617 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/module/DatabaseModule.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/module/DatabaseModule.kt @@ -4,6 +4,7 @@ import android.content.Context import androidx.room.Room import com.zaneschepke.wireguardautotunnel.R import com.zaneschepke.wireguardautotunnel.data.AppDatabase +import com.zaneschepke.wireguardautotunnel.data.DatabaseCallback import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -23,6 +24,7 @@ class DatabaseModule { context.getString(R.string.db_name), ) .fallbackToDestructiveMigration() + .addCallback(DatabaseCallback()) .build() } } diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/receiver/BootReceiver.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/receiver/BootReceiver.kt index 04c4194..320fb72 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/receiver/BootReceiver.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/receiver/BootReceiver.kt @@ -8,6 +8,7 @@ import com.zaneschepke.wireguardautotunnel.data.repository.TunnelConfigRepositor import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager import com.zaneschepke.wireguardautotunnel.util.goAsync import dagger.hilt.android.AndroidEntryPoint +import timber.log.Timber import javax.inject.Inject @AndroidEntryPoint @@ -21,8 +22,10 @@ class BootReceiver : BroadcastReceiver() { if (Intent.ACTION_BOOT_COMPLETED != intent?.action) return@goAsync val settings = settingsRepository.getSettings() if (settings.isAutoTunnelEnabled) { + Timber.i("Starting watcher service from boot") ServiceManager.startWatcherServiceForeground(context!!) } else if(settings.isAlwaysOnVpnEnabled) { + Timber.i("Starting tunnel from boot") ServiceManager.startVpnServicePrimaryTunnel(context!!, settings, tunnelConfigRepository.getAll().firstOrNull()) } } 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 b6f62f6..7e90a5f 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 @@ -152,12 +152,16 @@ class WireGuardConnectivityWatcherService : ForegroundService() { wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager).run { newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "$tag::lock").apply { - if (isBatterySaverOn) { - Timber.d("Initiating wakelock with timeout") - acquire(Constants.BATTERY_SAVER_WATCHER_WAKE_LOCK_TIMEOUT) - } else { - Timber.d("Initiating wakelock with zero timeout") - acquire(Constants.DEFAULT_WATCHER_WAKE_LOCK_TIMEOUT) + try { + if (isBatterySaverOn) { + Timber.d("Initiating wakelock with timeout") + acquire(Constants.BATTERY_SAVER_WATCHER_WAKE_LOCK_TIMEOUT) + } else { + Timber.d("Initiating wakelock with zero timeout") + acquire(Constants.DEFAULT_WATCHER_WAKE_LOCK_TIMEOUT) + } + } finally { + release() } } } diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tunnel/WireGuardTunnel.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tunnel/WireGuardTunnel.kt index a6974f9..80952ff 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tunnel/WireGuardTunnel.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tunnel/WireGuardTunnel.kt @@ -25,7 +25,7 @@ import javax.inject.Inject class WireGuardTunnel @Inject constructor( - @Userspace private val userspaceBackend: Backend, + @Userspace private val userspaceBackend : Backend, @Kernel private val kernelBackend: Backend, private val settingsRepository: SettingsRepository ) : VpnService { diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/ActivityViewModel.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/ActivityViewModel.kt index bee1b9b..b1a09b3 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/ActivityViewModel.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/ActivityViewModel.kt @@ -1,13 +1,12 @@ package com.zaneschepke.wireguardautotunnel.ui import androidx.lifecycle.ViewModel -import com.zaneschepke.wireguardautotunnel.data.SettingsDao import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject @HiltViewModel class ActivityViewModel @Inject -constructor( - private val settingsRepo: SettingsDao, -) : ViewModel() {} +constructor() : ViewModel() { + +} diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/MainActivity.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/MainActivity.kt index 4a6c9b2..f2012af 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/MainActivity.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/MainActivity.kt @@ -6,11 +6,13 @@ import android.net.Uri import android.os.Build import android.os.Bundle import android.provider.Settings -import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.SystemBarStyle import androidx.activity.compose.setContent -import androidx.activity.result.contract.ActivityResultContracts +import androidx.activity.enableEdgeToEdge import androidx.appcompat.app.AppCompatActivity import androidx.compose.foundation.focusable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.SnackbarData @@ -20,13 +22,12 @@ import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarResult import androidx.compose.material3.surfaceColorAtElevation import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusProperties +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.unit.dp import androidx.lifecycle.lifecycleScope import androidx.navigation.compose.NavHost @@ -35,7 +36,6 @@ import androidx.navigation.compose.rememberNavController import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.google.accompanist.permissions.isGranted import com.google.accompanist.permissions.rememberPermissionState -import com.wireguard.android.backend.GoBackend import com.zaneschepke.wireguardautotunnel.R import com.zaneschepke.wireguardautotunnel.WireGuardAutoTunnel import com.zaneschepke.wireguardautotunnel.data.datastore.DataStoreManager @@ -47,7 +47,6 @@ import com.zaneschepke.wireguardautotunnel.ui.screens.config.ConfigScreen import com.zaneschepke.wireguardautotunnel.ui.screens.main.MainScreen import com.zaneschepke.wireguardautotunnel.ui.screens.settings.SettingsScreen import com.zaneschepke.wireguardautotunnel.ui.screens.support.SupportScreen -import com.zaneschepke.wireguardautotunnel.ui.theme.TransparentSystemBars import com.zaneschepke.wireguardautotunnel.ui.theme.WireguardAutoTunnelTheme import com.zaneschepke.wireguardautotunnel.util.Constants import dagger.hilt.android.AndroidEntryPoint @@ -70,6 +69,8 @@ class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + enableEdgeToEdge(navigationBarStyle = SystemBarStyle.dark(Color.Transparent.toArgb())) + // load preferences into memory and init data lifecycleScope.launch { try { @@ -80,13 +81,12 @@ class MainActivity : AppCompatActivity() { } } setContent { - // val activityViewModel = hiltViewModel() + //val activityViewModel = hiltViewModel() val navController = rememberNavController() val focusRequester = remember { FocusRequester() } WireguardAutoTunnelTheme { - TransparentSystemBars() val snackbarHostState = remember { SnackbarHostState() } @@ -134,9 +134,11 @@ class MainActivity : AppCompatActivity() { ) } }, - modifier = Modifier.focusable().focusProperties { up = focusRequester }, + modifier = Modifier + .focusable() + .focusProperties { up = focusRequester }, bottomBar = - if (notificationPermissionState != null && notificationPermissionState.status.isGranted) { + if (notificationPermissionState == null || notificationPermissionState.status.isGranted) { { BottomNavBar( navController, @@ -151,66 +153,64 @@ class MainActivity : AppCompatActivity() { {} }, ) { padding -> - if (notificationPermissionState != null && !notificationPermissionState.status.isGranted) { - PermissionRequestFailedScreen( - padding = padding, - onRequestAgain = { - val intentSettings = - Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) - intentSettings.data = - Uri.fromParts( - Constants.URI_PACKAGE_SCHEME, - this.packageName, - null, - ) - startActivity(intentSettings) - }, - message = getString(R.string.notification_permission_required), - getString(R.string.open_settings), - ) - return@Scaffold - } - NavHost(navController, startDestination = Screen.Main.route) { - composable( - Screen.Main.route, - ) { - MainScreen( - padding = padding, - focusRequester = focusRequester, - showSnackbarMessage = { message -> showSnackBarMessage(message) }, - navController = navController, + Column(modifier = Modifier.padding(padding)) { + if (notificationPermissionState != null && !notificationPermissionState.status.isGranted) { + PermissionRequestFailedScreen( + onRequestAgain = { + val intentSettings = + Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) + intentSettings.data = + Uri.fromParts( + Constants.URI_PACKAGE_SCHEME, + this@MainActivity.packageName, + null, + ) + startActivity(intentSettings) + }, + message = getString(R.string.notification_permission_required), + getString(R.string.open_settings), ) + return@Scaffold } - composable( - Screen.Settings.route, - ) { - SettingsScreen( - padding = padding, - showSnackbarMessage = { message -> showSnackBarMessage(message) }, - focusRequester = focusRequester, - ) - } - composable( - Screen.Support.route, - ) { - SupportScreen( - padding = padding, - focusRequester = focusRequester, - showSnackbarMessage = { message -> showSnackBarMessage(message) }, - ) - } - composable("${Screen.Config.route}/{id}") { - val id = it.arguments?.getString("id") - if (!id.isNullOrBlank()) { - ConfigScreen( + NavHost(navController, startDestination = Screen.Main.route) { + composable( + Screen.Main.route, + ) { + MainScreen( + focusRequester = focusRequester, + showSnackbarMessage = { message -> showSnackBarMessage(message) }, navController = navController, - id = id, - showSnackbarMessage = { message -> - showSnackBarMessage(message) - }, + ) + } + composable( + Screen.Settings.route, + ) { + SettingsScreen( + showSnackbarMessage = { message -> showSnackBarMessage(message) }, focusRequester = focusRequester, ) } + composable( + Screen.Support.route, + ) { + SupportScreen( + focusRequester = focusRequester, + showSnackbarMessage = { message -> showSnackBarMessage(message) }, + ) + } + composable("${Screen.Config.route}/{id}") { + val id = it.arguments?.getString("id") + if (!id.isNullOrBlank()) { + ConfigScreen( + navController = navController, + id = id, + showSnackbarMessage = { message -> + showSnackBarMessage(message) + }, + focusRequester = focusRequester, + ) + } + } } } } diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/PermissionRequestFailedScreen.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/PermissionRequestFailedScreen.kt index 11058fb..f6b8056 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/PermissionRequestFailedScreen.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/PermissionRequestFailedScreen.kt @@ -2,7 +2,6 @@ package com.zaneschepke.wireguardautotunnel.ui.common import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material3.Button @@ -17,7 +16,6 @@ import kotlinx.coroutines.launch @Composable fun PermissionRequestFailedScreen( - padding: PaddingValues, onRequestAgain: () -> Unit, message: String, buttonText: String @@ -26,7 +24,7 @@ fun PermissionRequestFailedScreen( Column( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center, - modifier = Modifier.fillMaxSize().padding(padding), + modifier = Modifier.fillMaxSize(), ) { Text(message, textAlign = TextAlign.Center, modifier = Modifier.padding(15.dp)) Button( diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/prompt/CustomSnackbar.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/prompt/CustomSnackbar.kt index dd46f9f..7723f54 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/prompt/CustomSnackbar.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/prompt/CustomSnackbar.kt @@ -19,7 +19,6 @@ import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.LayoutDirection @@ -33,7 +32,6 @@ fun CustomSnackBar( isRtl: Boolean = true, containerColor: Color = MaterialTheme.colorScheme.surface ) { - val context = LocalContext.current Snackbar( containerColor = containerColor, modifier = diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/config/ConfigScreen.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/config/ConfigScreen.kt index bab109c..6721de8 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/config/ConfigScreen.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/config/ConfigScreen.kt @@ -1,7 +1,6 @@ package com.zaneschepke.wireguardautotunnel.ui.screens.config import android.annotation.SuppressLint -import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.Image import androidx.compose.foundation.clickable import androidx.compose.foundation.focusGroup @@ -29,7 +28,7 @@ import androidx.compose.material.icons.rounded.ContentCopy import androidx.compose.material.icons.rounded.Delete import androidx.compose.material.icons.rounded.Refresh import androidx.compose.material.icons.rounded.Save -import androidx.compose.material3.AlertDialog +import androidx.compose.material3.BasicAlertDialog import androidx.compose.material3.Checkbox import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FabPosition @@ -51,7 +50,6 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment -import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester @@ -86,9 +84,7 @@ import kotlinx.coroutines.delay @SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") @OptIn( - ExperimentalComposeUiApi::class, ExperimentalMaterial3Api::class, - ExperimentalFoundationApi::class, ) @Composable fun ConfigScreen( @@ -145,7 +141,7 @@ fun ConfigScreen( showAuthPrompt = false isAuthenticated = true }, - onError = { error -> + onError = { showAuthPrompt = false showSnackbarMessage(Event.Error.AuthenticationFailed.message) }, @@ -161,20 +157,20 @@ fun ConfigScreen( remember(uiState.packages) { uiState.packages.sortedBy { viewModel.getPackageLabel(it) } } - AlertDialog(onDismissRequest = { showApplicationsDialog = false }) { + BasicAlertDialog(onDismissRequest = { showApplicationsDialog = false }) { Surface( tonalElevation = 2.dp, shadowElevation = 2.dp, shape = RoundedCornerShape(12.dp), color = MaterialTheme.colorScheme.surface, modifier = - Modifier.fillMaxWidth() - .fillMaxHeight(if (uiState.isAllApplicationsEnabled) 1 / 5f else 4 / 5f), + Modifier.fillMaxWidth() + .fillMaxHeight(if (uiState.isAllApplicationsEnabled) 1 / 5f else 4 / 5f), ) { Column(modifier = Modifier.fillMaxWidth()) { Row( modifier = - Modifier.fillMaxWidth().padding(horizontal = 20.dp, vertical = 7.dp), + Modifier.fillMaxWidth().padding(horizontal = 20.dp, vertical = 7.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween, ) { @@ -187,8 +183,8 @@ fun ConfigScreen( if (!uiState.isAllApplicationsEnabled) { Row( modifier = - Modifier.fillMaxWidth() - .padding(horizontal = 20.dp, vertical = 7.dp), + Modifier.fillMaxWidth() + .padding(horizontal = 20.dp, vertical = 7.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween, ) { @@ -219,8 +215,8 @@ fun ConfigScreen( } Row( modifier = - Modifier.fillMaxWidth() - .padding(horizontal = 20.dp, vertical = 7.dp), + Modifier.fillMaxWidth() + .padding(horizontal = 20.dp, vertical = 7.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween, ) { @@ -262,9 +258,9 @@ fun ConfigScreen( Checkbox( modifier = Modifier.fillMaxSize(), checked = - (uiState.checkedPackageNames.contains( - pack.packageName - )), + (uiState.checkedPackageNames.contains( + pack.packageName + )), onCheckedChange = { if (it) { viewModel.onAddCheckedPackage(pack.packageName) @@ -299,7 +295,7 @@ fun ConfigScreen( var fobColor by remember { mutableStateOf(secondaryColor) } FloatingActionButton( modifier = - Modifier.padding(bottom = 90.dp).onFocusChanged { + Modifier.onFocusChanged { if (WireGuardAutoTunnel.isRunningOnAndroidTv()) { fobColor = if (it.isFocused) hoverColor else secondaryColor } 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 5a2255b..5fa25c3 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 @@ -1,6 +1,5 @@ package com.zaneschepke.wireguardautotunnel.ui.screens.main -import android.annotation.SuppressLint import android.content.Context import android.content.Intent import android.content.pm.PackageManager @@ -20,7 +19,6 @@ import androidx.compose.foundation.gestures.ScrollableDefaults import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize @@ -45,10 +43,10 @@ import androidx.compose.material.icons.rounded.Edit import androidx.compose.material.icons.rounded.Info import androidx.compose.material.icons.rounded.Star import androidx.compose.material3.AlertDialog -import androidx.compose.material3.Divider import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FabPosition import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme @@ -110,12 +108,10 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch -@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") @OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class) @Composable fun MainScreen( viewModel: MainViewModel = hiltViewModel(), - padding: PaddingValues, focusRequester: FocusRequester, showSnackbarMessage: (String) -> Unit, navController: NavController @@ -344,7 +340,6 @@ fun MainScreen( ) Modifier.focusRequester(focusRequester) else Modifier) - .padding(bottom = 90.dp) .onFocusChanged { if (WireGuardAutoTunnel.isRunningOnAndroidTv()) { fobColor = if (it.isFocused) hoverColor else secondaryColor @@ -367,7 +362,7 @@ fun MainScreen( Column( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center, - modifier = Modifier.fillMaxSize().padding(padding), + modifier = Modifier.fillMaxSize().padding(innerPadding), ) { Text(text = stringResource(R.string.no_tunnels), fontStyle = FontStyle.Italic) } @@ -398,7 +393,7 @@ fun MainScreen( ) } if (!WireGuardAutoTunnel.isRunningOnAndroidTv()) { - Divider() + HorizontalDivider() Row( modifier = Modifier.fillMaxWidth() @@ -430,7 +425,7 @@ fun MainScreen( ) } } - Divider() + HorizontalDivider() Row( modifier = Modifier.fillMaxWidth() @@ -461,8 +456,7 @@ fun MainScreen( modifier = Modifier.fillMaxWidth() .fillMaxHeight(.90f) - .overscroll(ScrollableDefaults.overscrollEffect()) - .padding(innerPadding), + .overscroll(ScrollableDefaults.overscrollEffect()), state = rememberLazyListState(0, uiState.tunnels.count()), userScrollEnabled = true, reverseLayout = true, 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 6ad7393..86e4634 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 @@ -20,7 +20,6 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.FlowRow 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 @@ -95,7 +94,6 @@ import java.io.File @Composable fun SettingsScreen( viewModel: SettingsViewModel = hiltViewModel(), - padding: PaddingValues, showSnackbarMessage: (String) -> Unit, focusRequester: FocusRequester ) { @@ -127,7 +125,7 @@ fun SettingsScreen( rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult -> if (result.resultCode == Activity.RESULT_OK) { - val intent = result.data + result.data // Handle the Intent } viewModel.setBatteryOptimizeDisableShown() @@ -248,7 +246,7 @@ fun SettingsScreen( Column( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Top, - modifier = Modifier.fillMaxSize().verticalScroll(scrollState).padding(padding), + modifier = Modifier.fillMaxSize().verticalScroll(scrollState), ) { Icon( Icons.Rounded.LocationOff, @@ -314,7 +312,7 @@ fun SettingsScreen( Column( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center, - modifier = Modifier.fillMaxSize().padding(padding), + modifier = Modifier.fillMaxSize(), ) { Text( stringResource(R.string.one_tunnel_required), @@ -347,7 +345,7 @@ fun SettingsScreen( .fillMaxWidth(fillMaxWidth) .padding(top = 10.dp) } else { - Modifier.fillMaxWidth(fillMaxWidth).padding(top = 60.dp) + Modifier.fillMaxWidth(fillMaxWidth).padding(top = 20.dp) }) .padding(bottom = 10.dp), ) { 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 8be48c7..50eab8a 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 @@ -8,7 +8,6 @@ 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 @@ -20,10 +19,10 @@ 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.automirrored.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.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface @@ -58,7 +57,6 @@ import com.zaneschepke.wireguardautotunnel.util.Event @Composable fun SupportScreen( viewModel: SupportViewModel = hiltViewModel(), - padding: PaddingValues, showSnackbarMessage: (String) -> Unit, focusRequester: FocusRequester ) { @@ -107,7 +105,6 @@ fun SupportScreen( Modifier.fillMaxSize() .verticalScroll(rememberScrollState()) .focusable() - .padding(padding), ) { Surface( tonalElevation = 2.dp, @@ -155,10 +152,16 @@ fun SupportScreen( modifier = Modifier.padding(start = 10.dp), ) } - Icon(Icons.Rounded.ArrowForward, stringResource(id = R.string.go)) + Icon( + Icons.AutoMirrored.Rounded.ArrowForward, + stringResource(id = R.string.go) + ) } } - Divider(color = MaterialTheme.colorScheme.onBackground, thickness = 0.5.dp) + HorizontalDivider( + thickness = 0.5.dp, + color = MaterialTheme.colorScheme.onBackground + ) TextButton( onClick = { openWebPage(context.resources.getString(R.string.discord_url)) }, modifier = Modifier.padding(vertical = 5.dp), @@ -180,10 +183,16 @@ fun SupportScreen( modifier = Modifier.padding(start = 10.dp), ) } - Icon(Icons.Rounded.ArrowForward, stringResource(id = R.string.go)) + Icon( + Icons.AutoMirrored.Rounded.ArrowForward, + stringResource(id = R.string.go) + ) } } - Divider(color = MaterialTheme.colorScheme.onBackground, thickness = 0.5.dp) + HorizontalDivider( + thickness = 0.5.dp, + color = MaterialTheme.colorScheme.onBackground + ) TextButton( onClick = { openWebPage(context.resources.getString(R.string.github_url)) }, modifier = Modifier.padding(vertical = 5.dp), @@ -205,10 +214,16 @@ fun SupportScreen( modifier = Modifier.padding(start = 10.dp), ) } - Icon(Icons.Rounded.ArrowForward, stringResource(id = R.string.go)) + Icon( + Icons.AutoMirrored.Rounded.ArrowForward, + stringResource(id = R.string.go) + ) } } - Divider(color = MaterialTheme.colorScheme.onBackground, thickness = 0.5.dp) + HorizontalDivider( + thickness = 0.5.dp, + color = MaterialTheme.colorScheme.onBackground + ) TextButton( onClick = { launchEmail() }, modifier = Modifier.padding(vertical = 5.dp), @@ -226,7 +241,10 @@ fun SupportScreen( modifier = Modifier.padding(start = 10.dp), ) } - Icon(Icons.Rounded.ArrowForward, stringResource(id = R.string.go)) + Icon( + Icons.AutoMirrored.Rounded.ArrowForward, + stringResource(id = R.string.go) + ) } } } diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/theme/TransparentSystemBars.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/theme/TransparentSystemBars.kt deleted file mode 100644 index 9d353ba..0000000 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/theme/TransparentSystemBars.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.zaneschepke.wireguardautotunnel.ui.theme - -import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import androidx.compose.ui.graphics.Color -import com.google.accompanist.systemuicontroller.rememberSystemUiController - -@Composable -fun TransparentSystemBars() { - val systemUiController = rememberSystemUiController() - val useDarkIcons = !isSystemInDarkTheme() - - DisposableEffect(systemUiController, useDarkIcons) { - systemUiController.setSystemBarsColor( - color = Color.Transparent, - darkIcons = useDarkIcons, - ) - - onDispose {} - } -} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d8cb6c5..cb6c60a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -140,7 +140,7 @@ Exported configs to downloads status Tunnel on untrusted wifi - zanecschepke@gmail.com + support@zaneschepke.com WG Tunnel Support Send an email… go diff --git a/buildSrc/src/main/kotlin/BuildHelper.kt b/buildSrc/src/main/kotlin/BuildHelper.kt index 82a5d7e..f0b0894 100644 --- a/buildSrc/src/main/kotlin/BuildHelper.kt +++ b/buildSrc/src/main/kotlin/BuildHelper.kt @@ -1,4 +1,5 @@ import org.gradle.api.invocation.Gradle +import java.io.File object BuildHelper { private fun getCurrentFlavor(gradle: Gradle): String { @@ -21,6 +22,17 @@ object BuildHelper { return flavor } + fun getLocalProperty(key: String, file: String = "local.properties"): String? { + val properties = java.util.Properties() + val localProperties = File(file) + if (localProperties.isFile) { + java.io.InputStreamReader(java.io.FileInputStream(localProperties), Charsets.UTF_8).use { reader -> + properties.load(reader) + } + } else return null + return properties.getProperty(key) + } + fun isGeneralFlavor(gradle: Gradle): Boolean { return getCurrentFlavor(gradle) == "general" } diff --git a/buildSrc/src/main/kotlin/Constants.kt b/buildSrc/src/main/kotlin/Constants.kt index 6457cb6..15a82fb 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.3.5" + const val VERSION_NAME = "3.3.6" const val JVM_TARGET = "17" - const val VERSION_CODE = 33500 + const val VERSION_CODE = 33600 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/33600.txt b/fastlane/metadata/android/en-US/changelogs/33600.txt new file mode 100644 index 0000000..6f42155 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/33600.txt @@ -0,0 +1,5 @@ +What's new: +- Improve first launch flow +- Switch to wireguard lib fork +- Request VPN permission on first VPN start +- Bump versions \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index fbab400..3967c52 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,35 +10,35 @@ datastorePreferences = "1.0.0" desugar_jdk_libs = "2.0.4" espressoCore = "3.5.1" firebase-crashlytics-gradle = "2.9.9" -google-services = "4.4.0" +google-services = "4.4.1" hiltAndroid = "2.50" hiltNavigationCompose = "1.1.0" junit = "4.13.2" kotlinx-serialization-json = "1.6.2" lifecycle-runtime-compose = "2.7.0" -material3 = "1.2.0-rc01" -navigationCompose = "2.7.6" +material3 = "1.2.0" +navigationCompose = "2.7.7" roomVersion = "2.6.1" timber = "5.0.1" -tunnel = "1.0.20230706" -androidGradlePlugin = "8.3.0-rc01" +tunnel = "1.1.0" +androidGradlePlugin = "8.3.0-rc02" kotlin = "1.9.22" ksp = "1.9.22-1.0.16" -composeBom = "2024.01.00" -firebaseBom = "32.7.1" -compose = "1.6.0" -crashlytics = "18.6.1" -analytics = "21.5.0" +composeBom = "2024.02.00" +firebaseBom = "32.7.2" +compose = "1.6.1" +crashlytics = "18.6.2" +analytics = "21.5.1" zxingAndroidEmbedded = "4.3.0" -zxingCore = "3.5.2" +zxingCore = "3.5.3" [libraries] + # accompanist accompanist-drawablepainter = { module = "com.google.accompanist:accompanist-drawablepainter", version.ref = "accompanist" } accompanist-flowlayout = { module = "com.google.accompanist:accompanist-flowlayout", version.ref = "accompanist" } accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanist" } -accompanist-systemuicontroller = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "accompanist" } #room androidx-biometric-ktx = { module = "androidx.biometric:biometric-ktx", version.ref = "biometricKtx" } @@ -82,7 +82,7 @@ lifecycle-runtime-compose = { module = "androidx.lifecycle:lifecycle-runtime-com material-icons-extended = { module = "androidx.compose.material:material-icons-extended", version.ref = "compose" } timber = { module = "com.jakewharton.timber:timber", version.ref = "timber" } -tunnel = { module = "com.wireguard.android:tunnel", version.ref = "tunnel" } +tunnel = { module = "com.zaneschepke:wireguard-android", version.ref = "tunnel" } #firebase google-firebase-crashlytics-ktx = { module = "com.google.firebase:firebase-crashlytics-ktx", version.ref = "crashlytics" } @@ -93,9 +93,9 @@ google-services = { module = "com.google.gms:google-services", version.ref = "go zxing-core = { module = "com.google.zxing:core", version.ref = "zxingCore" } zxing-android-embedded = { module = "com.journeyapps:zxing-android-embedded", version.ref = "zxingAndroidEmbedded" } - [plugins] android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } hilt-android = { id = "com.google.dagger.hilt.android", version.ref = "hiltAndroid" } -ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } \ No newline at end of file +ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } +androidLibrary = { id = "com.android.library", version.ref = "androidGradlePlugin" } \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 6e06648..8be2d76 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -2,5 +2,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip +distributionSha256Sum=3e1af3ae886920c3ac87f7a91f816c0c7c436f276a6eefdb3da152100fef72ae +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/settings.gradle.kts b/settings.gradle.kts index 9daeafa..a29e22c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -2,18 +2,42 @@ pluginManagement { repositories { google() mavenCentral() + mavenLocal() gradlePluginPortal() } } +val GITHUB_USER_VAR = "GH_USER" +val GITHUB_TOKEN_VAR = "GH_TOKEN" + dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { + maven { + name = "GitHubPackages" + url = uri("https://maven.pkg.github.com/zaneschepke/wireguard-android") + credentials { + username = getLocalProperty(GITHUB_USER_VAR) ?: System.getenv(GITHUB_USER_VAR) + password = getLocalProperty(GITHUB_TOKEN_VAR) ?: System.getenv(GITHUB_TOKEN_VAR) + } + } google() mavenCentral() + mavenLocal() } } +fun getLocalProperty(key: String, file: String = "local.properties"): String? { + val properties = java.util.Properties() + val localProperties = File(file) + if (localProperties.isFile) { + java.io.InputStreamReader(java.io.FileInputStream(localProperties), Charsets.UTF_8).use { reader -> + properties.load(reader) + } + } else return null + return properties.getProperty(key) +} + rootProject.name = "WG Tunnel" include(":app")