feat: migrate to forked lib
Migrated app to a forked version of wireguard-android to enable development work on features that require changes to the core lib, like #107 #104 #87 #52 #6 Improved first launch flow by change vpn permission to only launch on first tunnel start Changed to proper database seeding strategy Updated README to account for GitHub packages auth requirement Migrated from deprecated UI components and libs Bump versions
This commit is contained in:
parent
500b85f687
commit
2690ce29e1
|
@ -19,6 +19,8 @@ jobs:
|
||||||
SIGNING_STORE_PASSWORD: ${{ secrets.SIGNING_STORE_PASSWORD }}
|
SIGNING_STORE_PASSWORD: ${{ secrets.SIGNING_STORE_PASSWORD }}
|
||||||
KEY_STORE_FILE: 'android_keystore.jks'
|
KEY_STORE_FILE: 'android_keystore.jks'
|
||||||
KEY_STORE_LOCATION: ${{ github.workspace }}/app/keystore/
|
KEY_STORE_LOCATION: ${{ github.workspace }}/app/keystore/
|
||||||
|
GH_USER: ${{ secrets.GH_USER }}
|
||||||
|
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
|
@ -20,6 +20,8 @@ jobs:
|
||||||
SIGNING_STORE_PASSWORD: ${{ secrets.SIGNING_STORE_PASSWORD }}
|
SIGNING_STORE_PASSWORD: ${{ secrets.SIGNING_STORE_PASSWORD }}
|
||||||
KEY_STORE_FILE: 'android_keystore.jks'
|
KEY_STORE_FILE: 'android_keystore.jks'
|
||||||
KEY_STORE_LOCATION: ${{ github.workspace }}/app/keystore/
|
KEY_STORE_LOCATION: ${{ github.workspace }}/app/keystore/
|
||||||
|
GH_USER: ${{ secrets.GH_USER }}
|
||||||
|
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
25
README.md
25
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
|
$ git clone https://github.com/zaneschepke/wgtunnel
|
||||||
$ cd 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=<your github username>
|
||||||
|
GH_TOKEN=<the personal access token with read package permission you just created>
|
||||||
|
```
|
||||||
|
|
||||||
|
And then build the app:
|
||||||
|
```
|
||||||
$ ./gradlew assembleDebug
|
$ ./gradlew assembleDebug
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -156,13 +156,14 @@ dependencies {
|
||||||
debugImplementation(libs.androidx.compose.ui.tooling)
|
debugImplementation(libs.androidx.compose.ui.tooling)
|
||||||
debugImplementation(libs.androidx.compose.manifest)
|
debugImplementation(libs.androidx.compose.manifest)
|
||||||
|
|
||||||
// wg
|
// get tunnel lib from github packages or mavenLocal
|
||||||
implementation(libs.tunnel)
|
implementation(libs.tunnel)
|
||||||
coreLibraryDesugaring(libs.desugar.jdk.libs)
|
coreLibraryDesugaring(libs.desugar.jdk.libs)
|
||||||
|
|
||||||
// logging
|
// logging
|
||||||
implementation(libs.timber)
|
implementation(libs.timber)
|
||||||
|
|
||||||
|
|
||||||
// compose navigation
|
// compose navigation
|
||||||
implementation(libs.androidx.navigation.compose)
|
implementation(libs.androidx.navigation.compose)
|
||||||
implementation(libs.androidx.hilt.navigation.compose)
|
implementation(libs.androidx.hilt.navigation.compose)
|
||||||
|
@ -172,7 +173,6 @@ dependencies {
|
||||||
ksp(libs.hilt.android.compiler)
|
ksp(libs.hilt.android.compiler)
|
||||||
|
|
||||||
// accompanist
|
// accompanist
|
||||||
implementation(libs.accompanist.systemuicontroller)
|
|
||||||
implementation(libs.accompanist.permissions)
|
implementation(libs.accompanist.permissions)
|
||||||
implementation(libs.accompanist.flowlayout)
|
implementation(libs.accompanist.flowlayout)
|
||||||
implementation(libs.accompanist.drawablepainter)
|
implementation(libs.accompanist.drawablepainter)
|
||||||
|
|
|
@ -4,6 +4,7 @@ import androidx.room.testing.MigrationTestHelper
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import androidx.test.platform.app.InstrumentationRegistry
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
import com.zaneschepke.wireguardautotunnel.data.AppDatabase
|
import com.zaneschepke.wireguardautotunnel.data.AppDatabase
|
||||||
|
import com.zaneschepke.wireguardautotunnel.data.Queries
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
|
@ -26,33 +27,7 @@ class MigrationTest {
|
||||||
helper.createDatabase(dbName, 4).apply {
|
helper.createDatabase(dbName, 4).apply {
|
||||||
// Database has schema version 1. Insert some data using SQL queries.
|
// Database has schema version 1. Insert some data using SQL queries.
|
||||||
// You can't use DAO classes because they expect the latest schema.
|
// You can't use DAO classes because they expect the latest schema.
|
||||||
execSQL(
|
execSQL(Queries.createDefaultSettings())
|
||||||
"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(
|
execSQL(
|
||||||
"INSERT INTO TunnelConfig (name, wg_quick)" + " VALUES ('hello', 'hello')",
|
"INSERT INTO TunnelConfig (name, wg_quick)" + " VALUES ('hello', 'hello')",
|
||||||
)
|
)
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ import android.content.Context
|
||||||
import androidx.room.Room
|
import androidx.room.Room
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
import com.zaneschepke.wireguardautotunnel.data.AppDatabase
|
import com.zaneschepke.wireguardautotunnel.data.AppDatabase
|
||||||
|
import com.zaneschepke.wireguardautotunnel.data.DatabaseCallback
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
import dagger.hilt.InstallIn
|
import dagger.hilt.InstallIn
|
||||||
|
@ -23,6 +24,7 @@ class DatabaseModule {
|
||||||
context.getString(R.string.db_name),
|
context.getString(R.string.db_name),
|
||||||
)
|
)
|
||||||
.fallbackToDestructiveMigration()
|
.fallbackToDestructiveMigration()
|
||||||
|
.addCallback(DatabaseCallback())
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import com.zaneschepke.wireguardautotunnel.data.repository.TunnelConfigRepositor
|
||||||
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager
|
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager
|
||||||
import com.zaneschepke.wireguardautotunnel.util.goAsync
|
import com.zaneschepke.wireguardautotunnel.util.goAsync
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
|
@ -21,8 +22,10 @@ class BootReceiver : BroadcastReceiver() {
|
||||||
if (Intent.ACTION_BOOT_COMPLETED != intent?.action) return@goAsync
|
if (Intent.ACTION_BOOT_COMPLETED != intent?.action) return@goAsync
|
||||||
val settings = settingsRepository.getSettings()
|
val settings = settingsRepository.getSettings()
|
||||||
if (settings.isAutoTunnelEnabled) {
|
if (settings.isAutoTunnelEnabled) {
|
||||||
|
Timber.i("Starting watcher service from boot")
|
||||||
ServiceManager.startWatcherServiceForeground(context!!)
|
ServiceManager.startWatcherServiceForeground(context!!)
|
||||||
} else if(settings.isAlwaysOnVpnEnabled) {
|
} else if(settings.isAlwaysOnVpnEnabled) {
|
||||||
|
Timber.i("Starting tunnel from boot")
|
||||||
ServiceManager.startVpnServicePrimaryTunnel(context!!, settings, tunnelConfigRepository.getAll().firstOrNull())
|
ServiceManager.startVpnServicePrimaryTunnel(context!!, settings, tunnelConfigRepository.getAll().firstOrNull())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -152,6 +152,7 @@ class WireGuardConnectivityWatcherService : ForegroundService() {
|
||||||
wakeLock =
|
wakeLock =
|
||||||
(getSystemService(Context.POWER_SERVICE) as PowerManager).run {
|
(getSystemService(Context.POWER_SERVICE) as PowerManager).run {
|
||||||
newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "$tag::lock").apply {
|
newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "$tag::lock").apply {
|
||||||
|
try {
|
||||||
if (isBatterySaverOn) {
|
if (isBatterySaverOn) {
|
||||||
Timber.d("Initiating wakelock with timeout")
|
Timber.d("Initiating wakelock with timeout")
|
||||||
acquire(Constants.BATTERY_SAVER_WATCHER_WAKE_LOCK_TIMEOUT)
|
acquire(Constants.BATTERY_SAVER_WATCHER_WAKE_LOCK_TIMEOUT)
|
||||||
|
@ -159,6 +160,9 @@ class WireGuardConnectivityWatcherService : ForegroundService() {
|
||||||
Timber.d("Initiating wakelock with zero timeout")
|
Timber.d("Initiating wakelock with zero timeout")
|
||||||
acquire(Constants.DEFAULT_WATCHER_WAKE_LOCK_TIMEOUT)
|
acquire(Constants.DEFAULT_WATCHER_WAKE_LOCK_TIMEOUT)
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
release()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
package com.zaneschepke.wireguardautotunnel.ui
|
package com.zaneschepke.wireguardautotunnel.ui
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import com.zaneschepke.wireguardautotunnel.data.SettingsDao
|
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class ActivityViewModel
|
class ActivityViewModel
|
||||||
@Inject
|
@Inject
|
||||||
constructor(
|
constructor() : ViewModel() {
|
||||||
private val settingsRepo: SettingsDao,
|
|
||||||
) : ViewModel() {}
|
}
|
||||||
|
|
|
@ -6,11 +6,13 @@ import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import androidx.activity.SystemBarStyle
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.enableEdgeToEdge
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.compose.foundation.focusable
|
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.MaterialTheme
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.SnackbarData
|
import androidx.compose.material3.SnackbarData
|
||||||
|
@ -20,13 +22,12 @@ import androidx.compose.material3.SnackbarHostState
|
||||||
import androidx.compose.material3.SnackbarResult
|
import androidx.compose.material3.SnackbarResult
|
||||||
import androidx.compose.material3.surfaceColorAtElevation
|
import androidx.compose.material3.surfaceColorAtElevation
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.focus.FocusRequester
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
import androidx.compose.ui.focus.focusProperties
|
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.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.navigation.compose.NavHost
|
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.ExperimentalPermissionsApi
|
||||||
import com.google.accompanist.permissions.isGranted
|
import com.google.accompanist.permissions.isGranted
|
||||||
import com.google.accompanist.permissions.rememberPermissionState
|
import com.google.accompanist.permissions.rememberPermissionState
|
||||||
import com.wireguard.android.backend.GoBackend
|
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
import com.zaneschepke.wireguardautotunnel.WireGuardAutoTunnel
|
import com.zaneschepke.wireguardautotunnel.WireGuardAutoTunnel
|
||||||
import com.zaneschepke.wireguardautotunnel.data.datastore.DataStoreManager
|
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.main.MainScreen
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.SettingsScreen
|
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.SettingsScreen
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.screens.support.SupportScreen
|
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.ui.theme.WireguardAutoTunnelTheme
|
||||||
import com.zaneschepke.wireguardautotunnel.util.Constants
|
import com.zaneschepke.wireguardautotunnel.util.Constants
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
@ -70,6 +69,8 @@ class MainActivity : AppCompatActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
enableEdgeToEdge(navigationBarStyle = SystemBarStyle.dark(Color.Transparent.toArgb()))
|
||||||
|
|
||||||
// load preferences into memory and init data
|
// load preferences into memory and init data
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
try {
|
try {
|
||||||
|
@ -86,7 +87,6 @@ class MainActivity : AppCompatActivity() {
|
||||||
val focusRequester = remember { FocusRequester() }
|
val focusRequester = remember { FocusRequester() }
|
||||||
|
|
||||||
WireguardAutoTunnelTheme {
|
WireguardAutoTunnelTheme {
|
||||||
TransparentSystemBars()
|
|
||||||
|
|
||||||
val snackbarHostState = remember { SnackbarHostState() }
|
val snackbarHostState = remember { SnackbarHostState() }
|
||||||
|
|
||||||
|
@ -134,9 +134,11 @@ class MainActivity : AppCompatActivity() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
modifier = Modifier.focusable().focusProperties { up = focusRequester },
|
modifier = Modifier
|
||||||
|
.focusable()
|
||||||
|
.focusProperties { up = focusRequester },
|
||||||
bottomBar =
|
bottomBar =
|
||||||
if (notificationPermissionState != null && notificationPermissionState.status.isGranted) {
|
if (notificationPermissionState == null || notificationPermissionState.status.isGranted) {
|
||||||
{
|
{
|
||||||
BottomNavBar(
|
BottomNavBar(
|
||||||
navController,
|
navController,
|
||||||
|
@ -151,16 +153,16 @@ class MainActivity : AppCompatActivity() {
|
||||||
{}
|
{}
|
||||||
},
|
},
|
||||||
) { padding ->
|
) { padding ->
|
||||||
|
Column(modifier = Modifier.padding(padding)) {
|
||||||
if (notificationPermissionState != null && !notificationPermissionState.status.isGranted) {
|
if (notificationPermissionState != null && !notificationPermissionState.status.isGranted) {
|
||||||
PermissionRequestFailedScreen(
|
PermissionRequestFailedScreen(
|
||||||
padding = padding,
|
|
||||||
onRequestAgain = {
|
onRequestAgain = {
|
||||||
val intentSettings =
|
val intentSettings =
|
||||||
Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
|
Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
|
||||||
intentSettings.data =
|
intentSettings.data =
|
||||||
Uri.fromParts(
|
Uri.fromParts(
|
||||||
Constants.URI_PACKAGE_SCHEME,
|
Constants.URI_PACKAGE_SCHEME,
|
||||||
this.packageName,
|
this@MainActivity.packageName,
|
||||||
null,
|
null,
|
||||||
)
|
)
|
||||||
startActivity(intentSettings)
|
startActivity(intentSettings)
|
||||||
|
@ -175,7 +177,6 @@ class MainActivity : AppCompatActivity() {
|
||||||
Screen.Main.route,
|
Screen.Main.route,
|
||||||
) {
|
) {
|
||||||
MainScreen(
|
MainScreen(
|
||||||
padding = padding,
|
|
||||||
focusRequester = focusRequester,
|
focusRequester = focusRequester,
|
||||||
showSnackbarMessage = { message -> showSnackBarMessage(message) },
|
showSnackbarMessage = { message -> showSnackBarMessage(message) },
|
||||||
navController = navController,
|
navController = navController,
|
||||||
|
@ -185,7 +186,6 @@ class MainActivity : AppCompatActivity() {
|
||||||
Screen.Settings.route,
|
Screen.Settings.route,
|
||||||
) {
|
) {
|
||||||
SettingsScreen(
|
SettingsScreen(
|
||||||
padding = padding,
|
|
||||||
showSnackbarMessage = { message -> showSnackBarMessage(message) },
|
showSnackbarMessage = { message -> showSnackBarMessage(message) },
|
||||||
focusRequester = focusRequester,
|
focusRequester = focusRequester,
|
||||||
)
|
)
|
||||||
|
@ -194,7 +194,6 @@ class MainActivity : AppCompatActivity() {
|
||||||
Screen.Support.route,
|
Screen.Support.route,
|
||||||
) {
|
) {
|
||||||
SupportScreen(
|
SupportScreen(
|
||||||
padding = padding,
|
|
||||||
focusRequester = focusRequester,
|
focusRequester = focusRequester,
|
||||||
showSnackbarMessage = { message -> showSnackBarMessage(message) },
|
showSnackbarMessage = { message -> showSnackBarMessage(message) },
|
||||||
)
|
)
|
||||||
|
@ -218,3 +217,4 @@ class MainActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ package com.zaneschepke.wireguardautotunnel.ui.common
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
|
@ -17,7 +16,6 @@ import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PermissionRequestFailedScreen(
|
fun PermissionRequestFailedScreen(
|
||||||
padding: PaddingValues,
|
|
||||||
onRequestAgain: () -> Unit,
|
onRequestAgain: () -> Unit,
|
||||||
message: String,
|
message: String,
|
||||||
buttonText: String
|
buttonText: String
|
||||||
|
@ -26,7 +24,7 @@ fun PermissionRequestFailedScreen(
|
||||||
Column(
|
Column(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
verticalArrangement = Arrangement.Center,
|
verticalArrangement = Arrangement.Center,
|
||||||
modifier = Modifier.fillMaxSize().padding(padding),
|
modifier = Modifier.fillMaxSize(),
|
||||||
) {
|
) {
|
||||||
Text(message, textAlign = TextAlign.Center, modifier = Modifier.padding(15.dp))
|
Text(message, textAlign = TextAlign.Center, modifier = Modifier.padding(15.dp))
|
||||||
Button(
|
Button(
|
||||||
|
|
|
@ -19,7 +19,6 @@ import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.LayoutDirection
|
import androidx.compose.ui.unit.LayoutDirection
|
||||||
|
@ -33,7 +32,6 @@ fun CustomSnackBar(
|
||||||
isRtl: Boolean = true,
|
isRtl: Boolean = true,
|
||||||
containerColor: Color = MaterialTheme.colorScheme.surface
|
containerColor: Color = MaterialTheme.colorScheme.surface
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
|
||||||
Snackbar(
|
Snackbar(
|
||||||
containerColor = containerColor,
|
containerColor = containerColor,
|
||||||
modifier =
|
modifier =
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package com.zaneschepke.wireguardautotunnel.ui.screens.config
|
package com.zaneschepke.wireguardautotunnel.ui.screens.config
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.focusGroup
|
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.Delete
|
||||||
import androidx.compose.material.icons.rounded.Refresh
|
import androidx.compose.material.icons.rounded.Refresh
|
||||||
import androidx.compose.material.icons.rounded.Save
|
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.Checkbox
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.FabPosition
|
import androidx.compose.material3.FabPosition
|
||||||
|
@ -51,7 +50,6 @@ import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.focus.FocusRequester
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
import androidx.compose.ui.focus.focusRequester
|
import androidx.compose.ui.focus.focusRequester
|
||||||
|
@ -86,9 +84,7 @@ import kotlinx.coroutines.delay
|
||||||
|
|
||||||
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
|
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
|
||||||
@OptIn(
|
@OptIn(
|
||||||
ExperimentalComposeUiApi::class,
|
|
||||||
ExperimentalMaterial3Api::class,
|
ExperimentalMaterial3Api::class,
|
||||||
ExperimentalFoundationApi::class,
|
|
||||||
)
|
)
|
||||||
@Composable
|
@Composable
|
||||||
fun ConfigScreen(
|
fun ConfigScreen(
|
||||||
|
@ -145,7 +141,7 @@ fun ConfigScreen(
|
||||||
showAuthPrompt = false
|
showAuthPrompt = false
|
||||||
isAuthenticated = true
|
isAuthenticated = true
|
||||||
},
|
},
|
||||||
onError = { error ->
|
onError = {
|
||||||
showAuthPrompt = false
|
showAuthPrompt = false
|
||||||
showSnackbarMessage(Event.Error.AuthenticationFailed.message)
|
showSnackbarMessage(Event.Error.AuthenticationFailed.message)
|
||||||
},
|
},
|
||||||
|
@ -161,7 +157,7 @@ fun ConfigScreen(
|
||||||
remember(uiState.packages) {
|
remember(uiState.packages) {
|
||||||
uiState.packages.sortedBy { viewModel.getPackageLabel(it) }
|
uiState.packages.sortedBy { viewModel.getPackageLabel(it) }
|
||||||
}
|
}
|
||||||
AlertDialog(onDismissRequest = { showApplicationsDialog = false }) {
|
BasicAlertDialog(onDismissRequest = { showApplicationsDialog = false }) {
|
||||||
Surface(
|
Surface(
|
||||||
tonalElevation = 2.dp,
|
tonalElevation = 2.dp,
|
||||||
shadowElevation = 2.dp,
|
shadowElevation = 2.dp,
|
||||||
|
@ -299,7 +295,7 @@ fun ConfigScreen(
|
||||||
var fobColor by remember { mutableStateOf(secondaryColor) }
|
var fobColor by remember { mutableStateOf(secondaryColor) }
|
||||||
FloatingActionButton(
|
FloatingActionButton(
|
||||||
modifier =
|
modifier =
|
||||||
Modifier.padding(bottom = 90.dp).onFocusChanged {
|
Modifier.onFocusChanged {
|
||||||
if (WireGuardAutoTunnel.isRunningOnAndroidTv()) {
|
if (WireGuardAutoTunnel.isRunningOnAndroidTv()) {
|
||||||
fobColor = if (it.isFocused) hoverColor else secondaryColor
|
fobColor = if (it.isFocused) hoverColor else secondaryColor
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package com.zaneschepke.wireguardautotunnel.ui.screens.main
|
package com.zaneschepke.wireguardautotunnel.ui.screens.main
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
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.gestures.detectTapGestures
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
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.Info
|
||||||
import androidx.compose.material.icons.rounded.Star
|
import androidx.compose.material.icons.rounded.Star
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.Divider
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.FabPosition
|
import androidx.compose.material3.FabPosition
|
||||||
import androidx.compose.material3.FloatingActionButton
|
import androidx.compose.material3.FloatingActionButton
|
||||||
|
import androidx.compose.material3.HorizontalDivider
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
@ -110,12 +108,10 @@ import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)
|
@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun MainScreen(
|
fun MainScreen(
|
||||||
viewModel: MainViewModel = hiltViewModel(),
|
viewModel: MainViewModel = hiltViewModel(),
|
||||||
padding: PaddingValues,
|
|
||||||
focusRequester: FocusRequester,
|
focusRequester: FocusRequester,
|
||||||
showSnackbarMessage: (String) -> Unit,
|
showSnackbarMessage: (String) -> Unit,
|
||||||
navController: NavController
|
navController: NavController
|
||||||
|
@ -344,7 +340,6 @@ fun MainScreen(
|
||||||
)
|
)
|
||||||
Modifier.focusRequester(focusRequester)
|
Modifier.focusRequester(focusRequester)
|
||||||
else Modifier)
|
else Modifier)
|
||||||
.padding(bottom = 90.dp)
|
|
||||||
.onFocusChanged {
|
.onFocusChanged {
|
||||||
if (WireGuardAutoTunnel.isRunningOnAndroidTv()) {
|
if (WireGuardAutoTunnel.isRunningOnAndroidTv()) {
|
||||||
fobColor = if (it.isFocused) hoverColor else secondaryColor
|
fobColor = if (it.isFocused) hoverColor else secondaryColor
|
||||||
|
@ -367,7 +362,7 @@ fun MainScreen(
|
||||||
Column(
|
Column(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
verticalArrangement = Arrangement.Center,
|
verticalArrangement = Arrangement.Center,
|
||||||
modifier = Modifier.fillMaxSize().padding(padding),
|
modifier = Modifier.fillMaxSize().padding(innerPadding),
|
||||||
) {
|
) {
|
||||||
Text(text = stringResource(R.string.no_tunnels), fontStyle = FontStyle.Italic)
|
Text(text = stringResource(R.string.no_tunnels), fontStyle = FontStyle.Italic)
|
||||||
}
|
}
|
||||||
|
@ -398,7 +393,7 @@ fun MainScreen(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (!WireGuardAutoTunnel.isRunningOnAndroidTv()) {
|
if (!WireGuardAutoTunnel.isRunningOnAndroidTv()) {
|
||||||
Divider()
|
HorizontalDivider()
|
||||||
Row(
|
Row(
|
||||||
modifier =
|
modifier =
|
||||||
Modifier.fillMaxWidth()
|
Modifier.fillMaxWidth()
|
||||||
|
@ -430,7 +425,7 @@ fun MainScreen(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Divider()
|
HorizontalDivider()
|
||||||
Row(
|
Row(
|
||||||
modifier =
|
modifier =
|
||||||
Modifier.fillMaxWidth()
|
Modifier.fillMaxWidth()
|
||||||
|
@ -461,8 +456,7 @@ fun MainScreen(
|
||||||
modifier =
|
modifier =
|
||||||
Modifier.fillMaxWidth()
|
Modifier.fillMaxWidth()
|
||||||
.fillMaxHeight(.90f)
|
.fillMaxHeight(.90f)
|
||||||
.overscroll(ScrollableDefaults.overscrollEffect())
|
.overscroll(ScrollableDefaults.overscrollEffect()),
|
||||||
.padding(innerPadding),
|
|
||||||
state = rememberLazyListState(0, uiState.tunnels.count()),
|
state = rememberLazyListState(0, uiState.tunnels.count()),
|
||||||
userScrollEnabled = true,
|
userScrollEnabled = true,
|
||||||
reverseLayout = true,
|
reverseLayout = true,
|
||||||
|
|
|
@ -20,7 +20,6 @@ import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||||
import androidx.compose.foundation.layout.FlowRow
|
import androidx.compose.foundation.layout.FlowRow
|
||||||
import androidx.compose.foundation.layout.IntrinsicSize
|
import androidx.compose.foundation.layout.IntrinsicSize
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
@ -95,7 +94,6 @@ import java.io.File
|
||||||
@Composable
|
@Composable
|
||||||
fun SettingsScreen(
|
fun SettingsScreen(
|
||||||
viewModel: SettingsViewModel = hiltViewModel(),
|
viewModel: SettingsViewModel = hiltViewModel(),
|
||||||
padding: PaddingValues,
|
|
||||||
showSnackbarMessage: (String) -> Unit,
|
showSnackbarMessage: (String) -> Unit,
|
||||||
focusRequester: FocusRequester
|
focusRequester: FocusRequester
|
||||||
) {
|
) {
|
||||||
|
@ -127,7 +125,7 @@ fun SettingsScreen(
|
||||||
rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||||
result: ActivityResult ->
|
result: ActivityResult ->
|
||||||
if (result.resultCode == Activity.RESULT_OK) {
|
if (result.resultCode == Activity.RESULT_OK) {
|
||||||
val intent = result.data
|
result.data
|
||||||
// Handle the Intent
|
// Handle the Intent
|
||||||
}
|
}
|
||||||
viewModel.setBatteryOptimizeDisableShown()
|
viewModel.setBatteryOptimizeDisableShown()
|
||||||
|
@ -248,7 +246,7 @@ fun SettingsScreen(
|
||||||
Column(
|
Column(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
verticalArrangement = Arrangement.Top,
|
verticalArrangement = Arrangement.Top,
|
||||||
modifier = Modifier.fillMaxSize().verticalScroll(scrollState).padding(padding),
|
modifier = Modifier.fillMaxSize().verticalScroll(scrollState),
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
Icons.Rounded.LocationOff,
|
Icons.Rounded.LocationOff,
|
||||||
|
@ -314,7 +312,7 @@ fun SettingsScreen(
|
||||||
Column(
|
Column(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
verticalArrangement = Arrangement.Center,
|
verticalArrangement = Arrangement.Center,
|
||||||
modifier = Modifier.fillMaxSize().padding(padding),
|
modifier = Modifier.fillMaxSize(),
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
stringResource(R.string.one_tunnel_required),
|
stringResource(R.string.one_tunnel_required),
|
||||||
|
@ -347,7 +345,7 @@ fun SettingsScreen(
|
||||||
.fillMaxWidth(fillMaxWidth)
|
.fillMaxWidth(fillMaxWidth)
|
||||||
.padding(top = 10.dp)
|
.padding(top = 10.dp)
|
||||||
} else {
|
} else {
|
||||||
Modifier.fillMaxWidth(fillMaxWidth).padding(top = 60.dp)
|
Modifier.fillMaxWidth(fillMaxWidth).padding(top = 20.dp)
|
||||||
})
|
})
|
||||||
.padding(bottom = 10.dp),
|
.padding(bottom = 10.dp),
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -8,7 +8,6 @@ import androidx.compose.foundation.focusable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.IntrinsicSize
|
import androidx.compose.foundation.layout.IntrinsicSize
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
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.shape.RoundedCornerShape
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
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.Book
|
||||||
import androidx.compose.material.icons.rounded.Mail
|
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.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
|
@ -58,7 +57,6 @@ import com.zaneschepke.wireguardautotunnel.util.Event
|
||||||
@Composable
|
@Composable
|
||||||
fun SupportScreen(
|
fun SupportScreen(
|
||||||
viewModel: SupportViewModel = hiltViewModel(),
|
viewModel: SupportViewModel = hiltViewModel(),
|
||||||
padding: PaddingValues,
|
|
||||||
showSnackbarMessage: (String) -> Unit,
|
showSnackbarMessage: (String) -> Unit,
|
||||||
focusRequester: FocusRequester
|
focusRequester: FocusRequester
|
||||||
) {
|
) {
|
||||||
|
@ -107,7 +105,6 @@ fun SupportScreen(
|
||||||
Modifier.fillMaxSize()
|
Modifier.fillMaxSize()
|
||||||
.verticalScroll(rememberScrollState())
|
.verticalScroll(rememberScrollState())
|
||||||
.focusable()
|
.focusable()
|
||||||
.padding(padding),
|
|
||||||
) {
|
) {
|
||||||
Surface(
|
Surface(
|
||||||
tonalElevation = 2.dp,
|
tonalElevation = 2.dp,
|
||||||
|
@ -155,10 +152,16 @@ fun SupportScreen(
|
||||||
modifier = Modifier.padding(start = 10.dp),
|
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(
|
TextButton(
|
||||||
onClick = { openWebPage(context.resources.getString(R.string.discord_url)) },
|
onClick = { openWebPage(context.resources.getString(R.string.discord_url)) },
|
||||||
modifier = Modifier.padding(vertical = 5.dp),
|
modifier = Modifier.padding(vertical = 5.dp),
|
||||||
|
@ -180,10 +183,16 @@ fun SupportScreen(
|
||||||
modifier = Modifier.padding(start = 10.dp),
|
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(
|
TextButton(
|
||||||
onClick = { openWebPage(context.resources.getString(R.string.github_url)) },
|
onClick = { openWebPage(context.resources.getString(R.string.github_url)) },
|
||||||
modifier = Modifier.padding(vertical = 5.dp),
|
modifier = Modifier.padding(vertical = 5.dp),
|
||||||
|
@ -205,10 +214,16 @@ fun SupportScreen(
|
||||||
modifier = Modifier.padding(start = 10.dp),
|
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(
|
TextButton(
|
||||||
onClick = { launchEmail() },
|
onClick = { launchEmail() },
|
||||||
modifier = Modifier.padding(vertical = 5.dp),
|
modifier = Modifier.padding(vertical = 5.dp),
|
||||||
|
@ -226,7 +241,10 @@ fun SupportScreen(
|
||||||
modifier = Modifier.padding(start = 10.dp),
|
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)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -140,7 +140,7 @@
|
||||||
<string name="exported_configs_message">Exported configs to downloads</string>
|
<string name="exported_configs_message">Exported configs to downloads</string>
|
||||||
<string name="status">status</string>
|
<string name="status">status</string>
|
||||||
<string name="tunnel_on_wifi">Tunnel on untrusted wifi</string>
|
<string name="tunnel_on_wifi">Tunnel on untrusted wifi</string>
|
||||||
<string name="my_email" translatable="false">zanecschepke@gmail.com</string>
|
<string name="my_email" translatable="false">support@zaneschepke.com</string>
|
||||||
<string name="email_subject">WG Tunnel Support</string>
|
<string name="email_subject">WG Tunnel Support</string>
|
||||||
<string name="email_chooser">Send an email…</string>
|
<string name="email_chooser">Send an email…</string>
|
||||||
<string name="go">go</string>
|
<string name="go">go</string>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import org.gradle.api.invocation.Gradle
|
import org.gradle.api.invocation.Gradle
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
object BuildHelper {
|
object BuildHelper {
|
||||||
private fun getCurrentFlavor(gradle: Gradle): String {
|
private fun getCurrentFlavor(gradle: Gradle): String {
|
||||||
|
@ -21,6 +22,17 @@ object BuildHelper {
|
||||||
return flavor
|
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 {
|
fun isGeneralFlavor(gradle: Gradle): Boolean {
|
||||||
return getCurrentFlavor(gradle) == "general"
|
return getCurrentFlavor(gradle) == "general"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
object Constants {
|
object Constants {
|
||||||
const val VERSION_NAME = "3.3.5"
|
const val VERSION_NAME = "3.3.6"
|
||||||
const val JVM_TARGET = "17"
|
const val JVM_TARGET = "17"
|
||||||
const val VERSION_CODE = 33500
|
const val VERSION_CODE = 33600
|
||||||
const val TARGET_SDK = 34
|
const val TARGET_SDK = 34
|
||||||
const val MIN_SDK = 26
|
const val MIN_SDK = 26
|
||||||
const val APP_ID = "com.zaneschepke.wireguardautotunnel"
|
const val APP_ID = "com.zaneschepke.wireguardautotunnel"
|
||||||
|
|
|
@ -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
|
|
@ -10,35 +10,35 @@ datastorePreferences = "1.0.0"
|
||||||
desugar_jdk_libs = "2.0.4"
|
desugar_jdk_libs = "2.0.4"
|
||||||
espressoCore = "3.5.1"
|
espressoCore = "3.5.1"
|
||||||
firebase-crashlytics-gradle = "2.9.9"
|
firebase-crashlytics-gradle = "2.9.9"
|
||||||
google-services = "4.4.0"
|
google-services = "4.4.1"
|
||||||
hiltAndroid = "2.50"
|
hiltAndroid = "2.50"
|
||||||
hiltNavigationCompose = "1.1.0"
|
hiltNavigationCompose = "1.1.0"
|
||||||
junit = "4.13.2"
|
junit = "4.13.2"
|
||||||
kotlinx-serialization-json = "1.6.2"
|
kotlinx-serialization-json = "1.6.2"
|
||||||
lifecycle-runtime-compose = "2.7.0"
|
lifecycle-runtime-compose = "2.7.0"
|
||||||
material3 = "1.2.0-rc01"
|
material3 = "1.2.0"
|
||||||
navigationCompose = "2.7.6"
|
navigationCompose = "2.7.7"
|
||||||
roomVersion = "2.6.1"
|
roomVersion = "2.6.1"
|
||||||
timber = "5.0.1"
|
timber = "5.0.1"
|
||||||
tunnel = "1.0.20230706"
|
tunnel = "1.1.0"
|
||||||
androidGradlePlugin = "8.3.0-rc01"
|
androidGradlePlugin = "8.3.0-rc02"
|
||||||
kotlin = "1.9.22"
|
kotlin = "1.9.22"
|
||||||
ksp = "1.9.22-1.0.16"
|
ksp = "1.9.22-1.0.16"
|
||||||
composeBom = "2024.01.00"
|
composeBom = "2024.02.00"
|
||||||
firebaseBom = "32.7.1"
|
firebaseBom = "32.7.2"
|
||||||
compose = "1.6.0"
|
compose = "1.6.1"
|
||||||
crashlytics = "18.6.1"
|
crashlytics = "18.6.2"
|
||||||
analytics = "21.5.0"
|
analytics = "21.5.1"
|
||||||
zxingAndroidEmbedded = "4.3.0"
|
zxingAndroidEmbedded = "4.3.0"
|
||||||
zxingCore = "3.5.2"
|
zxingCore = "3.5.3"
|
||||||
|
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
|
|
||||||
# accompanist
|
# accompanist
|
||||||
accompanist-drawablepainter = { module = "com.google.accompanist:accompanist-drawablepainter", version.ref = "accompanist" }
|
accompanist-drawablepainter = { module = "com.google.accompanist:accompanist-drawablepainter", version.ref = "accompanist" }
|
||||||
accompanist-flowlayout = { module = "com.google.accompanist:accompanist-flowlayout", 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-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanist" }
|
||||||
accompanist-systemuicontroller = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "accompanist" }
|
|
||||||
|
|
||||||
#room
|
#room
|
||||||
androidx-biometric-ktx = { module = "androidx.biometric:biometric-ktx", version.ref = "biometricKtx" }
|
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" }
|
material-icons-extended = { module = "androidx.compose.material:material-icons-extended", version.ref = "compose" }
|
||||||
|
|
||||||
timber = { module = "com.jakewharton.timber:timber", version.ref = "timber" }
|
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
|
#firebase
|
||||||
google-firebase-crashlytics-ktx = { module = "com.google.firebase:firebase-crashlytics-ktx", version.ref = "crashlytics" }
|
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-core = { module = "com.google.zxing:core", version.ref = "zxingCore" }
|
||||||
zxing-android-embedded = { module = "com.journeyapps:zxing-android-embedded", version.ref = "zxingAndroidEmbedded" }
|
zxing-android-embedded = { module = "com.journeyapps:zxing-android-embedded", version.ref = "zxingAndroidEmbedded" }
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" }
|
android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" }
|
||||||
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||||
hilt-android = { id = "com.google.dagger.hilt.android", version.ref = "hiltAndroid" }
|
hilt-android = { id = "com.google.dagger.hilt.android", version.ref = "hiltAndroid" }
|
||||||
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
|
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
|
||||||
|
androidLibrary = { id = "com.android.library", version.ref = "androidGradlePlugin" }
|
|
@ -2,5 +2,7 @@
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
|
||||||
|
distributionSha256Sum=3e1af3ae886920c3ac87f7a91f816c0c7c436f276a6eefdb3da152100fef72ae
|
||||||
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|
|
@ -2,18 +2,42 @@ pluginManagement {
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
mavenLocal()
|
||||||
gradlePluginPortal()
|
gradlePluginPortal()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val GITHUB_USER_VAR = "GH_USER"
|
||||||
|
val GITHUB_TOKEN_VAR = "GH_TOKEN"
|
||||||
|
|
||||||
dependencyResolutionManagement {
|
dependencyResolutionManagement {
|
||||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||||
repositories {
|
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()
|
google()
|
||||||
mavenCentral()
|
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"
|
rootProject.name = "WG Tunnel"
|
||||||
|
|
||||||
include(":app")
|
include(":app")
|
||||||
|
|
Loading…
Reference in New Issue