diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index fb52e205..d9d7bbf9 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -172,8 +172,7 @@ dependencies {
debugImplementation(libs.androidx.compose.ui.tooling)
debugImplementation(libs.androidx.compose.manifest)
- // get tunnel lib from github packages or mavenLocal
-// implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.aar"))))
+ // tunnel
implementation(libs.tunnel)
implementation(libs.amneziawg.android)
coreLibraryDesugaring(libs.desugar.jdk.libs)
@@ -188,6 +187,7 @@ dependencies {
// hilt
implementation(libs.hilt.android)
ksp(libs.hilt.android.compiler)
+ ksp(libs.androidx.hilt.compiler)
// accompanist
implementation(libs.accompanist.permissions)
@@ -221,6 +221,10 @@ dependencies {
// splash
implementation(libs.androidx.core.splashscreen)
+
+ // worker
+ implementation(libs.androidx.work.runtime)
+ implementation(libs.androidx.hilt.work)
}
fun determineVersionName(): String {
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 92b3e62e..abd9b7f4 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -106,6 +106,13 @@
android:resource="@xml/file_paths" />
+
+
+
WireguardAutoTunnelTheme(theme = appUiState.generalState.theme) {
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/WireGuardAutoTunnel.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/WireGuardAutoTunnel.kt
index 935abd9c..38b9b027 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/WireGuardAutoTunnel.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/WireGuardAutoTunnel.kt
@@ -3,9 +3,11 @@ package com.zaneschepke.wireguardautotunnel
import android.app.Application
import android.os.StrictMode
import android.os.StrictMode.ThreadPolicy
+import androidx.hilt.work.HiltWorkerFactory
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ProcessLifecycleOwner
+import androidx.work.Configuration
import com.wireguard.android.backend.GoBackend
import com.zaneschepke.logcatter.LogReader
import com.zaneschepke.wireguardautotunnel.core.tunnel.TunnelManager
@@ -26,7 +28,15 @@ import timber.log.Timber
import javax.inject.Inject
@HiltAndroidApp
-class WireGuardAutoTunnel : Application() {
+class WireGuardAutoTunnel : Application(), Configuration.Provider {
+
+ @Inject
+ lateinit var workerFactory: HiltWorkerFactory
+
+ override val workManagerConfiguration: Configuration
+ get() = Configuration.Builder()
+ .setWorkerFactory(workerFactory)
+ .build()
@Inject
@ApplicationScope
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/broadcast/AppUpdateReceiver.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/broadcast/AppUpdateReceiver.kt
index 893b20b8..245c2b7c 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/broadcast/AppUpdateReceiver.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/broadcast/AppUpdateReceiver.kt
@@ -36,7 +36,7 @@ class AppUpdateReceiver : BroadcastReceiver() {
with(appDataRepository.settings.get()) {
if (isRestoreOnBootEnabled) {
// If auto tunnel is enabled, just start it and let auto tunnel start appropriate tun
- if (isAutoTunnelEnabled) return@launch serviceManager.startAutoTunnel(true)
+ if (isAutoTunnelEnabled && !serviceManager.autoTunnelActive.value) return@launch serviceManager.startAutoTunnel(true)
tunnelManager.restorePreviousState()
}
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/broadcast/BootReceiver.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/broadcast/BootReceiver.kt
index 3248ce0e..36dc82f5 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/broadcast/BootReceiver.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/broadcast/BootReceiver.kt
@@ -35,7 +35,7 @@ class BootReceiver : BroadcastReceiver() {
with(appDataRepository.settings.get()) {
if (isRestoreOnBootEnabled) {
// If auto tunnel is enabled, just start it and let auto tunnel start appropriate tun
- if (isAutoTunnelEnabled) return@launch serviceManager.startAutoTunnel(true)
+ if (isAutoTunnelEnabled && !serviceManager.autoTunnelActive.value) return@launch serviceManager.startAutoTunnel(true)
tunnelManager.restorePreviousState()
}
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/worker/ServiceWorker.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/worker/ServiceWorker.kt
new file mode 100644
index 00000000..87bc8d04
--- /dev/null
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/worker/ServiceWorker.kt
@@ -0,0 +1,60 @@
+package com.zaneschepke.wireguardautotunnel.core.worker
+
+import android.content.Context
+import androidx.hilt.work.HiltWorker
+import androidx.work.CoroutineWorker
+import androidx.work.ExistingPeriodicWorkPolicy
+import androidx.work.PeriodicWorkRequestBuilder
+import androidx.work.WorkManager
+import androidx.work.WorkerParameters
+import com.zaneschepke.wireguardautotunnel.core.service.ServiceManager
+import com.zaneschepke.wireguardautotunnel.core.tunnel.TunnelManager
+import com.zaneschepke.wireguardautotunnel.di.IoDispatcher
+import com.zaneschepke.wireguardautotunnel.domain.repository.AppDataRepository
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.withContext
+import timber.log.Timber
+import java.util.concurrent.TimeUnit
+
+@HiltWorker
+class ServiceWorker @AssistedInject constructor(
+ @Assisted private val context: Context,
+ @Assisted private val params: WorkerParameters,
+ private val serviceManager: ServiceManager,
+ private val appDataRepository: AppDataRepository,
+ @IoDispatcher private val ioDispatcher: CoroutineDispatcher,
+ private val tunnelManager: TunnelManager,
+) : CoroutineWorker(context, params) {
+
+ companion object {
+ private const val TAG = "service_worker"
+
+ fun stop(context: Context) {
+ WorkManager.getInstance(context).cancelAllWorkByTag(TAG)
+ }
+
+ fun start(context: Context) {
+ val periodicWorkRequest = PeriodicWorkRequestBuilder(
+ repeatInterval = 15,
+ repeatIntervalTimeUnit = TimeUnit.MINUTES,
+ ).build()
+ WorkManager.getInstance(context)
+ .enqueueUniquePeriodicWork(
+ TAG,
+ ExistingPeriodicWorkPolicy.KEEP,
+ periodicWorkRequest,
+ )
+ }
+ }
+
+ override suspend fun doWork(): Result = withContext(ioDispatcher) {
+ Timber.i("Service worker started")
+ with(appDataRepository.settings.get()) {
+ if (isAutoTunnelEnabled && !serviceManager.autoTunnelActive.value) return@with serviceManager.startAutoTunnel(true)
+ if (tunnelManager.activeTunnels().value.isEmpty()) tunnelManager.restorePreviousState()
+ }
+ Result.success()
+ }
+}
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index a0072372..48c2a969 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -10,7 +10,7 @@ datastorePreferences = "1.1.2"
desugar_jdk_libs = "2.1.4"
espressoCore = "3.6.1"
hiltAndroid = "2.55"
-hiltNavigationCompose = "1.2.0"
+hiltCompiler = "1.2.0"
junit = "4.13.2"
kotlinx-serialization-json = "1.8.0"
lifecycle-runtime-compose = "2.8.7"
@@ -25,6 +25,7 @@ kotlin = "2.1.10"
ksp = "2.1.10-1.0.30"
composeBom = "2025.02.00"
compose = "1.7.8"
+workRuntimeKtxVersion = "2.10.0"
zxingAndroidEmbedded = "4.3.0"
coreSplashscreen = "1.0.1"
gradlePlugins-grgit = "5.3.0"
@@ -45,6 +46,7 @@ amneziawg-android = { module = "com.zaneschepke:amneziawg-android", version.ref
androidx-biometric-ktx = { module = "androidx.biometric:biometric-ktx", version.ref = "biometricKtx" }
androidx-core = { module = "androidx.core:core", version.ref = "coreKtx" }
androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastorePreferences" }
+androidx-hilt-work = { module = "androidx.hilt:hilt-work", version.ref = "hiltCompiler" }
androidx-lifecycle-process = { module = "androidx.lifecycle:lifecycle-process", version.ref = "lifecycle-runtime-compose" }
androidx-lifecycle-service = { module = "androidx.lifecycle:lifecycle-service", version.ref = "lifecycle-runtime-compose" }
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "roomVersion" }
@@ -62,15 +64,17 @@ androidx-compose-ui = { module = "androidx.compose.ui:ui", version.ref = "compos
#hilt
androidx-room-testing = { module = "androidx.room:room-testing", version.ref = "roomVersion" }
+androidx-work-runtime = { module = "androidx.work:work-runtime-ktx", version.ref = "workRuntimeKtxVersion" }
desugar_jdk_libs = { module = "com.android.tools:desugar_jdk_libs", version.ref = "desugar_jdk_libs" }
hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hiltAndroid" }
hilt-android-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hiltAndroid" }
+androidx-hilt-compiler = { module = "androidx.hilt:hilt-compiler", version.ref = "hiltCompiler" }
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "activityCompose" }
androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" }
androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "coreKtx" }
androidx-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "espressoCore" }
-androidx-hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version.ref = "hiltNavigationCompose" }
+androidx-hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version.ref = "hiltCompiler" }
androidx-junit = { module = "androidx.test.ext:junit", version.ref = "androidx-junit" }
androidx-core-splashscreen = { module = "androidx.core:core-splashscreen", version.ref = "coreSplashscreen" }
androidx-lifecycle-runtime-ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "lifecycle-runtime-compose" }