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