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" }