refactor state
This commit is contained in:
parent
408d88390b
commit
a3386552d5
|
@ -3,7 +3,7 @@ package com.zaneschepke.wireguardautotunnel
|
||||||
import androidx.room.testing.MigrationTestHelper
|
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.repository.AppDatabase
|
import com.zaneschepke.wireguardautotunnel.data.AppDatabase
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
|
@ -29,3 +29,4 @@ fun BigDecimal.toThreeDecimalPlaceString(): String {
|
||||||
val df = DecimalFormat("#.###")
|
val df = DecimalFormat("#.###")
|
||||||
return df.format(this)
|
return df.format(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,9 +5,9 @@ import android.content.Context
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import androidx.lifecycle.ProcessLifecycleOwner
|
import androidx.lifecycle.ProcessLifecycleOwner
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.SettingsDoa
|
import com.zaneschepke.wireguardautotunnel.data.SettingsDao
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.datastore.DataStoreManager
|
import com.zaneschepke.wireguardautotunnel.data.datastore.DataStoreManager
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.model.Settings
|
import com.zaneschepke.wireguardautotunnel.data.model.Settings
|
||||||
import dagger.hilt.android.HiltAndroidApp
|
import dagger.hilt.android.HiltAndroidApp
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -17,7 +17,7 @@ import timber.log.Timber
|
||||||
@HiltAndroidApp
|
@HiltAndroidApp
|
||||||
class WireGuardAutoTunnel : Application() {
|
class WireGuardAutoTunnel : Application() {
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var settingsRepo: SettingsDoa
|
lateinit var settingsRepo: SettingsDao
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var dataStoreManager: DataStoreManager
|
lateinit var dataStoreManager: DataStoreManager
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
package com.zaneschepke.wireguardautotunnel.repository
|
package com.zaneschepke.wireguardautotunnel.data
|
||||||
|
|
||||||
import androidx.room.AutoMigration
|
import androidx.room.AutoMigration
|
||||||
import androidx.room.Database
|
import androidx.room.Database
|
||||||
import androidx.room.RoomDatabase
|
import androidx.room.RoomDatabase
|
||||||
import androidx.room.TypeConverters
|
import androidx.room.TypeConverters
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.model.Settings
|
import com.zaneschepke.wireguardautotunnel.data.model.Settings
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.model.TunnelConfig
|
import com.zaneschepke.wireguardautotunnel.data.model.TunnelConfig
|
||||||
|
|
||||||
@Database(
|
@Database(
|
||||||
entities = [Settings::class, TunnelConfig::class],
|
entities = [Settings::class, TunnelConfig::class],
|
||||||
|
@ -20,7 +20,7 @@ import com.zaneschepke.wireguardautotunnel.repository.model.TunnelConfig
|
||||||
)
|
)
|
||||||
@TypeConverters(DatabaseListConverters::class)
|
@TypeConverters(DatabaseListConverters::class)
|
||||||
abstract class AppDatabase : RoomDatabase() {
|
abstract class AppDatabase : RoomDatabase() {
|
||||||
abstract fun settingDao(): SettingsDoa
|
abstract fun settingDao(): SettingsDao
|
||||||
|
|
||||||
abstract fun tunnelConfigDoa(): TunnelConfigDao
|
abstract fun tunnelConfigDoa(): TunnelConfigDao
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package com.zaneschepke.wireguardautotunnel.repository
|
package com.zaneschepke.wireguardautotunnel.data
|
||||||
|
|
||||||
import androidx.room.TypeConverter
|
import androidx.room.TypeConverter
|
||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.encodeToString
|
|
@ -1,15 +1,15 @@
|
||||||
package com.zaneschepke.wireguardautotunnel.repository
|
package com.zaneschepke.wireguardautotunnel.data
|
||||||
|
|
||||||
import androidx.room.Dao
|
import androidx.room.Dao
|
||||||
import androidx.room.Delete
|
import androidx.room.Delete
|
||||||
import androidx.room.Insert
|
import androidx.room.Insert
|
||||||
import androidx.room.OnConflictStrategy
|
import androidx.room.OnConflictStrategy
|
||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.model.Settings
|
import com.zaneschepke.wireguardautotunnel.data.model.Settings
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface SettingsDoa {
|
interface SettingsDao {
|
||||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
suspend fun save(t: Settings)
|
suspend fun save(t: Settings)
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
package com.zaneschepke.wireguardautotunnel.repository
|
package com.zaneschepke.wireguardautotunnel.data
|
||||||
|
|
||||||
import androidx.room.Dao
|
import androidx.room.Dao
|
||||||
import androidx.room.Delete
|
import androidx.room.Delete
|
||||||
import androidx.room.Insert
|
import androidx.room.Insert
|
||||||
import androidx.room.OnConflictStrategy
|
import androidx.room.OnConflictStrategy
|
||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.model.TunnelConfig
|
import com.zaneschepke.wireguardautotunnel.data.model.TunnelConfig
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
@Dao
|
@Dao
|
|
@ -1,4 +1,4 @@
|
||||||
package com.zaneschepke.wireguardautotunnel.repository.datastore
|
package com.zaneschepke.wireguardautotunnel.data.datastore
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.datastore.preferences.core.Preferences
|
import androidx.datastore.preferences.core.Preferences
|
||||||
import androidx.datastore.preferences.core.booleanPreferencesKey
|
import androidx.datastore.preferences.core.booleanPreferencesKey
|
|
@ -1,4 +1,4 @@
|
||||||
package com.zaneschepke.wireguardautotunnel.repository.model
|
package com.zaneschepke.wireguardautotunnel.data.model
|
||||||
|
|
||||||
import androidx.room.ColumnInfo
|
import androidx.room.ColumnInfo
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
|
@ -1,4 +1,4 @@
|
||||||
package com.zaneschepke.wireguardautotunnel.repository.model
|
package com.zaneschepke.wireguardautotunnel.data.model
|
||||||
|
|
||||||
import androidx.room.ColumnInfo
|
import androidx.room.ColumnInfo
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
|
@ -0,0 +1,11 @@
|
||||||
|
package com.zaneschepke.wireguardautotunnel.data.repository
|
||||||
|
|
||||||
|
import com.zaneschepke.wireguardautotunnel.data.model.Settings
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
interface SettingsRepository {
|
||||||
|
suspend fun save(settings : Settings)
|
||||||
|
fun getSettings() : Flow<Settings>
|
||||||
|
|
||||||
|
suspend fun getAll() : List<Settings>
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.zaneschepke.wireguardautotunnel.data.repository
|
||||||
|
|
||||||
|
import com.zaneschepke.wireguardautotunnel.data.SettingsDao
|
||||||
|
import com.zaneschepke.wireguardautotunnel.data.model.Settings
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
class SettingsRepositoryImpl(private val settingsDoa: SettingsDao) : SettingsRepository {
|
||||||
|
|
||||||
|
override suspend fun save(settings: Settings) {
|
||||||
|
settingsDoa.save(settings)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getSettings(): Flow<Settings> {
|
||||||
|
return settingsDoa.getSettingsFlow()
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getAll(): List<Settings> {
|
||||||
|
return settingsDoa.getAll()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package com.zaneschepke.wireguardautotunnel.data.repository
|
||||||
|
|
||||||
|
interface TunnelConfigRepository {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.zaneschepke.wireguardautotunnel.data.repository
|
||||||
|
|
||||||
|
import com.zaneschepke.wireguardautotunnel.data.TunnelConfigDao
|
||||||
|
|
||||||
|
class TunnelConfigRepositoryImpl(private val tunnelConfigDao: TunnelConfigDao) : TunnelConfigRepository {
|
||||||
|
|
||||||
|
}
|
|
@ -3,7 +3,7 @@ package com.zaneschepke.wireguardautotunnel.module
|
||||||
import android.content.Context
|
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.repository.AppDatabase
|
import com.zaneschepke.wireguardautotunnel.data.AppDatabase
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
import dagger.hilt.InstallIn
|
import dagger.hilt.InstallIn
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
package com.zaneschepke.wireguardautotunnel.module
|
package com.zaneschepke.wireguardautotunnel.module
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.AppDatabase
|
import com.zaneschepke.wireguardautotunnel.data.AppDatabase
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.SettingsDoa
|
import com.zaneschepke.wireguardautotunnel.data.SettingsDao
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.TunnelConfigDao
|
import com.zaneschepke.wireguardautotunnel.data.TunnelConfigDao
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.datastore.DataStoreManager
|
import com.zaneschepke.wireguardautotunnel.data.datastore.DataStoreManager
|
||||||
|
import com.zaneschepke.wireguardautotunnel.data.repository.SettingsRepository
|
||||||
|
import com.zaneschepke.wireguardautotunnel.data.repository.SettingsRepositoryImpl
|
||||||
|
import com.zaneschepke.wireguardautotunnel.data.repository.TunnelConfigRepository
|
||||||
|
import com.zaneschepke.wireguardautotunnel.data.repository.TunnelConfigRepositoryImpl
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
import dagger.hilt.InstallIn
|
import dagger.hilt.InstallIn
|
||||||
|
@ -17,16 +21,28 @@ import javax.inject.Singleton
|
||||||
class RepositoryModule {
|
class RepositoryModule {
|
||||||
@Singleton
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
fun provideSettingsRepository(appDatabase: AppDatabase): SettingsDoa {
|
fun provideSettingsDoa(appDatabase: AppDatabase): SettingsDao {
|
||||||
return appDatabase.settingDao()
|
return appDatabase.settingDao()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
fun provideTunnelConfigRepository(appDatabase: AppDatabase): TunnelConfigDao {
|
fun provideTunnelConfigDoa(appDatabase: AppDatabase): TunnelConfigDao {
|
||||||
return appDatabase.tunnelConfigDoa()
|
return appDatabase.tunnelConfigDoa()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
@Provides
|
||||||
|
fun provideTunnelConfigRepository(tunnelConfigDao: TunnelConfigDao): TunnelConfigRepository {
|
||||||
|
return TunnelConfigRepositoryImpl(tunnelConfigDao)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
@Provides
|
||||||
|
fun provideSettingsRepository(settingsDao: SettingsDao): SettingsRepository {
|
||||||
|
return SettingsRepositoryImpl(settingsDao)
|
||||||
|
}
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
fun providePreferencesDataStore(@ApplicationContext context: Context): DataStoreManager {
|
fun providePreferencesDataStore(@ApplicationContext context: Context): DataStoreManager {
|
||||||
|
|
|
@ -6,7 +6,7 @@ import com.wireguard.android.backend.GoBackend
|
||||||
import com.wireguard.android.backend.WgQuickBackend
|
import com.wireguard.android.backend.WgQuickBackend
|
||||||
import com.wireguard.android.util.RootShell
|
import com.wireguard.android.util.RootShell
|
||||||
import com.wireguard.android.util.ToolsInstaller
|
import com.wireguard.android.util.ToolsInstaller
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.SettingsDoa
|
import com.zaneschepke.wireguardautotunnel.data.SettingsDao
|
||||||
import com.zaneschepke.wireguardautotunnel.service.tunnel.VpnService
|
import com.zaneschepke.wireguardautotunnel.service.tunnel.VpnService
|
||||||
import com.zaneschepke.wireguardautotunnel.service.tunnel.WireGuardTunnel
|
import com.zaneschepke.wireguardautotunnel.service.tunnel.WireGuardTunnel
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
|
@ -51,7 +51,7 @@ class TunnelModule {
|
||||||
fun provideVpnService(
|
fun provideVpnService(
|
||||||
@Userspace userspaceBackend: Backend,
|
@Userspace userspaceBackend: Backend,
|
||||||
@Kernel kernelBackend: Backend,
|
@Kernel kernelBackend: Backend,
|
||||||
settingsDoa: SettingsDoa
|
settingsDoa: SettingsDao
|
||||||
): VpnService {
|
): VpnService {
|
||||||
return WireGuardTunnel(userspaceBackend, kernelBackend, settingsDoa)
|
return WireGuardTunnel(userspaceBackend, kernelBackend, settingsDoa)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import com.zaneschepke.wireguardautotunnel.goAsync
|
import com.zaneschepke.wireguardautotunnel.goAsync
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.SettingsDoa
|
import com.zaneschepke.wireguardautotunnel.data.SettingsDao
|
||||||
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager
|
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -13,7 +13,7 @@ import kotlinx.coroutines.cancel
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class BootReceiver : BroadcastReceiver() {
|
class BootReceiver : BroadcastReceiver() {
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var settingsRepo: SettingsDoa
|
lateinit var settingsRepo: SettingsDao
|
||||||
|
|
||||||
override fun onReceive(
|
override fun onReceive(
|
||||||
context: Context,
|
context: Context,
|
||||||
|
|
|
@ -5,7 +5,7 @@ import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import com.zaneschepke.wireguardautotunnel.Constants
|
import com.zaneschepke.wireguardautotunnel.Constants
|
||||||
import com.zaneschepke.wireguardautotunnel.goAsync
|
import com.zaneschepke.wireguardautotunnel.goAsync
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.SettingsDoa
|
import com.zaneschepke.wireguardautotunnel.data.SettingsDao
|
||||||
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager
|
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -15,7 +15,7 @@ import kotlinx.coroutines.delay
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class NotificationActionReceiver : BroadcastReceiver() {
|
class NotificationActionReceiver : BroadcastReceiver() {
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var settingsRepo: SettingsDoa
|
lateinit var settingsRepo: SettingsDao
|
||||||
|
|
||||||
override fun onReceive(
|
override fun onReceive(
|
||||||
context: Context,
|
context: Context,
|
||||||
|
|
|
@ -12,8 +12,8 @@ import androidx.lifecycle.lifecycleScope
|
||||||
import com.wireguard.android.backend.Tunnel
|
import com.wireguard.android.backend.Tunnel
|
||||||
import com.zaneschepke.wireguardautotunnel.Constants
|
import com.zaneschepke.wireguardautotunnel.Constants
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.SettingsDoa
|
import com.zaneschepke.wireguardautotunnel.data.SettingsDao
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.model.Settings
|
import com.zaneschepke.wireguardautotunnel.data.model.Settings
|
||||||
import com.zaneschepke.wireguardautotunnel.service.network.EthernetService
|
import com.zaneschepke.wireguardautotunnel.service.network.EthernetService
|
||||||
import com.zaneschepke.wireguardautotunnel.service.network.MobileDataService
|
import com.zaneschepke.wireguardautotunnel.service.network.MobileDataService
|
||||||
import com.zaneschepke.wireguardautotunnel.service.network.NetworkService
|
import com.zaneschepke.wireguardautotunnel.service.network.NetworkService
|
||||||
|
@ -44,7 +44,7 @@ class WireGuardConnectivityWatcherService : ForegroundService() {
|
||||||
lateinit var ethernetService: NetworkService<EthernetService>
|
lateinit var ethernetService: NetworkService<EthernetService>
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var settingsRepo: SettingsDoa
|
lateinit var settingsRepo: SettingsDao
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var notificationService: NotificationService
|
lateinit var notificationService: NotificationService
|
||||||
|
|
|
@ -8,8 +8,8 @@ import androidx.lifecycle.lifecycleScope
|
||||||
import com.zaneschepke.wireguardautotunnel.Constants
|
import com.zaneschepke.wireguardautotunnel.Constants
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
import com.zaneschepke.wireguardautotunnel.receiver.NotificationActionReceiver
|
import com.zaneschepke.wireguardautotunnel.receiver.NotificationActionReceiver
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.SettingsDoa
|
import com.zaneschepke.wireguardautotunnel.data.SettingsDao
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.model.TunnelConfig
|
import com.zaneschepke.wireguardautotunnel.data.model.TunnelConfig
|
||||||
import com.zaneschepke.wireguardautotunnel.service.notification.NotificationService
|
import com.zaneschepke.wireguardautotunnel.service.notification.NotificationService
|
||||||
import com.zaneschepke.wireguardautotunnel.service.tunnel.HandshakeStatus
|
import com.zaneschepke.wireguardautotunnel.service.tunnel.HandshakeStatus
|
||||||
import com.zaneschepke.wireguardautotunnel.service.tunnel.VpnService
|
import com.zaneschepke.wireguardautotunnel.service.tunnel.VpnService
|
||||||
|
@ -28,7 +28,7 @@ class WireGuardTunnelService : ForegroundService() {
|
||||||
lateinit var vpnService: VpnService
|
lateinit var vpnService: VpnService
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var settingsRepo: SettingsDoa
|
lateinit var settingsRepo: SettingsDao
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var notificationService: NotificationService
|
lateinit var notificationService: NotificationService
|
||||||
|
|
|
@ -3,10 +3,10 @@ package com.zaneschepke.wireguardautotunnel.service.shortcut
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.SettingsDoa
|
import com.zaneschepke.wireguardautotunnel.data.SettingsDao
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.TunnelConfigDao
|
import com.zaneschepke.wireguardautotunnel.data.TunnelConfigDao
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.model.Settings
|
import com.zaneschepke.wireguardautotunnel.data.model.Settings
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.model.TunnelConfig
|
import com.zaneschepke.wireguardautotunnel.data.model.TunnelConfig
|
||||||
import com.zaneschepke.wireguardautotunnel.service.foreground.Action
|
import com.zaneschepke.wireguardautotunnel.service.foreground.Action
|
||||||
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager
|
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager
|
||||||
import com.zaneschepke.wireguardautotunnel.service.foreground.WireGuardTunnelService
|
import com.zaneschepke.wireguardautotunnel.service.foreground.WireGuardTunnelService
|
||||||
|
@ -20,7 +20,7 @@ import timber.log.Timber
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class ShortcutsActivity : ComponentActivity() {
|
class ShortcutsActivity : ComponentActivity() {
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var settingsRepo: SettingsDoa
|
lateinit var settingsRepo: SettingsDao
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var tunnelConfigRepo: TunnelConfigDao
|
lateinit var tunnelConfigRepo: TunnelConfigDao
|
||||||
|
|
|
@ -5,9 +5,9 @@ import android.service.quicksettings.Tile
|
||||||
import android.service.quicksettings.TileService
|
import android.service.quicksettings.TileService
|
||||||
import com.wireguard.android.backend.Tunnel
|
import com.wireguard.android.backend.Tunnel
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.SettingsDoa
|
import com.zaneschepke.wireguardautotunnel.data.SettingsDao
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.TunnelConfigDao
|
import com.zaneschepke.wireguardautotunnel.data.TunnelConfigDao
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.model.TunnelConfig
|
import com.zaneschepke.wireguardautotunnel.data.model.TunnelConfig
|
||||||
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager
|
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager
|
||||||
import com.zaneschepke.wireguardautotunnel.service.tunnel.VpnService
|
import com.zaneschepke.wireguardautotunnel.service.tunnel.VpnService
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
@ -22,7 +22,7 @@ import timber.log.Timber
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class TunnelControlTile : TileService() {
|
class TunnelControlTile : TileService() {
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var settingsRepo: SettingsDoa
|
lateinit var settingsRepo: SettingsDao
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var configRepo: TunnelConfigDao
|
lateinit var configRepo: TunnelConfigDao
|
||||||
|
|
|
@ -3,7 +3,7 @@ package com.zaneschepke.wireguardautotunnel.service.tunnel
|
||||||
import com.wireguard.android.backend.Statistics
|
import com.wireguard.android.backend.Statistics
|
||||||
import com.wireguard.android.backend.Tunnel
|
import com.wireguard.android.backend.Tunnel
|
||||||
import com.wireguard.crypto.Key
|
import com.wireguard.crypto.Key
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.model.TunnelConfig
|
import com.zaneschepke.wireguardautotunnel.data.model.TunnelConfig
|
||||||
import kotlinx.coroutines.flow.SharedFlow
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
|
|
||||||
interface VpnService : Tunnel {
|
interface VpnService : Tunnel {
|
||||||
|
|
|
@ -9,8 +9,8 @@ import com.wireguard.crypto.Key
|
||||||
import com.zaneschepke.wireguardautotunnel.Constants
|
import com.zaneschepke.wireguardautotunnel.Constants
|
||||||
import com.zaneschepke.wireguardautotunnel.module.Kernel
|
import com.zaneschepke.wireguardautotunnel.module.Kernel
|
||||||
import com.zaneschepke.wireguardautotunnel.module.Userspace
|
import com.zaneschepke.wireguardautotunnel.module.Userspace
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.SettingsDoa
|
import com.zaneschepke.wireguardautotunnel.data.SettingsDao
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.model.TunnelConfig
|
import com.zaneschepke.wireguardautotunnel.data.model.TunnelConfig
|
||||||
import com.zaneschepke.wireguardautotunnel.util.NumberUtils
|
import com.zaneschepke.wireguardautotunnel.util.NumberUtils
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
@ -31,7 +31,7 @@ class WireGuardTunnel
|
||||||
constructor(
|
constructor(
|
||||||
@Userspace private val userspaceBackend: Backend,
|
@Userspace private val userspaceBackend: Backend,
|
||||||
@Kernel private val kernelBackend: Backend,
|
@Kernel private val kernelBackend: Backend,
|
||||||
private val settingsRepo: SettingsDoa
|
private val settingsRepo: SettingsDao
|
||||||
) : VpnService {
|
) : VpnService {
|
||||||
private val _tunnelName = MutableStateFlow("")
|
private val _tunnelName = MutableStateFlow("")
|
||||||
override val tunnelName get() = _tunnelName.asStateFlow()
|
override val tunnelName get() = _tunnelName.asStateFlow()
|
||||||
|
|
|
@ -1,17 +1,13 @@
|
||||||
package com.zaneschepke.wireguardautotunnel.ui
|
package com.zaneschepke.wireguardautotunnel.ui
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import com.zaneschepke.wireguardautotunnel.data.SettingsDao
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.SettingsDoa
|
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.model.Settings
|
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
|
||||||
import kotlinx.coroutines.flow.stateIn
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class ActivityViewModel @Inject constructor(
|
class ActivityViewModel @Inject constructor(
|
||||||
private val settingsRepo: SettingsDoa,
|
private val settingsRepo: SettingsDao,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
// val settings = settingsRepo.getSettingsFlow().stateIn(viewModelScope,
|
// val settings = settingsRepo.getSettingsFlow().stateIn(viewModelScope,
|
||||||
// SharingStarted.WhileSubscribed(5000L), Settings()
|
// SharingStarted.WhileSubscribed(5000L), Settings()
|
||||||
|
|
|
@ -15,9 +15,9 @@ import com.wireguard.config.Peer
|
||||||
import com.wireguard.crypto.Key
|
import com.wireguard.crypto.Key
|
||||||
import com.wireguard.crypto.KeyPair
|
import com.wireguard.crypto.KeyPair
|
||||||
import com.zaneschepke.wireguardautotunnel.Constants
|
import com.zaneschepke.wireguardautotunnel.Constants
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.SettingsDoa
|
import com.zaneschepke.wireguardautotunnel.data.SettingsDao
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.TunnelConfigDao
|
import com.zaneschepke.wireguardautotunnel.data.TunnelConfigDao
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.model.TunnelConfig
|
import com.zaneschepke.wireguardautotunnel.data.model.TunnelConfig
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.models.InterfaceProxy
|
import com.zaneschepke.wireguardautotunnel.ui.models.InterfaceProxy
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.models.PeerProxy
|
import com.zaneschepke.wireguardautotunnel.ui.models.PeerProxy
|
||||||
import com.zaneschepke.wireguardautotunnel.util.NumberUtils
|
import com.zaneschepke.wireguardautotunnel.util.NumberUtils
|
||||||
|
@ -35,7 +35,7 @@ class ConfigViewModel
|
||||||
constructor(
|
constructor(
|
||||||
private val application: Application,
|
private val application: Application,
|
||||||
private val tunnelRepo: TunnelConfigDao,
|
private val tunnelRepo: TunnelConfigDao,
|
||||||
private val settingsRepo: SettingsDoa
|
private val settingsRepo: SettingsDao
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
private val _tunnel = MutableStateFlow<TunnelConfig?>(null)
|
private val _tunnel = MutableStateFlow<TunnelConfig?>(null)
|
||||||
private val _tunnelName = MutableStateFlow("")
|
private val _tunnelName = MutableStateFlow("")
|
||||||
|
|
|
@ -84,9 +84,8 @@ import com.wireguard.android.backend.Tunnel
|
||||||
import com.zaneschepke.wireguardautotunnel.Constants
|
import com.zaneschepke.wireguardautotunnel.Constants
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
import com.zaneschepke.wireguardautotunnel.WireGuardAutoTunnel
|
import com.zaneschepke.wireguardautotunnel.WireGuardAutoTunnel
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.model.TunnelConfig
|
import com.zaneschepke.wireguardautotunnel.data.model.TunnelConfig
|
||||||
import com.zaneschepke.wireguardautotunnel.service.tunnel.HandshakeStatus
|
import com.zaneschepke.wireguardautotunnel.service.tunnel.HandshakeStatus
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.ActivityViewModel
|
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.CaptureActivityPortrait
|
import com.zaneschepke.wireguardautotunnel.ui.CaptureActivityPortrait
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.Routes
|
import com.zaneschepke.wireguardautotunnel.ui.Routes
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.RowListItem
|
import com.zaneschepke.wireguardautotunnel.ui.common.RowListItem
|
||||||
|
|
|
@ -10,10 +10,11 @@ import androidx.lifecycle.viewModelScope
|
||||||
import com.wireguard.config.Config
|
import com.wireguard.config.Config
|
||||||
import com.zaneschepke.wireguardautotunnel.Constants
|
import com.zaneschepke.wireguardautotunnel.Constants
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.SettingsDoa
|
import com.zaneschepke.wireguardautotunnel.data.SettingsDao
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.TunnelConfigDao
|
import com.zaneschepke.wireguardautotunnel.data.TunnelConfigDao
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.model.Settings
|
import com.zaneschepke.wireguardautotunnel.data.model.Settings
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.model.TunnelConfig
|
import com.zaneschepke.wireguardautotunnel.data.model.TunnelConfig
|
||||||
|
import com.zaneschepke.wireguardautotunnel.data.repository.SettingsRepository
|
||||||
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager
|
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager
|
||||||
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceState
|
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceState
|
||||||
import com.zaneschepke.wireguardautotunnel.service.foreground.WireGuardConnectivityWatcherService
|
import com.zaneschepke.wireguardautotunnel.service.foreground.WireGuardConnectivityWatcherService
|
||||||
|
@ -39,7 +40,7 @@ class MainViewModel
|
||||||
constructor(
|
constructor(
|
||||||
private val application: Application,
|
private val application: Application,
|
||||||
private val tunnelRepo: TunnelConfigDao,
|
private val tunnelRepo: TunnelConfigDao,
|
||||||
private val settingsRepo: SettingsDoa,
|
private val settingsRepository: SettingsRepository,
|
||||||
private val vpnService: VpnService
|
private val vpnService: VpnService
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
val tunnels get() = tunnelRepo.getAllFlow()
|
val tunnels get() = tunnelRepo.getAllFlow()
|
||||||
|
@ -53,10 +54,9 @@ constructor(
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
settingsRepo.getAllFlow().filter { it.isNotEmpty() }.collect {
|
settingsRepository.getSettings().collect {
|
||||||
val settings = it.first()
|
validateWatcherServiceState(it)
|
||||||
validateWatcherServiceState(settings)
|
_settings.emit(it)
|
||||||
_settings.emit(settings)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,13 +79,13 @@ constructor(
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
if (tunnelRepo.count() == 1L) {
|
if (tunnelRepo.count() == 1L) {
|
||||||
ServiceManager.stopWatcherService(application.applicationContext)
|
ServiceManager.stopWatcherService(application.applicationContext)
|
||||||
val settings = settingsRepo.getAll()
|
val settings = settingsRepository.getAll()
|
||||||
if (settings.isNotEmpty()) {
|
if (settings.isNotEmpty()) {
|
||||||
val setting = settings[0]
|
val setting = settings[0]
|
||||||
setting.defaultTunnel = null
|
setting.defaultTunnel = null
|
||||||
setting.isAutoTunnelEnabled = false
|
setting.isAutoTunnelEnabled = false
|
||||||
setting.isAlwaysOnVpnEnabled = false
|
setting.isAlwaysOnVpnEnabled = false
|
||||||
settingsRepo.save(setting)
|
saveSettings(setting)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tunnelRepo.delete(tunnel)
|
tunnelRepo.delete(tunnel)
|
||||||
|
@ -237,6 +237,11 @@ constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun saveSettings(settings: Settings) {
|
||||||
|
//TODO handle error if fails
|
||||||
|
settingsRepository.save(settings)
|
||||||
|
}
|
||||||
|
|
||||||
private fun getFileName(
|
private fun getFileName(
|
||||||
context: Context,
|
context: Context,
|
||||||
uri: Uri
|
uri: Uri
|
||||||
|
@ -268,7 +273,7 @@ constructor(
|
||||||
defaultTunnel = selectedTunnel.toString()
|
defaultTunnel = selectedTunnel.toString()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
settingsRepo.save(_settings.value)
|
settingsRepository.save(_settings.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import android.Manifest
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.provider.Settings
|
import android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
@ -38,13 +38,11 @@ import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.SideEffect
|
import androidx.compose.runtime.SideEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
|
||||||
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.ExperimentalComposeUiApi
|
||||||
|
@ -72,17 +70,15 @@ import com.wireguard.android.backend.Tunnel
|
||||||
import com.wireguard.android.backend.WgQuickBackend
|
import com.wireguard.android.backend.WgQuickBackend
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
import com.zaneschepke.wireguardautotunnel.WireGuardAutoTunnel
|
import com.zaneschepke.wireguardautotunnel.WireGuardAutoTunnel
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.datastore.DataStoreManager
|
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.ActivityViewModel
|
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.ClickableIconButton
|
import com.zaneschepke.wireguardautotunnel.ui.common.ClickableIconButton
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.config.ConfigurationToggle
|
import com.zaneschepke.wireguardautotunnel.ui.common.config.ConfigurationToggle
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.prompt.AuthorizationPrompt
|
import com.zaneschepke.wireguardautotunnel.ui.common.prompt.AuthorizationPrompt
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.text.SectionTitle
|
import com.zaneschepke.wireguardautotunnel.ui.common.text.SectionTitle
|
||||||
import com.zaneschepke.wireguardautotunnel.util.FileUtils
|
import com.zaneschepke.wireguardautotunnel.util.FileUtils
|
||||||
import com.zaneschepke.wireguardautotunnel.util.WgTunnelException
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@OptIn(
|
@OptIn(
|
||||||
ExperimentalPermissionsApi::class,
|
ExperimentalPermissionsApi::class,
|
||||||
ExperimentalLayoutApi::class,
|
ExperimentalLayoutApi::class,
|
||||||
|
@ -102,33 +98,26 @@ fun SettingsScreen(
|
||||||
val keyboardController = LocalSoftwareKeyboardController.current
|
val keyboardController = LocalSoftwareKeyboardController.current
|
||||||
val interactionSource = remember { MutableInteractionSource() }
|
val interactionSource = remember { MutableInteractionSource() }
|
||||||
|
|
||||||
val settings by viewModel.settings.collectAsStateWithLifecycle()
|
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||||
val tunnels by viewModel.tunnels.collectAsStateWithLifecycle()
|
|
||||||
val vpnState = viewModel.vpnState.collectAsStateWithLifecycle()
|
|
||||||
|
|
||||||
val fineLocationState = rememberPermissionState(Manifest.permission.ACCESS_FINE_LOCATION)
|
val fineLocationState = rememberPermissionState(Manifest.permission.ACCESS_FINE_LOCATION)
|
||||||
var currentText by remember { mutableStateOf("") }
|
var currentText by remember { mutableStateOf("") }
|
||||||
var isBackgroundLocationGranted by remember { mutableStateOf(true) }
|
var isBackgroundLocationGranted by remember { mutableStateOf(true) }
|
||||||
var didExportFiles by remember { mutableStateOf(false) }
|
var didExportFiles by remember { mutableStateOf(false) }
|
||||||
var showAuthPrompt by remember { mutableStateOf(false) }
|
var showAuthPrompt by remember { mutableStateOf(false) }
|
||||||
var isLocationDisclosureShown by rememberSaveable {
|
|
||||||
mutableStateOf(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
val screenPadding = 5.dp
|
val screenPadding = 5.dp
|
||||||
val fillMaxWidth = .85f
|
val fillMaxWidth = .85f
|
||||||
|
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
//TODO add error collecting and displaying for WGTunnelErrors
|
||||||
isLocationDisclosureShown = viewModel.isLocationDisclosureShown()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun exportAllConfigs() {
|
fun exportAllConfigs() {
|
||||||
try {
|
try {
|
||||||
val files = tunnels.map { File(context.cacheDir, "${it.name}.conf") }
|
val files = uiState.tunnels.map { File(context.cacheDir, "${it.name}.conf") }
|
||||||
files.forEachIndexed { index, file ->
|
files.forEachIndexed { index, file ->
|
||||||
file.outputStream().use {
|
file.outputStream().use {
|
||||||
it.write(tunnels[index].wgQuick.toByteArray())
|
it.write(uiState.tunnels[index].wgQuick.toByteArray())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FileUtils.saveFilesToZip(context, files)
|
FileUtils.saveFilesToZip(context, files)
|
||||||
|
@ -163,20 +152,31 @@ fun SettingsScreen(
|
||||||
fun openSettings() {
|
fun openSettings() {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
val intentSettings =
|
val intentSettings =
|
||||||
Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
|
Intent(ACTION_APPLICATION_DETAILS_SETTINGS)
|
||||||
intentSettings.data =
|
intentSettings.data =
|
||||||
Uri.fromParts("package", context.packageName, null)
|
Uri.fromParts("package", context.packageName, null)
|
||||||
context.startActivity(intentSettings)
|
context.startActivity(intentSettings)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun checkFineLocationGranted() {
|
||||||
|
isBackgroundLocationGranted = if (!fineLocationState.status.isGranted) {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
scope.launch {
|
||||||
|
viewModel.setLocationDisclosureShown()
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
val backgroundLocationState =
|
val backgroundLocationState =
|
||||||
rememberPermissionState(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
|
rememberPermissionState(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
|
||||||
isBackgroundLocationGranted = if (!backgroundLocationState.status.isGranted) {
|
isBackgroundLocationGranted = if (!backgroundLocationState.status.isGranted) {
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
if(!isLocationDisclosureShown) {
|
SideEffect {
|
||||||
viewModel.setLocationDisclosureShown()
|
viewModel.setLocationDisclosureShown()
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
|
@ -194,7 +194,7 @@ fun SettingsScreen(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimatedVisibility(!isLocationDisclosureShown) {
|
AnimatedVisibility(!uiState.isLocationDisclosureShown) {
|
||||||
Column(
|
Column(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
verticalArrangement = Arrangement.Top,
|
verticalArrangement = Arrangement.Top,
|
||||||
|
@ -270,7 +270,7 @@ fun SettingsScreen(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tunnels.isEmpty()) {
|
if (uiState.tunnels.isEmpty()) {
|
||||||
Column(
|
Column(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
verticalArrangement = Arrangement.Center,
|
verticalArrangement = Arrangement.Center,
|
||||||
|
@ -330,17 +330,15 @@ fun SettingsScreen(
|
||||||
)
|
)
|
||||||
ConfigurationToggle(
|
ConfigurationToggle(
|
||||||
stringResource(id = R.string.tunnel_on_wifi),
|
stringResource(id = R.string.tunnel_on_wifi),
|
||||||
enabled = !(settings.isAutoTunnelEnabled || settings.isAlwaysOnVpnEnabled),
|
enabled = !(uiState.settings.isAutoTunnelEnabled || uiState.settings.isAlwaysOnVpnEnabled),
|
||||||
checked = settings.isTunnelOnWifiEnabled,
|
checked = uiState.settings.isTunnelOnWifiEnabled,
|
||||||
padding = screenPadding,
|
padding = screenPadding,
|
||||||
onCheckChanged = {
|
onCheckChanged = {
|
||||||
scope.launch {
|
viewModel.onToggleTunnelOnWifi()
|
||||||
viewModel.onToggleTunnelOnWifi()
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
modifier = Modifier.focusRequester(focusRequester)
|
modifier = Modifier.focusRequester(focusRequester)
|
||||||
)
|
)
|
||||||
AnimatedVisibility(visible = settings.isTunnelOnWifiEnabled) {
|
AnimatedVisibility(visible = uiState.settings.isTunnelOnWifiEnabled) {
|
||||||
Column {
|
Column {
|
||||||
FlowRow(
|
FlowRow(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
@ -348,19 +346,17 @@ fun SettingsScreen(
|
||||||
.fillMaxWidth(),
|
.fillMaxWidth(),
|
||||||
horizontalArrangement = Arrangement.spacedBy(5.dp)
|
horizontalArrangement = Arrangement.spacedBy(5.dp)
|
||||||
) {
|
) {
|
||||||
settings.trustedNetworkSSIDs.forEach { ssid ->
|
uiState.settings.trustedNetworkSSIDs.forEach { ssid ->
|
||||||
ClickableIconButton(
|
ClickableIconButton(
|
||||||
onIconClick = {
|
onIconClick = {
|
||||||
scope.launch {
|
viewModel.onDeleteTrustedSSID(ssid)
|
||||||
viewModel.onDeleteTrustedSSID(ssid)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
text = ssid,
|
text = ssid,
|
||||||
icon = Icons.Filled.Close,
|
icon = Icons.Filled.Close,
|
||||||
enabled = !(settings.isAutoTunnelEnabled || settings.isAlwaysOnVpnEnabled)
|
enabled = !(uiState.settings.isAutoTunnelEnabled || uiState.settings.isAlwaysOnVpnEnabled)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (settings.trustedNetworkSSIDs.isEmpty()) {
|
if (uiState.settings.trustedNetworkSSIDs.isEmpty()) {
|
||||||
Text(
|
Text(
|
||||||
stringResource(R.string.none),
|
stringResource(R.string.none),
|
||||||
fontStyle = FontStyle.Italic,
|
fontStyle = FontStyle.Italic,
|
||||||
|
@ -369,7 +365,7 @@ fun SettingsScreen(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
enabled = !(settings.isAutoTunnelEnabled || settings.isAlwaysOnVpnEnabled),
|
enabled = !(uiState.settings.isAutoTunnelEnabled || uiState.settings.isAlwaysOnVpnEnabled),
|
||||||
value = currentText,
|
value = currentText,
|
||||||
onValueChange = { currentText = it },
|
onValueChange = { currentText = it },
|
||||||
label = { Text(stringResource(R.string.add_trusted_ssid)) },
|
label = { Text(stringResource(R.string.add_trusted_ssid)) },
|
||||||
|
@ -418,35 +414,29 @@ fun SettingsScreen(
|
||||||
}
|
}
|
||||||
ConfigurationToggle(
|
ConfigurationToggle(
|
||||||
stringResource(R.string.tunnel_mobile_data),
|
stringResource(R.string.tunnel_mobile_data),
|
||||||
enabled = !(settings.isAutoTunnelEnabled || settings.isAlwaysOnVpnEnabled),
|
enabled = !(uiState.settings.isAutoTunnelEnabled || uiState.settings.isAlwaysOnVpnEnabled),
|
||||||
checked = settings.isTunnelOnMobileDataEnabled,
|
checked = uiState.settings.isTunnelOnMobileDataEnabled,
|
||||||
padding = screenPadding,
|
padding = screenPadding,
|
||||||
onCheckChanged = {
|
onCheckChanged = {
|
||||||
scope.launch {
|
viewModel.onToggleTunnelOnMobileData()
|
||||||
viewModel.onToggleTunnelOnMobileData()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
ConfigurationToggle(
|
ConfigurationToggle(
|
||||||
stringResource(id = R.string.tunnel_on_ethernet),
|
stringResource(id = R.string.tunnel_on_ethernet),
|
||||||
enabled = !(settings.isAutoTunnelEnabled || settings.isAlwaysOnVpnEnabled),
|
enabled = !(uiState.settings.isAutoTunnelEnabled || uiState.settings.isAlwaysOnVpnEnabled),
|
||||||
checked = settings.isTunnelOnEthernetEnabled,
|
checked = uiState.settings.isTunnelOnEthernetEnabled,
|
||||||
padding = screenPadding,
|
padding = screenPadding,
|
||||||
onCheckChanged = {
|
onCheckChanged = {
|
||||||
scope.launch {
|
viewModel.onToggleTunnelOnEthernet()
|
||||||
viewModel.onToggleTunnelOnEthernet()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
ConfigurationToggle(
|
ConfigurationToggle(
|
||||||
stringResource(R.string.battery_saver),
|
stringResource(R.string.battery_saver),
|
||||||
enabled = !(settings.isAutoTunnelEnabled || settings.isAlwaysOnVpnEnabled),
|
enabled = !(uiState.settings.isAutoTunnelEnabled || uiState.settings.isAlwaysOnVpnEnabled),
|
||||||
checked = settings.isBatterySaverEnabled,
|
checked = uiState.settings.isBatterySaverEnabled,
|
||||||
padding = screenPadding,
|
padding = screenPadding,
|
||||||
onCheckChanged = {
|
onCheckChanged = {
|
||||||
scope.launch {
|
viewModel.onToggleBatterySaver()
|
||||||
viewModel.onToggleBatterySaver()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
Row(
|
Row(
|
||||||
|
@ -458,9 +448,9 @@ fun SettingsScreen(
|
||||||
horizontalArrangement = Arrangement.Center
|
horizontalArrangement = Arrangement.Center
|
||||||
) {
|
) {
|
||||||
TextButton(
|
TextButton(
|
||||||
enabled = !settings.isAlwaysOnVpnEnabled,
|
enabled = !uiState.settings.isAlwaysOnVpnEnabled,
|
||||||
onClick = {
|
onClick = {
|
||||||
if (!isAllAutoTunnelPermissionsEnabled() && settings.isTunnelOnWifiEnabled) {
|
if (!isAllAutoTunnelPermissionsEnabled() && uiState.settings.isTunnelOnWifiEnabled) {
|
||||||
val message =
|
val message =
|
||||||
if (!isBackgroundLocationGranted) {
|
if (!isBackgroundLocationGranted) {
|
||||||
context.getString(R.string.background_location_required)
|
context.getString(R.string.background_location_required)
|
||||||
|
@ -471,14 +461,12 @@ fun SettingsScreen(
|
||||||
}
|
}
|
||||||
showSnackbarMessage(message)
|
showSnackbarMessage(message)
|
||||||
} else {
|
} else {
|
||||||
scope.launch {
|
viewModel.toggleAutoTunnel()
|
||||||
viewModel.toggleAutoTunnel()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
val autoTunnelButtonText =
|
val autoTunnelButtonText =
|
||||||
if (settings.isAutoTunnelEnabled) {
|
if (uiState.settings.isAutoTunnelEnabled) {
|
||||||
stringResource(R.string.disable_auto_tunnel)
|
stringResource(R.string.disable_auto_tunnel)
|
||||||
} else {
|
} else {
|
||||||
stringResource(id = R.string.enable_auto_tunnel)
|
stringResource(id = R.string.enable_auto_tunnel)
|
||||||
|
@ -510,19 +498,13 @@ fun SettingsScreen(
|
||||||
ConfigurationToggle(
|
ConfigurationToggle(
|
||||||
stringResource(R.string.use_kernel),
|
stringResource(R.string.use_kernel),
|
||||||
enabled = !(
|
enabled = !(
|
||||||
settings.isAutoTunnelEnabled || settings.isAlwaysOnVpnEnabled ||
|
uiState.settings.isAutoTunnelEnabled || uiState.settings.isAlwaysOnVpnEnabled ||
|
||||||
(vpnState.value == Tunnel.State.UP)
|
(uiState.tunnelState == Tunnel.State.UP)
|
||||||
),
|
),
|
||||||
checked = settings.isKernelEnabled,
|
checked = uiState.settings.isKernelEnabled,
|
||||||
padding = screenPadding,
|
padding = screenPadding,
|
||||||
onCheckChanged = {
|
onCheckChanged = {
|
||||||
scope.launch {
|
viewModel.onToggleKernelMode()
|
||||||
try {
|
|
||||||
viewModel.onToggleKernelMode()
|
|
||||||
} catch (e: WgTunnelException) {
|
|
||||||
showSnackbarMessage(e.message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -550,8 +532,8 @@ fun SettingsScreen(
|
||||||
)
|
)
|
||||||
ConfigurationToggle(
|
ConfigurationToggle(
|
||||||
stringResource(R.string.always_on_vpn_support),
|
stringResource(R.string.always_on_vpn_support),
|
||||||
enabled = !settings.isAutoTunnelEnabled,
|
enabled = !uiState.settings.isAutoTunnelEnabled,
|
||||||
checked = settings.isAlwaysOnVpnEnabled,
|
checked = uiState.settings.isAlwaysOnVpnEnabled,
|
||||||
padding = screenPadding,
|
padding = screenPadding,
|
||||||
onCheckChanged = {
|
onCheckChanged = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
|
@ -562,7 +544,7 @@ fun SettingsScreen(
|
||||||
ConfigurationToggle(
|
ConfigurationToggle(
|
||||||
stringResource(R.string.enabled_app_shortcuts),
|
stringResource(R.string.enabled_app_shortcuts),
|
||||||
enabled = true,
|
enabled = true,
|
||||||
checked = settings.isShortcutsEnabled,
|
checked = uiState.settings.isShortcutsEnabled,
|
||||||
padding = screenPadding,
|
padding = screenPadding,
|
||||||
onCheckChanged = {
|
onCheckChanged = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
package com.zaneschepke.wireguardautotunnel.ui.screens.settings
|
||||||
|
|
||||||
|
import com.wireguard.android.backend.Tunnel
|
||||||
|
import com.zaneschepke.wireguardautotunnel.data.model.Settings
|
||||||
|
import com.zaneschepke.wireguardautotunnel.data.model.TunnelConfig
|
||||||
|
|
||||||
|
data class SettingsUiState(
|
||||||
|
val settings : Settings = Settings(),
|
||||||
|
val tunnels : List<TunnelConfig> = emptyList(),
|
||||||
|
val tunnelState : Tunnel.State = Tunnel.State.DOWN,
|
||||||
|
val isLocationDisclosureShown : Boolean = true,
|
||||||
|
val loading : Boolean = true,
|
||||||
|
)
|
|
@ -6,18 +6,18 @@ import android.location.LocationManager
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.wireguard.android.backend.Tunnel
|
|
||||||
import com.wireguard.android.util.RootShell
|
import com.wireguard.android.util.RootShell
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.SettingsDoa
|
import com.zaneschepke.wireguardautotunnel.data.TunnelConfigDao
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.TunnelConfigDao
|
import com.zaneschepke.wireguardautotunnel.data.datastore.DataStoreManager
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.datastore.DataStoreManager
|
import com.zaneschepke.wireguardautotunnel.data.model.Settings
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.model.Settings
|
import com.zaneschepke.wireguardautotunnel.data.repository.SettingsRepository
|
||||||
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager
|
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager
|
||||||
import com.zaneschepke.wireguardautotunnel.service.tunnel.VpnService
|
import com.zaneschepke.wireguardautotunnel.service.tunnel.VpnService
|
||||||
import com.zaneschepke.wireguardautotunnel.util.WgTunnelException
|
import com.zaneschepke.wireguardautotunnel.util.WgTunnelException
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
@ -28,24 +28,27 @@ class SettingsViewModel
|
||||||
constructor(
|
constructor(
|
||||||
private val application: Application,
|
private val application: Application,
|
||||||
private val tunnelRepo: TunnelConfigDao,
|
private val tunnelRepo: TunnelConfigDao,
|
||||||
private val settingsRepo: SettingsDoa,
|
private val settingsRepository: SettingsRepository,
|
||||||
private val dataStoreManager: DataStoreManager,
|
private val dataStoreManager: DataStoreManager,
|
||||||
private val rootShell: RootShell,
|
private val rootShell: RootShell,
|
||||||
private val vpnService: VpnService
|
private val vpnService: VpnService
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
val settings = settingsRepo.getSettingsFlow().stateIn(viewModelScope,
|
val uiState = combine(
|
||||||
SharingStarted.WhileSubscribed(5_000L), Settings())
|
settingsRepository.getSettings(),
|
||||||
val tunnels = tunnelRepo.getAllFlow().stateIn(viewModelScope,
|
tunnelRepo.getAllFlow(),
|
||||||
SharingStarted.WhileSubscribed(5_000L), emptyList())
|
vpnService.state,
|
||||||
val vpnState get() = vpnService.state.stateIn(viewModelScope,
|
dataStoreManager.locationDisclosureFlow,
|
||||||
SharingStarted.WhileSubscribed(5_000L), Tunnel.State.DOWN)
|
){ settings, tunnels, tunnelState, locationDisclosure ->
|
||||||
|
SettingsUiState(settings, tunnels, tunnelState, locationDisclosure ?: false, false)
|
||||||
|
}.stateIn(viewModelScope,
|
||||||
|
SharingStarted.WhileSubscribed(5_000L), SettingsUiState())
|
||||||
|
|
||||||
suspend fun onSaveTrustedSSID(ssid: String) {
|
fun onSaveTrustedSSID(ssid: String) {
|
||||||
val trimmed = ssid.trim()
|
val trimmed = ssid.trim()
|
||||||
if (!settings.value.trustedNetworkSSIDs.contains(trimmed)) {
|
if (!uiState.value.settings.trustedNetworkSSIDs.contains(trimmed)) {
|
||||||
settings.value.trustedNetworkSSIDs.add(trimmed)
|
uiState.value.settings.trustedNetworkSSIDs.add(trimmed)
|
||||||
settingsRepo.save(settings.value)
|
saveSettings(uiState.value.settings)
|
||||||
} else {
|
} else {
|
||||||
throw WgTunnelException("SSID already exists.")
|
throw WgTunnelException("SSID already exists.")
|
||||||
}
|
}
|
||||||
|
@ -55,39 +58,37 @@ constructor(
|
||||||
return dataStoreManager.getFromStore(DataStoreManager.LOCATION_DISCLOSURE_SHOWN) ?: false
|
return dataStoreManager.getFromStore(DataStoreManager.LOCATION_DISCLOSURE_SHOWN) ?: false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setLocationDisclosureShown() {
|
fun setLocationDisclosureShown() = viewModelScope.launch {
|
||||||
viewModelScope.launch {
|
|
||||||
dataStoreManager.saveToDataStore(DataStoreManager.LOCATION_DISCLOSURE_SHOWN, true)
|
dataStoreManager.saveToDataStore(DataStoreManager.LOCATION_DISCLOSURE_SHOWN, true)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun onToggleTunnelOnMobileData() {
|
fun onToggleTunnelOnMobileData() {
|
||||||
settingsRepo.save(
|
saveSettings(
|
||||||
settings.value.copy(
|
uiState.value.settings.copy(
|
||||||
isTunnelOnMobileDataEnabled = !settings.value.isTunnelOnMobileDataEnabled
|
isTunnelOnMobileDataEnabled = !uiState.value.settings.isTunnelOnMobileDataEnabled
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun onDeleteTrustedSSID(ssid: String) {
|
fun onDeleteTrustedSSID(ssid: String) {
|
||||||
settings.value.trustedNetworkSSIDs.remove(ssid)
|
uiState.value.settings.trustedNetworkSSIDs.remove(ssid)
|
||||||
settingsRepo.save(settings.value)
|
saveSettings(uiState.value.settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getDefaultTunnelOrFirst() : String {
|
private suspend fun getDefaultTunnelOrFirst() : String {
|
||||||
return settings.value.defaultTunnel ?: tunnelRepo.getAll().first().wgQuick
|
return uiState.value.settings.defaultTunnel ?: tunnelRepo.getAll().first().wgQuick
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun toggleAutoTunnel() {
|
fun toggleAutoTunnel() = viewModelScope.launch {
|
||||||
val defaultTunnel = getDefaultTunnelOrFirst()
|
val defaultTunnel = getDefaultTunnelOrFirst()
|
||||||
if (settings.value.isAutoTunnelEnabled) {
|
if (uiState.value.settings.isAutoTunnelEnabled) {
|
||||||
ServiceManager.stopWatcherService(application)
|
ServiceManager.stopWatcherService(application)
|
||||||
} else {
|
} else {
|
||||||
ServiceManager.startWatcherService(application, defaultTunnel)
|
ServiceManager.startWatcherService(application, defaultTunnel)
|
||||||
}
|
}
|
||||||
saveSettings(
|
saveSettings(
|
||||||
settings.value.copy(
|
uiState.value.settings.copy(
|
||||||
isAutoTunnelEnabled = settings.value.isAutoTunnelEnabled,
|
isAutoTunnelEnabled = uiState.value.settings.isAutoTunnelEnabled,
|
||||||
defaultTunnel = defaultTunnel
|
defaultTunnel = defaultTunnel
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -95,20 +96,20 @@ constructor(
|
||||||
|
|
||||||
suspend fun onToggleAlwaysOnVPN() {
|
suspend fun onToggleAlwaysOnVPN() {
|
||||||
val updatedSettings =
|
val updatedSettings =
|
||||||
settings.value.copy(
|
uiState.value.settings.copy(
|
||||||
isAlwaysOnVpnEnabled = !settings.value.isAlwaysOnVpnEnabled,
|
isAlwaysOnVpnEnabled = !uiState.value.settings.isAlwaysOnVpnEnabled,
|
||||||
defaultTunnel = getDefaultTunnelOrFirst()
|
defaultTunnel = getDefaultTunnelOrFirst()
|
||||||
)
|
)
|
||||||
saveSettings(updatedSettings)
|
saveSettings(updatedSettings)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun saveSettings(settings: Settings) {
|
private fun saveSettings(settings: Settings) = viewModelScope.launch {
|
||||||
settingsRepo.save(settings)
|
settingsRepository.save(settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun onToggleTunnelOnEthernet() {
|
fun onToggleTunnelOnEthernet() {
|
||||||
saveSettings(settings.value.copy(
|
saveSettings(uiState.value.settings.copy(
|
||||||
isTunnelOnEthernetEnabled = !settings.value.isTunnelOnEthernetEnabled
|
isTunnelOnEthernetEnabled = !uiState.value.settings.isTunnelOnEthernetEnabled
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,40 +123,40 @@ constructor(
|
||||||
return (!isLocationServicesEnabled() && Build.VERSION.SDK_INT > Build.VERSION_CODES.P)
|
return (!isLocationServicesEnabled() && Build.VERSION.SDK_INT > Build.VERSION_CODES.P)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun onToggleShortcutsEnabled() {
|
fun onToggleShortcutsEnabled() {
|
||||||
saveSettings(
|
saveSettings(
|
||||||
settings.value.copy(
|
uiState.value.settings.copy(
|
||||||
isShortcutsEnabled = !settings.value.isShortcutsEnabled
|
isShortcutsEnabled = !uiState.value.settings.isShortcutsEnabled
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun onToggleBatterySaver() {
|
fun onToggleBatterySaver() {
|
||||||
saveSettings(
|
saveSettings(
|
||||||
settings.value.copy(
|
uiState.value.settings.copy(
|
||||||
isBatterySaverEnabled = !settings.value.isBatterySaverEnabled
|
isBatterySaverEnabled = !uiState.value.settings.isBatterySaverEnabled
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun saveKernelMode(on: Boolean) {
|
private fun saveKernelMode(on: Boolean) {
|
||||||
saveSettings(
|
saveSettings(
|
||||||
settings.value.copy(
|
uiState.value.settings.copy(
|
||||||
isKernelEnabled = on
|
isKernelEnabled = on
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun onToggleTunnelOnWifi() {
|
fun onToggleTunnelOnWifi() {
|
||||||
saveSettings(
|
saveSettings(
|
||||||
settings.value.copy(
|
uiState.value.settings.copy(
|
||||||
isTunnelOnWifiEnabled = !settings.value.isTunnelOnWifiEnabled
|
isTunnelOnWifiEnabled = !uiState.value.settings.isTunnelOnWifiEnabled
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun onToggleKernelMode() {
|
fun onToggleKernelMode() = viewModelScope.launch {
|
||||||
if (!settings.value.isKernelEnabled) {
|
if (!uiState.value.settings.isKernelEnabled) {
|
||||||
try {
|
try {
|
||||||
rootShell.start()
|
rootShell.start()
|
||||||
Timber.d("Root shell accepted!")
|
Timber.d("Root shell accepted!")
|
||||||
|
|
|
@ -51,194 +51,160 @@ import com.zaneschepke.wireguardautotunnel.BuildConfig
|
||||||
import com.zaneschepke.wireguardautotunnel.Constants
|
import com.zaneschepke.wireguardautotunnel.Constants
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
import com.zaneschepke.wireguardautotunnel.WireGuardAutoTunnel
|
import com.zaneschepke.wireguardautotunnel.WireGuardAutoTunnel
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.SettingsViewModel
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SupportScreen(
|
fun SupportScreen(
|
||||||
viewModel: SettingsViewModel = hiltViewModel(),
|
viewModel: SupportViewModel = hiltViewModel(),
|
||||||
padding: PaddingValues,
|
padding: PaddingValues,
|
||||||
focusRequester: FocusRequester
|
focusRequester: FocusRequester
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val fillMaxWidth = .85f
|
val fillMaxWidth = .85f
|
||||||
|
|
||||||
val settings by viewModel.settings.collectAsStateWithLifecycle()
|
val settings by viewModel.settings.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
fun openWebPage(url: String) {
|
fun openWebPage(url: String) {
|
||||||
val webpage: Uri = Uri.parse(url)
|
val webpage: Uri = Uri.parse(url)
|
||||||
val intent = Intent(Intent.ACTION_VIEW, webpage)
|
val intent = Intent(Intent.ACTION_VIEW, webpage)
|
||||||
context.startActivity(intent)
|
context.startActivity(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun launchEmail() {
|
fun launchEmail() {
|
||||||
val intent =
|
val intent =
|
||||||
Intent(Intent.ACTION_SEND).apply {
|
Intent(Intent.ACTION_SEND).apply {
|
||||||
type = Constants.EMAIL_MIME_TYPE
|
type = Constants.EMAIL_MIME_TYPE
|
||||||
putExtra(Intent.EXTRA_EMAIL, context.getString(R.string.my_email))
|
putExtra(Intent.EXTRA_EMAIL, context.getString(R.string.my_email))
|
||||||
putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.email_subject))
|
putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.email_subject))
|
||||||
}
|
}
|
||||||
startActivity(
|
startActivity(context, createChooser(intent, context.getString(R.string.email_chooser)), null)
|
||||||
context,
|
}
|
||||||
createChooser(intent, context.getString(R.string.email_chooser)),
|
|
||||||
null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
verticalArrangement = Arrangement.Top,
|
verticalArrangement = Arrangement.Top,
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier.fillMaxSize()
|
||||||
.fillMaxSize()
|
.verticalScroll(rememberScrollState())
|
||||||
.verticalScroll(rememberScrollState())
|
.focusable()
|
||||||
.focusable()
|
.padding(padding)) {
|
||||||
.padding(padding)
|
|
||||||
) {
|
|
||||||
Surface(
|
Surface(
|
||||||
tonalElevation = 2.dp,
|
tonalElevation = 2.dp,
|
||||||
shadowElevation = 2.dp,
|
shadowElevation = 2.dp,
|
||||||
shape = RoundedCornerShape(12.dp),
|
shape = RoundedCornerShape(12.dp),
|
||||||
color = MaterialTheme.colorScheme.surface,
|
color = MaterialTheme.colorScheme.surface,
|
||||||
modifier =
|
modifier =
|
||||||
(
|
(if (WireGuardAutoTunnel.isRunningOnAndroidTv(context)) {
|
||||||
if (WireGuardAutoTunnel.isRunningOnAndroidTv(context)) {
|
Modifier.height(IntrinsicSize.Min)
|
||||||
Modifier
|
.fillMaxWidth(fillMaxWidth)
|
||||||
.height(IntrinsicSize.Min)
|
.padding(top = 10.dp)
|
||||||
.fillMaxWidth(fillMaxWidth)
|
|
||||||
.padding(top = 10.dp)
|
|
||||||
} else {
|
} else {
|
||||||
Modifier
|
Modifier.fillMaxWidth(fillMaxWidth).padding(top = 20.dp)
|
||||||
.fillMaxWidth(fillMaxWidth)
|
})
|
||||||
.padding(top = 20.dp)
|
.padding(bottom = 25.dp)) {
|
||||||
}
|
Column(modifier = Modifier.padding(20.dp)) {
|
||||||
).padding(bottom = 25.dp)
|
|
||||||
) {
|
|
||||||
Column(modifier = Modifier.padding(20.dp)) {
|
|
||||||
Text(
|
Text(
|
||||||
stringResource(R.string.thank_you),
|
stringResource(R.string.thank_you),
|
||||||
textAlign = TextAlign.Start,
|
textAlign = TextAlign.Start,
|
||||||
modifier = Modifier.padding(bottom = 20.dp),
|
modifier = Modifier.padding(bottom = 20.dp),
|
||||||
fontSize = 16.sp
|
fontSize = 16.sp)
|
||||||
)
|
|
||||||
Text(
|
Text(
|
||||||
stringResource(id = R.string.support_help_text),
|
stringResource(id = R.string.support_help_text),
|
||||||
textAlign = TextAlign.Start,
|
textAlign = TextAlign.Start,
|
||||||
fontSize = 16.sp,
|
fontSize = 16.sp,
|
||||||
modifier = Modifier.padding(bottom = 20.dp)
|
modifier = Modifier.padding(bottom = 20.dp))
|
||||||
)
|
TextButton(
|
||||||
TextButton(onClick = {
|
onClick = { openWebPage(context.resources.getString(R.string.docs_url)) },
|
||||||
openWebPage(context.resources.getString(R.string.docs_url))
|
modifier = Modifier.padding(vertical = 5.dp).focusRequester(focusRequester)) {
|
||||||
}, modifier = Modifier.padding(vertical = 5.dp).focusRequester(focusRequester)) {
|
Row(
|
||||||
Row(
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
modifier = Modifier.fillMaxWidth()) {
|
||||||
modifier = Modifier.fillMaxWidth()
|
Row {
|
||||||
) {
|
Icon(Icons.Rounded.Book, stringResource(id = R.string.docs))
|
||||||
Row {
|
Text(
|
||||||
Icon(Icons.Rounded.Book, stringResource(id = R.string.docs))
|
stringResource(id = R.string.docs_description),
|
||||||
Text(
|
textAlign = TextAlign.Justify,
|
||||||
stringResource(id = R.string.docs_description),
|
modifier = Modifier.padding(start = 10.dp))
|
||||||
textAlign = TextAlign.Justify,
|
}
|
||||||
modifier = Modifier.padding(start = 10.dp)
|
Icon(Icons.Rounded.ArrowForward, stringResource(id = R.string.go))
|
||||||
)
|
}
|
||||||
}
|
|
||||||
Icon(Icons.Rounded.ArrowForward, stringResource(id = R.string.go))
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Divider(color = MaterialTheme.colorScheme.onBackground, thickness = 0.5.dp)
|
Divider(color = MaterialTheme.colorScheme.onBackground, thickness = 0.5.dp)
|
||||||
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)) {
|
||||||
) {
|
Row(
|
||||||
Row(
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
modifier = Modifier.fillMaxWidth()) {
|
||||||
modifier = Modifier.fillMaxWidth()
|
Row {
|
||||||
) {
|
Icon(
|
||||||
Row {
|
imageVector = ImageVector.vectorResource(R.drawable.discord),
|
||||||
Icon(
|
stringResource(id = R.string.discord),
|
||||||
imageVector = ImageVector.vectorResource(R.drawable.discord),
|
Modifier.size(25.dp))
|
||||||
stringResource(
|
Text(
|
||||||
id = R.string.discord
|
stringResource(id = R.string.discord_description),
|
||||||
),
|
textAlign = TextAlign.Justify,
|
||||||
Modifier.size(25.dp)
|
modifier = Modifier.padding(start = 10.dp))
|
||||||
)
|
}
|
||||||
Text(
|
Icon(Icons.Rounded.ArrowForward, stringResource(id = R.string.go))
|
||||||
stringResource(id = R.string.discord_description),
|
}
|
||||||
textAlign = TextAlign.Justify,
|
|
||||||
modifier = Modifier.padding(start = 10.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Icon(Icons.Rounded.ArrowForward, stringResource(id = R.string.go))
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Divider(color = MaterialTheme.colorScheme.onBackground, thickness = 0.5.dp)
|
Divider(color = MaterialTheme.colorScheme.onBackground, thickness = 0.5.dp)
|
||||||
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)) {
|
||||||
) {
|
Row(
|
||||||
Row(
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
modifier = Modifier.fillMaxWidth()) {
|
||||||
modifier = Modifier.fillMaxWidth()
|
Row {
|
||||||
) {
|
Icon(
|
||||||
Row {
|
imageVector = ImageVector.vectorResource(R.drawable.github),
|
||||||
Icon(
|
stringResource(id = R.string.github),
|
||||||
imageVector = ImageVector.vectorResource(R.drawable.github),
|
Modifier.size(25.dp))
|
||||||
stringResource(
|
Text(
|
||||||
id = R.string.github
|
"Open an issue",
|
||||||
),
|
textAlign = TextAlign.Justify,
|
||||||
Modifier.size(25.dp)
|
modifier = Modifier.padding(start = 10.dp))
|
||||||
)
|
}
|
||||||
Text(
|
Icon(Icons.Rounded.ArrowForward, stringResource(id = R.string.go))
|
||||||
"Open an issue",
|
}
|
||||||
textAlign = TextAlign.Justify,
|
|
||||||
modifier = Modifier.padding(start = 10.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Icon(Icons.Rounded.ArrowForward, stringResource(id = R.string.go))
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Divider(color = MaterialTheme.colorScheme.onBackground, thickness = 0.5.dp)
|
Divider(color = MaterialTheme.colorScheme.onBackground, thickness = 0.5.dp)
|
||||||
TextButton(
|
TextButton(
|
||||||
onClick = { launchEmail() },
|
onClick = { launchEmail() }, modifier = Modifier.padding(vertical = 5.dp)) {
|
||||||
modifier = Modifier.padding(vertical = 5.dp)
|
Row(
|
||||||
) {
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
Row(
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
modifier = Modifier.fillMaxWidth()) {
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
Row {
|
||||||
modifier = Modifier.fillMaxWidth()
|
Icon(Icons.Rounded.Mail, stringResource(id = R.string.email))
|
||||||
) {
|
Text(
|
||||||
Row {
|
stringResource(id = R.string.email_description),
|
||||||
Icon(Icons.Rounded.Mail, stringResource(id = R.string.email))
|
textAlign = TextAlign.Justify,
|
||||||
Text(
|
modifier = Modifier.padding(start = 10.dp))
|
||||||
stringResource(id = R.string.email_description),
|
}
|
||||||
textAlign = TextAlign.Justify,
|
Icon(Icons.Rounded.ArrowForward, stringResource(id = R.string.go))
|
||||||
modifier = Modifier.padding(start = 10.dp)
|
}
|
||||||
)
|
|
||||||
}
|
|
||||||
Icon(Icons.Rounded.ArrowForward, stringResource(id = R.string.go))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
Text(
|
Text(
|
||||||
stringResource(id = R.string.privacy_policy),
|
stringResource(id = R.string.privacy_policy),
|
||||||
style = TextStyle(textDecoration = TextDecoration.Underline),
|
style = TextStyle(textDecoration = TextDecoration.Underline),
|
||||||
fontSize = 16.sp,
|
fontSize = 16.sp,
|
||||||
modifier =
|
modifier =
|
||||||
Modifier.clickable {
|
Modifier.clickable {
|
||||||
openWebPage(context.resources.getString(R.string.privacy_policy_url))
|
openWebPage(context.resources.getString(R.string.privacy_policy_url))
|
||||||
}
|
})
|
||||||
)
|
|
||||||
Row(
|
Row(
|
||||||
horizontalArrangement = Arrangement.spacedBy(25.dp),
|
horizontalArrangement = Arrangement.spacedBy(25.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier.padding(25.dp)
|
modifier = Modifier.padding(25.dp)) {
|
||||||
) {
|
Text("Version: ${BuildConfig.VERSION_NAME}")
|
||||||
Text("Version: ${BuildConfig.VERSION_NAME}")
|
Text("Mode: ${if (settings.isKernelEnabled) "Kernel" else "Userspace" }")
|
||||||
Text("Mode: ${if (settings.isKernelEnabled) "Kernel" else "Userspace" }")
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,9 @@ package com.zaneschepke.wireguardautotunnel.ui.screens.support
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.SettingsDoa
|
import com.zaneschepke.wireguardautotunnel.data.SettingsDao
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.model.Settings
|
import com.zaneschepke.wireguardautotunnel.data.model.Settings
|
||||||
|
import com.zaneschepke.wireguardautotunnel.data.repository.SettingsRepository
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
@ -13,13 +14,13 @@ import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class SupportViewModel @Inject constructor(
|
class SupportViewModel @Inject constructor(
|
||||||
private val settingsRepo: SettingsDoa
|
private val settingsRepository: SettingsRepository
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
private val _settings = MutableStateFlow(Settings())
|
private val _settings = MutableStateFlow(Settings())
|
||||||
val settings get() = _settings.asStateFlow()
|
val settings = _settings.asStateFlow()
|
||||||
init {
|
init {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
_settings.value = settingsRepo.getAll().first()
|
_settings.value = settingsRepository.getAll().first()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue