mirror of
https://github.com/wgtunnel/android.git
synced 2026-06-02 00:29:08 +02:00
fix: navigation sync bug
This commit is contained in:
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
-keep class com.zaneschepke.wireguardautotunnel.ui.navigation.Route { *; }
|
||||
-keep class com.zaneschepke.wireguardautotunnel.ui.navigation.Route$** { *; }
|
||||
-keepclassmembers class com.zaneschepke.wireguardautotunnel.ui.navigation.Route$** { *; }
|
||||
@@ -4,10 +4,10 @@ import androidx.room.testing.MigrationTestHelper
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import com.zaneschepke.wireguardautotunnel.data.AppDatabase
|
||||
import java.io.IOException
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import java.io.IOException
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class MigrationTest {
|
||||
|
||||
@@ -50,6 +50,7 @@ import com.zaneschepke.wireguardautotunnel.ui.common.snackbar.CustomSnackBar
|
||||
import com.zaneschepke.wireguardautotunnel.ui.navigation.Route
|
||||
import com.zaneschepke.wireguardautotunnel.ui.navigation.components.BottomNavbar
|
||||
import com.zaneschepke.wireguardautotunnel.ui.navigation.components.DynamicTopAppBar
|
||||
import com.zaneschepke.wireguardautotunnel.ui.navigation.components.currentBackStackEntryAsNavbarState
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.autotunnel.AutoTunnelScreen
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.autotunnel.advanced.AutoTunnelAdvancedScreen
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.autotunnel.detection.WifiDetectionMethodScreen
|
||||
@@ -79,10 +80,11 @@ import com.zaneschepke.wireguardautotunnel.util.extensions.*
|
||||
import com.zaneschepke.wireguardautotunnel.viewmodel.*
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import de.raphaelebner.roomdatabasebackup.core.RoomBackup
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import xyz.teamgravity.pin_lock_compose.PinManager
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.launch
|
||||
import xyz.teamgravity.pin_lock_compose.PinManager
|
||||
|
||||
@AndroidEntryPoint
|
||||
class MainActivity : AppCompatActivity() {
|
||||
@@ -132,6 +134,7 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
val navState by navController.currentBackStackEntryAsNavbarState(viewModel)
|
||||
val snackbar = remember { SnackbarHostState() }
|
||||
var showVpnPermissionDialog by remember { mutableStateOf(false) }
|
||||
var vpnPermissionDenied by remember { mutableStateOf(false) }
|
||||
@@ -139,6 +142,8 @@ class MainActivity : AppCompatActivity() {
|
||||
mutableStateOf<Pair<AppMode?, TunnelConf?>>(Pair(null, null))
|
||||
}
|
||||
|
||||
LaunchedEffect(navState) { Timber.d("New navbar state $navState") }
|
||||
|
||||
val vpnActivity =
|
||||
rememberLauncherForActivityResult(
|
||||
ActivityResultContracts.StartActivityForResult(),
|
||||
@@ -237,13 +242,9 @@ class MainActivity : AppCompatActivity() {
|
||||
)
|
||||
}
|
||||
},
|
||||
topBar = { DynamicTopAppBar(appState.navBarState) },
|
||||
topBar = { DynamicTopAppBar(navState) },
|
||||
bottomBar = {
|
||||
BottomNavbar(
|
||||
appState.isAutoTunnelActive,
|
||||
appState.navBarState,
|
||||
navController,
|
||||
)
|
||||
BottomNavbar(appState.isAutoTunnelActive, navState, navController)
|
||||
},
|
||||
) { padding ->
|
||||
Box(
|
||||
|
||||
@@ -17,7 +17,6 @@ import com.zaneschepke.wireguardautotunnel.domain.enums.BackendMode
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.AppDataRepository
|
||||
import com.zaneschepke.wireguardautotunnel.util.ReleaseTree
|
||||
import dagger.hilt.android.HiltAndroidApp
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.cancel
|
||||
@@ -26,6 +25,7 @@ import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltAndroidApp
|
||||
class WireGuardAutoTunnel : Application(), Configuration.Provider {
|
||||
|
||||
+1
-1
@@ -8,9 +8,9 @@ import com.zaneschepke.wireguardautotunnel.core.tunnel.TunnelManager
|
||||
import com.zaneschepke.wireguardautotunnel.di.ApplicationScope
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.TunnelRepository
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class KernelReceiver : BroadcastReceiver() {
|
||||
|
||||
+1
-1
@@ -10,9 +10,9 @@ import com.zaneschepke.wireguardautotunnel.di.ApplicationScope
|
||||
import com.zaneschepke.wireguardautotunnel.domain.enums.NotificationAction
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.TunnelRepository
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class NotificationActionReceiver : BroadcastReceiver() {
|
||||
|
||||
+1
-1
@@ -9,10 +9,10 @@ import com.zaneschepke.wireguardautotunnel.di.ApplicationScope
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.AppDataRepository
|
||||
import com.zaneschepke.wireguardautotunnel.util.Constants
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class RemoteControlReceiver : BroadcastReceiver() {
|
||||
|
||||
+1
-1
@@ -10,11 +10,11 @@ import com.zaneschepke.wireguardautotunnel.di.ApplicationScope
|
||||
import com.zaneschepke.wireguardautotunnel.di.IoDispatcher
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.AppDataRepository
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class RestartReceiver : BroadcastReceiver() {
|
||||
|
||||
+1
-1
@@ -21,11 +21,11 @@ import com.zaneschepke.wireguardautotunnel.util.Constants
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.distinctByKeys
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.ktor.util.collections.*
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class TunnelForegroundService : LifecycleService() {
|
||||
|
||||
+3
-3
@@ -29,14 +29,14 @@ import com.zaneschepke.wireguardautotunnel.util.extensions.Tunnels
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.to
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.toMillis
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Provider
|
||||
import kotlin.math.pow
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Provider
|
||||
import kotlin.math.pow
|
||||
|
||||
@AndroidEntryPoint
|
||||
class AutoTunnelService : LifecycleService() {
|
||||
|
||||
+1
-1
@@ -11,9 +11,9 @@ import androidx.lifecycle.lifecycleScope
|
||||
import com.zaneschepke.wireguardautotunnel.core.service.ServiceManager
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.AppDataRepository
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class AutoTunnelControlTile : TileService(), LifecycleOwner {
|
||||
|
||||
+1
-1
@@ -17,9 +17,9 @@ import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.AppDataRepository
|
||||
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelState
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class TunnelControlTile : TileService(), LifecycleOwner {
|
||||
|
||||
+1
-1
@@ -9,10 +9,10 @@ import com.zaneschepke.wireguardautotunnel.core.tunnel.TunnelProvider
|
||||
import com.zaneschepke.wireguardautotunnel.di.ApplicationScope
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.AppDataRepository
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class ShortcutsActivity : ComponentActivity() {
|
||||
|
||||
@@ -12,8 +12,6 @@ import com.zaneschepke.wireguardautotunnel.domain.state.TunnelState
|
||||
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelStatistics
|
||||
import com.zaneschepke.wireguardautotunnel.ui.state.ConfigProxy
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.asTunnelState
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import kotlin.coroutines.cancellation.CancellationException
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
@@ -23,6 +21,8 @@ import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import org.amnezia.awg.crypto.Key
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import kotlin.coroutines.cancellation.CancellationException
|
||||
|
||||
abstract class BaseTunnel(
|
||||
private val applicationScope: CoroutineScope,
|
||||
|
||||
@@ -14,9 +14,9 @@ import com.zaneschepke.wireguardautotunnel.domain.repository.AppDataRepository
|
||||
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelStatistics
|
||||
import com.zaneschepke.wireguardautotunnel.domain.state.WireGuardStatistics
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.toBackendCoreException
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class KernelTunnel
|
||||
@Inject
|
||||
|
||||
@@ -13,11 +13,6 @@ import com.zaneschepke.wireguardautotunnel.domain.repository.AppDataRepository
|
||||
import com.zaneschepke.wireguardautotunnel.domain.state.PingState
|
||||
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelState
|
||||
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelStatistics
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import javax.inject.Inject
|
||||
import kotlin.concurrent.atomics.AtomicBoolean
|
||||
import kotlin.concurrent.atomics.AtomicReference
|
||||
import kotlin.concurrent.atomics.ExperimentalAtomicApi
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
@@ -25,6 +20,11 @@ import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.plus
|
||||
import org.amnezia.awg.crypto.Key
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import javax.inject.Inject
|
||||
import kotlin.concurrent.atomics.AtomicBoolean
|
||||
import kotlin.concurrent.atomics.AtomicReference
|
||||
import kotlin.concurrent.atomics.ExperimentalAtomicApi
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class TunnelManager
|
||||
|
||||
@@ -11,11 +11,11 @@ import com.zaneschepke.wireguardautotunnel.util.extensions.toMillis
|
||||
import com.zaneschepke.wireguardautotunnel.util.network.NetworkUtils
|
||||
import dagger.hilt.android.scopes.ServiceScoped
|
||||
import io.ktor.util.collections.*
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
import org.amnezia.awg.crypto.Key
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@ServiceScoped
|
||||
class TunnelMonitor
|
||||
|
||||
@@ -8,10 +8,10 @@ import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.state.PingState
|
||||
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelState
|
||||
import com.zaneschepke.wireguardautotunnel.domain.state.TunnelStatistics
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import org.amnezia.awg.crypto.Key
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
interface TunnelProvider {
|
||||
/** Starts the specified tunnel configuration. */
|
||||
|
||||
+3
-3
@@ -13,9 +13,6 @@ import com.zaneschepke.wireguardautotunnel.domain.state.TunnelStatistics
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.asAmBackendMode
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.asBackendMode
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.toBackendCoreException
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import org.amnezia.awg.backend.Backend
|
||||
import org.amnezia.awg.backend.BackendException
|
||||
@@ -27,6 +24,9 @@ import org.amnezia.awg.config.proxy.HttpProxy
|
||||
import org.amnezia.awg.config.proxy.Proxy
|
||||
import org.amnezia.awg.config.proxy.Socks5Proxy
|
||||
import timber.log.Timber
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class UserspaceTunnel
|
||||
@Inject
|
||||
|
||||
@@ -8,10 +8,10 @@ import com.zaneschepke.wireguardautotunnel.di.IoDispatcher
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.AppDataRepository
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedInject
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
@HiltWorker
|
||||
class ServiceWorker
|
||||
|
||||
@@ -7,7 +7,6 @@ import androidx.datastore.preferences.core.edit
|
||||
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||
import androidx.datastore.preferences.preferencesDataStore
|
||||
import com.zaneschepke.wireguardautotunnel.di.IoDispatcher
|
||||
import java.io.IOException
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.first
|
||||
@@ -15,6 +14,7 @@ import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
import java.io.IOException
|
||||
|
||||
class DataStoreManager(
|
||||
private val context: Context,
|
||||
|
||||
+1
-1
@@ -14,10 +14,10 @@ import io.ktor.client.request.*
|
||||
import io.ktor.client.statement.*
|
||||
import io.ktor.http.*
|
||||
import io.ktor.utils.io.*
|
||||
import java.io.File
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
|
||||
class GitHubUpdateRepository(
|
||||
private val gitHubApi: GitHubApi,
|
||||
|
||||
+1
-1
@@ -11,12 +11,12 @@ import com.zaneschepke.wireguardautotunnel.domain.model.InstalledPackage
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.InstalledPackageRepository
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.getAllInternetCapablePackages
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.getFriendlyAppName
|
||||
import javax.inject.Singleton
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class InstalledAndroidPackageRepository(
|
||||
|
||||
@@ -15,10 +15,10 @@ import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
|
||||
@@ -20,9 +20,9 @@ import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import io.ktor.client.*
|
||||
import javax.inject.Singleton
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
|
||||
@@ -18,7 +18,6 @@ import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
@@ -28,6 +27,7 @@ import org.amnezia.awg.backend.Backend
|
||||
import org.amnezia.awg.backend.GoBackend
|
||||
import org.amnezia.awg.backend.ProxyGoBackend
|
||||
import org.amnezia.awg.backend.RootTunnelActionHandler
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
|
||||
@@ -4,9 +4,9 @@ import android.os.Parcelable
|
||||
import com.wireguard.android.backend.Tunnel
|
||||
import com.wireguard.config.Config
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.*
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import java.io.InputStream
|
||||
import java.nio.charset.StandardCharsets
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Parcelize
|
||||
data class TunnelConf(
|
||||
|
||||
+1
-1
@@ -1,9 +1,9 @@
|
||||
package com.zaneschepke.wireguardautotunnel.domain.repository
|
||||
|
||||
import com.zaneschepke.wireguardautotunnel.domain.sideeffect.GlobalSideEffect
|
||||
import javax.inject.Singleton
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.asSharedFlow
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class GlobalEffectRepository {
|
||||
|
||||
@@ -1,58 +1,61 @@
|
||||
package com.zaneschepke.wireguardautotunnel.ui.navigation
|
||||
|
||||
import androidx.annotation.Keep
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Keep
|
||||
@Serializable
|
||||
sealed class Route {
|
||||
@Serializable data object TunnelsGraph : Route()
|
||||
|
||||
@Serializable data object AutoTunnelGraph : Route()
|
||||
@Keep @Serializable data object TunnelsGraph : Route()
|
||||
|
||||
@Serializable data object SettingsGraph : Route()
|
||||
@Keep @Serializable data object AutoTunnelGraph : Route()
|
||||
|
||||
@Serializable data object SupportGraph : Route()
|
||||
@Keep @Serializable data object SettingsGraph : Route()
|
||||
|
||||
@Serializable data object Support : Route()
|
||||
@Keep @Serializable data object SupportGraph : Route()
|
||||
|
||||
@Serializable data object Lock : Route()
|
||||
@Keep @Serializable data object Support : Route()
|
||||
|
||||
@Serializable data object License : Route()
|
||||
@Keep @Serializable data object Lock : Route()
|
||||
|
||||
@Serializable data object Logs : Route()
|
||||
@Keep @Serializable data object License : Route()
|
||||
|
||||
@Serializable data object Appearance : Route()
|
||||
@Keep @Serializable data object Logs : Route()
|
||||
|
||||
@Serializable data object Language : Route()
|
||||
@Keep @Serializable data object Appearance : Route()
|
||||
|
||||
@Serializable data object Display : Route()
|
||||
@Keep @Serializable data object Language : Route()
|
||||
|
||||
@Serializable data object Tunnels : Route()
|
||||
@Keep @Serializable data object Display : Route()
|
||||
|
||||
@Serializable data class TunnelOptions(val id: Int) : Route()
|
||||
@Keep @Serializable data object Tunnels : Route()
|
||||
|
||||
@Serializable data class Config(val id: Int?) : Route()
|
||||
@Keep @Serializable data class TunnelOptions(val id: Int) : Route()
|
||||
|
||||
@Serializable data class SplitTunnel(val id: Int) : Route()
|
||||
@Keep @Serializable data class Config(val id: Int?) : Route()
|
||||
|
||||
@Serializable data class TunnelAutoTunnel(val id: Int) : Route()
|
||||
@Keep @Serializable data class SplitTunnel(val id: Int) : Route()
|
||||
|
||||
@Serializable data object Sort : Route()
|
||||
@Keep @Serializable data class TunnelAutoTunnel(val id: Int) : Route()
|
||||
|
||||
@Serializable data object Settings : Route()
|
||||
@Keep @Serializable data object Sort : Route()
|
||||
|
||||
@Serializable data object TunnelMonitoring : Route()
|
||||
@Keep @Serializable data object Settings : Route()
|
||||
|
||||
@Serializable data object SystemFeatures : Route()
|
||||
@Keep @Serializable data object TunnelMonitoring : Route()
|
||||
|
||||
@Serializable data object Dns : Route()
|
||||
@Keep @Serializable data object SystemFeatures : Route()
|
||||
|
||||
@Serializable data object ProxySettings : Route()
|
||||
@Keep @Serializable data object Dns : Route()
|
||||
|
||||
@Serializable data object AutoTunnel : Route()
|
||||
@Keep @Serializable data object ProxySettings : Route()
|
||||
|
||||
@Serializable data object AdvancedAutoTunnel : Route()
|
||||
@Keep @Serializable data object AutoTunnel : Route()
|
||||
|
||||
@Serializable data object WifiDetectionMethod : Route()
|
||||
@Keep @Serializable data object AdvancedAutoTunnel : Route()
|
||||
|
||||
@Serializable data object LocationDisclosure : Route()
|
||||
@Keep @Serializable data object WifiDetectionMethod : Route()
|
||||
|
||||
@Keep @Serializable data object LocationDisclosure : Route()
|
||||
}
|
||||
|
||||
+4
-18
@@ -17,7 +17,6 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavGraph.Companion.findStartDestination
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
@@ -25,7 +24,6 @@ import com.zaneschepke.wireguardautotunnel.ui.navigation.BottomNavItem
|
||||
import com.zaneschepke.wireguardautotunnel.ui.navigation.Route
|
||||
import com.zaneschepke.wireguardautotunnel.ui.state.NavbarState
|
||||
import com.zaneschepke.wireguardautotunnel.ui.theme.SilverTree
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.debounce
|
||||
|
||||
@Composable
|
||||
fun NavHostController.getCurrentGraph(): State<Route?> {
|
||||
@@ -54,44 +52,32 @@ fun BottomNavbar(
|
||||
) {
|
||||
|
||||
val currentGraph by navController.getCurrentGraph()
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
val navigateToDebounced =
|
||||
remember<(Route) -> Unit> {
|
||||
debounce(scope = coroutineScope, 150L) { route ->
|
||||
navController.navigate(route) {
|
||||
popUpTo(navController.graph.findStartDestination().id) { saveState = true }
|
||||
launchSingleTop = true
|
||||
restoreState = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val items =
|
||||
listOf(
|
||||
BottomNavItem(
|
||||
name = stringResource(R.string.tunnels),
|
||||
icon = Icons.Rounded.Home,
|
||||
onClick = { navigateToDebounced(Route.TunnelsGraph) },
|
||||
onClick = { navController.navigate(Route.TunnelsGraph) },
|
||||
route = Route.TunnelsGraph,
|
||||
),
|
||||
BottomNavItem(
|
||||
name = stringResource(R.string.auto_tunnel),
|
||||
icon = Icons.Rounded.Bolt,
|
||||
onClick = { navigateToDebounced(Route.AutoTunnelGraph) },
|
||||
onClick = { navController.navigate(Route.AutoTunnelGraph) },
|
||||
route = Route.AutoTunnelGraph,
|
||||
active = isAutoTunnelActive,
|
||||
),
|
||||
BottomNavItem(
|
||||
name = stringResource(R.string.settings),
|
||||
icon = Icons.Rounded.Settings,
|
||||
onClick = { navigateToDebounced(Route.SettingsGraph) },
|
||||
onClick = { navController.navigate(Route.SettingsGraph) },
|
||||
route = Route.SettingsGraph,
|
||||
),
|
||||
BottomNavItem(
|
||||
name = stringResource(R.string.support),
|
||||
icon = Icons.Rounded.QuestionMark,
|
||||
onClick = { navigateToDebounced(Route.SupportGraph) },
|
||||
onClick = { navController.navigate(Route.SupportGraph) },
|
||||
route = Route.SupportGraph,
|
||||
),
|
||||
)
|
||||
|
||||
+259
@@ -0,0 +1,259 @@
|
||||
package com.zaneschepke.wireguardautotunnel.ui.navigation.components
|
||||
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.rounded.Sort
|
||||
import androidx.compose.material.icons.rounded.*
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||
import androidx.navigation.toRoute
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.ActionIconButton
|
||||
import com.zaneschepke.wireguardautotunnel.ui.navigation.Route
|
||||
import com.zaneschepke.wireguardautotunnel.ui.sideeffect.LocalSideEffect
|
||||
import com.zaneschepke.wireguardautotunnel.ui.state.NavbarState
|
||||
import com.zaneschepke.wireguardautotunnel.viewmodel.SharedAppViewModel
|
||||
|
||||
@Composable
|
||||
fun NavHostController.currentBackStackEntryAsNavbarState(
|
||||
sharedViewModel: SharedAppViewModel
|
||||
): State<NavbarState> {
|
||||
val sharedState by sharedViewModel.container.stateFlow.collectAsStateWithLifecycle()
|
||||
val backStackEntry by currentBackStackEntryAsState()
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
val route =
|
||||
remember(backStackEntry) {
|
||||
backStackEntry?.destination?.route?.let {
|
||||
when (it.substringBefore("?").substringBefore("/").substringAfterLast(".")) {
|
||||
Route.Support::class.simpleName -> backStackEntry?.toRoute<Route.Support>()
|
||||
Route.Lock::class.simpleName -> backStackEntry?.toRoute<Route.Lock>()
|
||||
Route.License::class.simpleName -> backStackEntry?.toRoute<Route.License>()
|
||||
Route.Logs::class.simpleName -> backStackEntry?.toRoute<Route.Logs>()
|
||||
Route.Appearance::class.simpleName ->
|
||||
backStackEntry?.toRoute<Route.Appearance>()
|
||||
Route.Language::class.simpleName -> backStackEntry?.toRoute<Route.Language>()
|
||||
Route.Display::class.simpleName -> backStackEntry?.toRoute<Route.Display>()
|
||||
Route.Tunnels::class.simpleName -> backStackEntry?.toRoute<Route.Tunnels>()
|
||||
Route.TunnelOptions::class.simpleName ->
|
||||
backStackEntry?.toRoute<Route.TunnelOptions>()
|
||||
Route.Config::class.simpleName -> backStackEntry?.toRoute<Route.Config>()
|
||||
Route.SplitTunnel::class.simpleName ->
|
||||
backStackEntry?.toRoute<Route.SplitTunnel>()
|
||||
Route.TunnelAutoTunnel::class.simpleName ->
|
||||
backStackEntry?.toRoute<Route.TunnelAutoTunnel>()
|
||||
Route.Sort::class.simpleName -> backStackEntry?.toRoute<Route.Sort>()
|
||||
Route.Settings::class.simpleName -> backStackEntry?.toRoute<Route.Settings>()
|
||||
Route.TunnelMonitoring::class.simpleName ->
|
||||
backStackEntry?.toRoute<Route.TunnelMonitoring>()
|
||||
Route.SystemFeatures::class.simpleName ->
|
||||
backStackEntry?.toRoute<Route.SystemFeatures>()
|
||||
Route.Dns::class.simpleName -> backStackEntry?.toRoute<Route.Dns>()
|
||||
Route.ProxySettings::class.simpleName ->
|
||||
backStackEntry?.toRoute<Route.ProxySettings>()
|
||||
Route.AutoTunnel::class.simpleName ->
|
||||
backStackEntry?.toRoute<Route.AutoTunnel>()
|
||||
Route.AdvancedAutoTunnel::class.simpleName ->
|
||||
backStackEntry?.toRoute<Route.AdvancedAutoTunnel>()
|
||||
Route.WifiDetectionMethod::class.simpleName ->
|
||||
backStackEntry?.toRoute<Route.WifiDetectionMethod>()
|
||||
Route.LocationDisclosure::class.simpleName ->
|
||||
backStackEntry?.toRoute<Route.LocationDisclosure>()
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return produceState(initialValue = NavbarState(), route, sharedState.topNavActions) {
|
||||
value =
|
||||
when (route) {
|
||||
Route.AdvancedAutoTunnel ->
|
||||
NavbarState(
|
||||
showBottomItems = true,
|
||||
topTitle = { Text(stringResource(R.string.advanced_settings)) },
|
||||
)
|
||||
Route.Appearance ->
|
||||
NavbarState(
|
||||
showBottomItems = true,
|
||||
topTitle = { Text(stringResource(R.string.appearance)) },
|
||||
)
|
||||
Route.AutoTunnel ->
|
||||
NavbarState(
|
||||
showBottomItems = true,
|
||||
topTitle = { Text(stringResource(R.string.auto_tunnel)) },
|
||||
)
|
||||
is Route.Config -> {
|
||||
val tunnel = sharedState.tunnels.find { it.id == route.id }
|
||||
NavbarState(
|
||||
showBottomItems = true,
|
||||
topTitle = {
|
||||
val title = tunnel?.name ?: stringResource(R.string.new_tunnel)
|
||||
Text(title)
|
||||
},
|
||||
topTrailing = {
|
||||
ActionIconButton(Icons.Rounded.Save, R.string.save) {
|
||||
keyboardController?.hide()
|
||||
sharedViewModel.postSideEffect(LocalSideEffect.SaveChanges)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
Route.Display ->
|
||||
NavbarState(
|
||||
showBottomItems = true,
|
||||
topTitle = { Text(stringResource(R.string.display_theme)) },
|
||||
)
|
||||
Route.Dns ->
|
||||
NavbarState(
|
||||
showBottomItems = true,
|
||||
topTitle = { Text(stringResource(R.string.dns_settings)) },
|
||||
)
|
||||
Route.Language ->
|
||||
NavbarState(
|
||||
showBottomItems = true,
|
||||
topTitle = { Text(stringResource(R.string.language)) },
|
||||
)
|
||||
Route.License ->
|
||||
NavbarState(
|
||||
showBottomItems = true,
|
||||
topTitle = { Text(stringResource(R.string.licenses)) },
|
||||
)
|
||||
Route.LocationDisclosure -> NavbarState(showBottomItems = true)
|
||||
Route.Lock -> NavbarState(showBottomItems = false)
|
||||
Route.Logs ->
|
||||
NavbarState(
|
||||
showBottomItems = false,
|
||||
removeBottom = true,
|
||||
topTitle = { Text(stringResource(R.string.logs)) },
|
||||
topTrailing = {
|
||||
ActionIconButton(Icons.Rounded.Menu, R.string.quick_actions) {
|
||||
sharedViewModel.postSideEffect(LocalSideEffect.Sheet.LoggerActions)
|
||||
}
|
||||
},
|
||||
)
|
||||
Route.ProxySettings ->
|
||||
NavbarState(
|
||||
showBottomItems = true,
|
||||
topTitle = { Text(stringResource(R.string.proxy_settings)) },
|
||||
topTrailing = {
|
||||
ActionIconButton(Icons.Rounded.Save, R.string.save) {
|
||||
keyboardController?.hide()
|
||||
sharedViewModel.postSideEffect(LocalSideEffect.SaveChanges)
|
||||
}
|
||||
},
|
||||
)
|
||||
Route.Settings ->
|
||||
NavbarState(
|
||||
showBottomItems = true,
|
||||
topTitle = { Text(stringResource(R.string.settings)) },
|
||||
topTrailing = {
|
||||
ActionIconButton(
|
||||
Icons.Rounded.SettingsBackupRestore,
|
||||
R.string.quick_actions,
|
||||
) {
|
||||
sharedViewModel.postSideEffect(LocalSideEffect.Sheet.BackupApp)
|
||||
}
|
||||
},
|
||||
)
|
||||
Route.Sort ->
|
||||
NavbarState(
|
||||
showBottomItems = true,
|
||||
topTitle = { Text(stringResource(R.string.sort)) },
|
||||
topTrailing = {
|
||||
Row {
|
||||
ActionIconButton(Icons.Rounded.SortByAlpha, R.string.sort) {
|
||||
sharedViewModel.postSideEffect(LocalSideEffect.Sort)
|
||||
}
|
||||
ActionIconButton(Icons.Rounded.Save, R.string.save) {
|
||||
sharedViewModel.postSideEffect(LocalSideEffect.SaveChanges)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
is Route.SplitTunnel -> {
|
||||
val tunnel = sharedState.tunnels.find { it.id == route.id }
|
||||
NavbarState(
|
||||
topTitle = { Text(tunnel?.name ?: "") },
|
||||
topTrailing = {
|
||||
ActionIconButton(Icons.Rounded.Save, R.string.save) {
|
||||
sharedViewModel.postSideEffect(LocalSideEffect.SaveChanges)
|
||||
}
|
||||
},
|
||||
showBottomItems = true,
|
||||
)
|
||||
}
|
||||
Route.Support ->
|
||||
NavbarState(
|
||||
topTitle = { Text(stringResource(R.string.support)) },
|
||||
showBottomItems = true,
|
||||
)
|
||||
Route.SystemFeatures ->
|
||||
NavbarState(
|
||||
topTitle = { Text(stringResource(R.string.android_integrations)) },
|
||||
showBottomItems = true,
|
||||
)
|
||||
is Route.TunnelAutoTunnel -> {
|
||||
val tunnel = sharedState.tunnels.find { it.id == route.id }
|
||||
NavbarState(showBottomItems = true, topTitle = { Text(tunnel?.name ?: "") })
|
||||
}
|
||||
Route.TunnelMonitoring ->
|
||||
NavbarState(
|
||||
topTitle = { Text(stringResource(R.string.tunnel_monitoring)) },
|
||||
showBottomItems = true,
|
||||
)
|
||||
is Route.TunnelOptions -> {
|
||||
val tunnel = sharedState.tunnels.find { it.id == route.id }
|
||||
NavbarState(
|
||||
showBottomItems = true,
|
||||
topTitle = { Text(tunnel?.name ?: "") },
|
||||
topTrailing = {
|
||||
Row {
|
||||
ActionIconButton(Icons.Rounded.QrCode2, R.string.show_qr) {
|
||||
sharedViewModel.postSideEffect(LocalSideEffect.Modal.QR)
|
||||
}
|
||||
ActionIconButton(Icons.Rounded.Edit, R.string.edit_tunnel) {
|
||||
navigate(Route.Config(route.id))
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
Route.Tunnels ->
|
||||
NavbarState(
|
||||
topTitle = { Text(stringResource(R.string.tunnels)) },
|
||||
topTrailing =
|
||||
sharedState.topNavActions
|
||||
?: {
|
||||
Row {
|
||||
ActionIconButton(
|
||||
Icons.AutoMirrored.Rounded.Sort,
|
||||
R.string.sort,
|
||||
) {
|
||||
navigate(Route.Sort)
|
||||
}
|
||||
ActionIconButton(Icons.Rounded.Add, R.string.add_tunnel) {
|
||||
sharedViewModel.postSideEffect(
|
||||
LocalSideEffect.Sheet.ImportTunnels
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
showBottomItems = true,
|
||||
)
|
||||
Route.WifiDetectionMethod ->
|
||||
NavbarState(
|
||||
topTitle = { Text(stringResource(R.string.wifi_detection_method)) },
|
||||
showBottomItems = true,
|
||||
)
|
||||
Route.TunnelsGraph,
|
||||
Route.SettingsGraph,
|
||||
Route.AutoTunnelGraph,
|
||||
Route.SupportGraph,
|
||||
null -> NavbarState()
|
||||
}
|
||||
}
|
||||
}
|
||||
-13
@@ -22,7 +22,6 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.ui.LocalNavController
|
||||
import com.zaneschepke.wireguardautotunnel.ui.LocalSharedVm
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.SectionDivider
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.banner.WarningBanner
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
|
||||
@@ -32,7 +31,6 @@ import com.zaneschepke.wireguardautotunnel.ui.navigation.Route
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.autotunnel.components.AdvancedSettingsItem
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.autotunnel.components.networkTunnelingItems
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.autotunnel.components.wifiTunnelingItems
|
||||
import com.zaneschepke.wireguardautotunnel.ui.state.NavbarState
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.launchAppSettings
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.launchLocationServicesSettings
|
||||
import com.zaneschepke.wireguardautotunnel.viewmodel.AutoTunnelViewModel
|
||||
@@ -41,21 +39,10 @@ import com.zaneschepke.wireguardautotunnel.viewmodel.AutoTunnelViewModel
|
||||
@Composable
|
||||
fun AutoTunnelScreen(viewModel: AutoTunnelViewModel = hiltViewModel()) {
|
||||
val context = LocalContext.current
|
||||
val sharedViewModel = LocalSharedVm.current
|
||||
val navController = LocalNavController.current
|
||||
val autoTunnelState by viewModel.container.stateFlow.collectAsStateWithLifecycle()
|
||||
|
||||
if (!autoTunnelState.stateInitialized) return
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
sharedViewModel.updateNavbarState(
|
||||
NavbarState(
|
||||
showBottomItems = true,
|
||||
topTitle = { Text(stringResource(R.string.auto_tunnel)) },
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
var showLocationDialog by remember { mutableStateOf(false) }
|
||||
|
||||
val showLocationServicesWarning by
|
||||
|
||||
-14
@@ -12,7 +12,6 @@ import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -20,25 +19,12 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.ui.LocalSharedVm
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.dropdown.LabelledDropdown
|
||||
import com.zaneschepke.wireguardautotunnel.ui.state.NavbarState
|
||||
import com.zaneschepke.wireguardautotunnel.viewmodel.AutoTunnelViewModel
|
||||
|
||||
@Composable
|
||||
fun AutoTunnelAdvancedScreen(viewModel: AutoTunnelViewModel) {
|
||||
val sharedViewModel = LocalSharedVm.current
|
||||
val autoTunnelState by viewModel.container.stateFlow.collectAsStateWithLifecycle()
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
sharedViewModel.updateNavbarState(
|
||||
NavbarState(
|
||||
showBottomItems = true,
|
||||
topTitle = { Text(stringResource(R.string.advanced_settings)) },
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.Start,
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp, Alignment.Top),
|
||||
|
||||
-14
@@ -4,21 +4,16 @@ import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.data.model.WifiDetectionMethod
|
||||
import com.zaneschepke.wireguardautotunnel.ui.LocalSharedVm
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.IconSurfaceButton
|
||||
import com.zaneschepke.wireguardautotunnel.ui.state.NavbarState
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.asDescriptionString
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.asTitleString
|
||||
import com.zaneschepke.wireguardautotunnel.viewmodel.AutoTunnelViewModel
|
||||
@@ -30,15 +25,6 @@ fun WifiDetectionMethodScreen(viewModel: AutoTunnelViewModel) {
|
||||
|
||||
val autoTunnelState by viewModel.container.stateFlow.collectAsStateWithLifecycle()
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
sharedViewModel.updateNavbarState(
|
||||
NavbarState(
|
||||
topTitle = { Text(stringResource(R.string.wifi_detection_method)) },
|
||||
showBottomItems = true,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.Start,
|
||||
verticalArrangement = Arrangement.spacedBy(24.dp, Alignment.Top),
|
||||
|
||||
-5
@@ -10,20 +10,15 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.zaneschepke.wireguardautotunnel.ui.LocalNavController
|
||||
import com.zaneschepke.wireguardautotunnel.ui.LocalSharedVm
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SurfaceSelectionGroupButton
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.autotunnel.disclosure.components.LocationDisclosureHeader
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.autotunnel.disclosure.components.appSettingsItem
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.autotunnel.disclosure.components.skipItem
|
||||
import com.zaneschepke.wireguardautotunnel.ui.state.NavbarState
|
||||
import com.zaneschepke.wireguardautotunnel.viewmodel.AutoTunnelViewModel
|
||||
|
||||
@Composable
|
||||
fun LocationDisclosureScreen(viewModel: AutoTunnelViewModel) {
|
||||
val navController = LocalNavController.current
|
||||
val sharedViewModel = LocalSharedVm.current
|
||||
|
||||
LaunchedEffect(Unit) { sharedViewModel.updateNavbarState(NavbarState(showBottomItems = true)) }
|
||||
|
||||
LaunchedEffect(Unit) { viewModel.setLocationDisclosureShown() }
|
||||
|
||||
|
||||
+4
-4
@@ -3,14 +3,16 @@ package com.zaneschepke.wireguardautotunnel.ui.screens.pin
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.ui.LocalNavController
|
||||
import com.zaneschepke.wireguardautotunnel.ui.LocalSharedVm
|
||||
import com.zaneschepke.wireguardautotunnel.ui.navigation.Route
|
||||
import com.zaneschepke.wireguardautotunnel.ui.state.NavbarState
|
||||
import com.zaneschepke.wireguardautotunnel.util.StringValue
|
||||
import xyz.teamgravity.pin_lock_compose.PinLock
|
||||
import xyz.teamgravity.pin_lock_compose.PinManager
|
||||
@@ -22,8 +24,6 @@ fun PinLockScreen() {
|
||||
val pinAlreadyExists by rememberSaveable { mutableStateOf(PinManager.pinExists()) }
|
||||
var pinCreated by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
LaunchedEffect(Unit) { sharedViewModel.updateNavbarState(NavbarState(showBottomItems = false)) }
|
||||
|
||||
PinLock(
|
||||
title = {
|
||||
Text(
|
||||
|
||||
+11
-23
@@ -8,32 +8,27 @@ import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.SettingsBackupRestore
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.data.model.AppMode
|
||||
import com.zaneschepke.wireguardautotunnel.ui.LocalIsAndroidTV
|
||||
import com.zaneschepke.wireguardautotunnel.ui.LocalNavController
|
||||
import com.zaneschepke.wireguardautotunnel.ui.LocalSharedVm
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.SectionDivider
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.ActionIconButton
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SurfaceSelectionGroupButton
|
||||
import com.zaneschepke.wireguardautotunnel.ui.navigation.Route
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.components.*
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.proxy.compoents.AppModeBottomSheet
|
||||
import com.zaneschepke.wireguardautotunnel.ui.state.NavbarState
|
||||
import com.zaneschepke.wireguardautotunnel.ui.sideeffect.LocalSideEffect
|
||||
import com.zaneschepke.wireguardautotunnel.viewmodel.SettingsViewModel
|
||||
import org.orbitmvi.orbit.compose.collectSideEffect
|
||||
import xyz.teamgravity.pin_lock_compose.PinManager
|
||||
|
||||
@Composable
|
||||
@@ -47,11 +42,18 @@ fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel()) {
|
||||
|
||||
val settingsState by viewModel.container.stateFlow.collectAsStateWithLifecycle()
|
||||
|
||||
if (!settingsState.stateInitialized) return
|
||||
|
||||
var showBackupSheet by rememberSaveable { mutableStateOf(false) }
|
||||
var showAppModeSheet by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
if (!settingsState.stateInitialized) return
|
||||
|
||||
sharedViewModel.collectSideEffect { sideEffect ->
|
||||
when (sideEffect) {
|
||||
LocalSideEffect.Sheet.BackupApp -> showBackupSheet = true
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
val showProxySettings by
|
||||
remember(settingsState.settings.appMode) {
|
||||
derivedStateOf {
|
||||
@@ -62,20 +64,6 @@ fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel()) {
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
sharedViewModel.updateNavbarState(
|
||||
NavbarState(
|
||||
showBottomItems = true,
|
||||
topTitle = { Text(stringResource(R.string.settings)) },
|
||||
topTrailing = {
|
||||
ActionIconButton(Icons.Rounded.SettingsBackupRestore, R.string.quick_actions) {
|
||||
showBackupSheet = true
|
||||
}
|
||||
},
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (showBackupSheet) BackupBottomSheet() { showBackupSheet = false }
|
||||
if (showAppModeSheet)
|
||||
AppModeBottomSheet(sharedViewModel::setAppMode, settingsState.settings.appMode) {
|
||||
|
||||
-16
@@ -4,36 +4,20 @@ import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.ui.LocalNavController
|
||||
import com.zaneschepke.wireguardautotunnel.ui.LocalSharedVm
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.SectionDivider
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SurfaceSelectionGroupButton
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.appearance.components.DisplayThemeItem
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.appearance.components.LanguageItem
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.appearance.components.NotificationsItem
|
||||
import com.zaneschepke.wireguardautotunnel.ui.state.NavbarState
|
||||
|
||||
@Composable
|
||||
fun AppearanceScreen() {
|
||||
val sharedViewModel = LocalSharedVm.current
|
||||
val navController = LocalNavController.current
|
||||
LaunchedEffect(Unit) {
|
||||
sharedViewModel.updateNavbarState(
|
||||
NavbarState(
|
||||
showBottomItems = true,
|
||||
topTitle = { Text(stringResource(R.string.appearance)) },
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.Start,
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp, Alignment.Top),
|
||||
|
||||
-12
@@ -4,9 +4,7 @@ import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -16,7 +14,6 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.ui.LocalSharedVm
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.IconSurfaceButton
|
||||
import com.zaneschepke.wireguardautotunnel.ui.state.NavbarState
|
||||
import com.zaneschepke.wireguardautotunnel.ui.theme.Theme
|
||||
|
||||
@Composable
|
||||
@@ -26,15 +23,6 @@ fun DisplayScreen() {
|
||||
|
||||
val appState by sharedViewModel.container.stateFlow.collectAsStateWithLifecycle()
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
sharedViewModel.updateNavbarState(
|
||||
NavbarState(
|
||||
showBottomItems = true,
|
||||
topTitle = { Text(stringResource(R.string.display_theme)) },
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.Start,
|
||||
verticalArrangement = Arrangement.spacedBy(24.dp, Alignment.Top),
|
||||
|
||||
-11
@@ -5,7 +5,6 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -18,7 +17,6 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.ui.LocalSharedVm
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.IconSurfaceButton
|
||||
import com.zaneschepke.wireguardautotunnel.ui.state.NavbarState
|
||||
import com.zaneschepke.wireguardautotunnel.util.LocaleUtil
|
||||
import java.text.Collator
|
||||
import java.util.*
|
||||
@@ -29,15 +27,6 @@ fun LanguageScreen() {
|
||||
val sharedViewModel = LocalSharedVm.current
|
||||
val appState by sharedViewModel.container.stateFlow.collectAsStateWithLifecycle()
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
sharedViewModel.updateNavbarState(
|
||||
NavbarState(
|
||||
showBottomItems = true,
|
||||
topTitle = { Text(stringResource(R.string.language)) },
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
val collator = Collator.getInstance(Locale.getDefault())
|
||||
val locales =
|
||||
LocaleUtil.supportedLocales.map {
|
||||
|
||||
-13
@@ -14,7 +14,6 @@ import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -25,26 +24,14 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.data.model.DnsProtocol
|
||||
import com.zaneschepke.wireguardautotunnel.data.model.DnsProvider
|
||||
import com.zaneschepke.wireguardautotunnel.ui.LocalSharedVm
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.dropdown.LabelledDropdown
|
||||
import com.zaneschepke.wireguardautotunnel.ui.state.NavbarState
|
||||
import com.zaneschepke.wireguardautotunnel.viewmodel.SettingsViewModel
|
||||
|
||||
@Composable
|
||||
fun DnsSettingsScreen(viewModel: SettingsViewModel) {
|
||||
val context = LocalContext.current
|
||||
val sharedViewModel = LocalSharedVm.current
|
||||
val settingsState by viewModel.container.stateFlow.collectAsStateWithLifecycle()
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
sharedViewModel.updateNavbarState(
|
||||
NavbarState(
|
||||
showBottomItems = true,
|
||||
topTitle = { Text(stringResource(R.string.dns_settings)) },
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.Start,
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp, Alignment.Top),
|
||||
|
||||
+5
-18
@@ -4,8 +4,6 @@ import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Menu
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
@@ -19,15 +17,15 @@ import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.ui.LocalSharedVm
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.ActionIconButton
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.logs.components.LogList
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.logs.components.LogsBottomSheet
|
||||
import com.zaneschepke.wireguardautotunnel.ui.state.NavbarState
|
||||
import com.zaneschepke.wireguardautotunnel.ui.sideeffect.LocalSideEffect
|
||||
import com.zaneschepke.wireguardautotunnel.viewmodel.LoggerViewModel
|
||||
import org.orbitmvi.orbit.compose.collectSideEffect
|
||||
|
||||
@Composable
|
||||
fun LogsScreen(viewModel: LoggerViewModel = hiltViewModel()) {
|
||||
val sharedViewModel = LocalSharedVm.current
|
||||
val sharedAppViewModel = LocalSharedVm.current
|
||||
val loggerState by viewModel.container.stateFlow.collectAsStateWithLifecycle()
|
||||
|
||||
val lazyColumnListState = rememberLazyListState()
|
||||
@@ -35,19 +33,8 @@ fun LogsScreen(viewModel: LoggerViewModel = hiltViewModel()) {
|
||||
var lastScrollPosition by rememberSaveable() { mutableIntStateOf(0) }
|
||||
var showLogsSheet by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
sharedViewModel.updateNavbarState(
|
||||
NavbarState(
|
||||
showBottomItems = false,
|
||||
removeBottom = true,
|
||||
topTitle = { Text(stringResource(R.string.logs)) },
|
||||
topTrailing = {
|
||||
ActionIconButton(Icons.Rounded.Menu, R.string.quick_actions) {
|
||||
showLogsSheet = true
|
||||
}
|
||||
},
|
||||
)
|
||||
)
|
||||
sharedAppViewModel.collectSideEffect { sideEffect ->
|
||||
if (sideEffect is LocalSideEffect.Sheet.LoggerActions) showLogsSheet = true
|
||||
}
|
||||
|
||||
LaunchedEffect(isAutoScrolling) {
|
||||
|
||||
-13
@@ -14,7 +14,6 @@ import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -22,28 +21,16 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.ui.LocalSharedVm
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SurfaceSelectionGroupButton
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.dropdown.LabelledDropdown
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.monitoring.components.detailedPingStatsItem
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.monitoring.components.enablePingMonitoringItem
|
||||
import com.zaneschepke.wireguardautotunnel.ui.state.NavbarState
|
||||
import com.zaneschepke.wireguardautotunnel.viewmodel.SettingsViewModel
|
||||
|
||||
@Composable
|
||||
fun TunnelMonitoringScreen(viewModel: SettingsViewModel) {
|
||||
val sharedViewModel = LocalSharedVm.current
|
||||
val settingsState by viewModel.container.stateFlow.collectAsStateWithLifecycle()
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
sharedViewModel.updateNavbarState(
|
||||
NavbarState(
|
||||
topTitle = { Text(stringResource(R.string.tunnel_monitoring)) },
|
||||
showBottomItems = true,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.Start,
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp, Alignment.Top),
|
||||
|
||||
+18
-28
@@ -5,10 +5,8 @@ import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Forward5
|
||||
import androidx.compose.material.icons.outlined.Http
|
||||
import androidx.compose.material.icons.outlined.RemoveRedEye
|
||||
import androidx.compose.material.icons.rounded.Save
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -24,7 +22,6 @@ import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.AppProxySettings
|
||||
import com.zaneschepke.wireguardautotunnel.ui.LocalSharedVm
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.SecureScreenFromRecording
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.ActionIconButton
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.ScaledSwitch
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItem
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItemLabel
|
||||
@@ -32,14 +29,13 @@ import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionLab
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SurfaceSelectionGroupButton
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.label.GroupLabel
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.textbox.ConfigurationTextBox
|
||||
import com.zaneschepke.wireguardautotunnel.ui.state.NavbarState
|
||||
import com.zaneschepke.wireguardautotunnel.ui.sideeffect.LocalSideEffect
|
||||
import com.zaneschepke.wireguardautotunnel.viewmodel.ProxySettingsViewModel
|
||||
import org.orbitmvi.orbit.compose.collectSideEffect
|
||||
|
||||
@Composable
|
||||
fun ProxySettingsScreen(viewModel: ProxySettingsViewModel = hiltViewModel()) {
|
||||
|
||||
val sharedViewModel = LocalSharedVm.current
|
||||
|
||||
val proxySettingsState by viewModel.container.stateFlow.collectAsStateWithLifecycle()
|
||||
|
||||
val proxySettings by remember { derivedStateOf { proxySettingsState.proxySettings } }
|
||||
@@ -57,28 +53,22 @@ fun ProxySettingsScreen(viewModel: ProxySettingsViewModel = hiltViewModel()) {
|
||||
|
||||
if (!proxySettingsState.stateInitialized) return
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
sharedViewModel.updateNavbarState(
|
||||
NavbarState(
|
||||
showBottomItems = true,
|
||||
topTitle = { Text(stringResource(R.string.proxy_settings)) },
|
||||
topTrailing = {
|
||||
ActionIconButton(Icons.Rounded.Save, R.string.save) {
|
||||
keyboardController?.hide()
|
||||
viewModel.save(
|
||||
AppProxySettings(
|
||||
socks5ProxyEnabled = proxySettings.socks5ProxyEnabled,
|
||||
socks5ProxyBindAddress = socksBindAddress,
|
||||
httpProxyEnabled = proxySettings.httpProxyEnabled,
|
||||
httpProxyBindAddress = httpBindAddress,
|
||||
proxyUsername = proxyUsername,
|
||||
proxyPassword = proxyPassword,
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
)
|
||||
sharedViewModel.collectSideEffect { sideEffect ->
|
||||
when (sideEffect) {
|
||||
LocalSideEffect.SaveChanges -> {
|
||||
viewModel.save(
|
||||
AppProxySettings(
|
||||
socks5ProxyEnabled = proxySettings.socks5ProxyEnabled,
|
||||
socks5ProxyBindAddress = socksBindAddress,
|
||||
httpProxyEnabled = proxySettings.httpProxyEnabled,
|
||||
httpProxyBindAddress = httpBindAddress,
|
||||
proxyUsername = proxyUsername,
|
||||
proxyPassword = proxyPassword,
|
||||
)
|
||||
)
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
SecureScreenFromRecording()
|
||||
|
||||
-16
@@ -6,41 +6,25 @@ import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.ui.LocalIsAndroidTV
|
||||
import com.zaneschepke.wireguardautotunnel.ui.LocalSharedVm
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.SectionDivider
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.SecureScreenFromRecording
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SurfaceSelectionGroupButton
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.system.components.*
|
||||
import com.zaneschepke.wireguardautotunnel.ui.state.NavbarState
|
||||
import com.zaneschepke.wireguardautotunnel.viewmodel.SettingsViewModel
|
||||
|
||||
@Composable
|
||||
fun SystemFeaturesScreen(viewModel: SettingsViewModel) {
|
||||
val sharedViewModel = LocalSharedVm.current
|
||||
val settingsState by viewModel.container.stateFlow.collectAsStateWithLifecycle()
|
||||
|
||||
val isTv = LocalIsAndroidTV.current
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
sharedViewModel.updateNavbarState(
|
||||
NavbarState(
|
||||
topTitle = { Text(stringResource(R.string.android_integrations)) },
|
||||
showBottomItems = true,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
SecureScreenFromRecording()
|
||||
|
||||
Column(
|
||||
|
||||
+4
-13
@@ -6,8 +6,11 @@ import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.LinearProgressIndicator
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.StrokeCap
|
||||
@@ -20,14 +23,12 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.zaneschepke.wireguardautotunnel.BuildConfig
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.ui.LocalNavController
|
||||
import com.zaneschepke.wireguardautotunnel.ui.LocalSharedVm
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.SectionDivider
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.dialog.InfoDialog
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.label.GroupLabel
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.support.components.ContactSupportOptions
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.support.components.GeneralSupportOptions
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.support.components.UpdateSection
|
||||
import com.zaneschepke.wireguardautotunnel.ui.state.NavbarState
|
||||
import com.zaneschepke.wireguardautotunnel.util.Constants
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.canInstallPackages
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.openWebUrl
|
||||
@@ -37,21 +38,11 @@ import com.zaneschepke.wireguardautotunnel.viewmodel.SupportViewModel
|
||||
@Composable
|
||||
fun SupportScreen(viewModel: SupportViewModel) {
|
||||
val context = LocalContext.current
|
||||
val sharedViewModel = LocalSharedVm.current
|
||||
val navController = LocalNavController.current
|
||||
val supportState by viewModel.container.stateFlow.collectAsStateWithLifecycle()
|
||||
|
||||
var showPermissionDialog by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
sharedViewModel.updateNavbarState(
|
||||
NavbarState(
|
||||
topTitle = { Text(stringResource(R.string.support)) },
|
||||
showBottomItems = true,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (supportState.appUpdate != null) {
|
||||
InfoDialog(
|
||||
onDismiss = { viewModel.dismissUpdate() },
|
||||
|
||||
-15
@@ -5,16 +5,11 @@ import android.content.Context
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.ui.LocalSharedVm
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.support.license.components.LicenseList
|
||||
import com.zaneschepke.wireguardautotunnel.ui.state.NavbarState
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.json.Json
|
||||
@@ -22,18 +17,8 @@ import kotlinx.serialization.json.Json
|
||||
@Composable
|
||||
fun LicenseScreen() {
|
||||
val context = LocalContext.current
|
||||
val sharedViewModel = LocalSharedVm.current
|
||||
var licenses by remember { mutableStateOf<List<LicenseFileEntry>>(emptyList()) }
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
sharedViewModel.updateNavbarState(
|
||||
NavbarState(
|
||||
showBottomItems = true,
|
||||
topTitle = { Text(stringResource(R.string.licenses)) },
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) { licenses = loadLicenseeJson(context) }
|
||||
|
||||
if (licenses.isEmpty()) {
|
||||
|
||||
+54
-58
@@ -7,8 +7,10 @@ import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.rounded.Sort
|
||||
import androidx.compose.material.icons.rounded.*
|
||||
import androidx.compose.material.icons.rounded.CopyAll
|
||||
import androidx.compose.material.icons.rounded.Delete
|
||||
import androidx.compose.material.icons.rounded.Download
|
||||
import androidx.compose.material.icons.rounded.SelectAll
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
@@ -30,10 +32,11 @@ import com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.components.ExportT
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.components.TunnelImportSheet
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.components.TunnelList
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.components.UrlImportDialog
|
||||
import com.zaneschepke.wireguardautotunnel.ui.state.NavbarState
|
||||
import com.zaneschepke.wireguardautotunnel.ui.sideeffect.LocalSideEffect
|
||||
import com.zaneschepke.wireguardautotunnel.util.FileUtils
|
||||
import com.zaneschepke.wireguardautotunnel.util.StringValue
|
||||
import com.zaneschepke.wireguardautotunnel.viewmodel.TunnelsViewModel
|
||||
import org.orbitmvi.orbit.compose.collectSideEffect
|
||||
|
||||
@Composable
|
||||
fun TunnelsScreen(viewModel: TunnelsViewModel) {
|
||||
@@ -48,70 +51,57 @@ fun TunnelsScreen(viewModel: TunnelsViewModel) {
|
||||
var showDeleteModal by rememberSaveable { mutableStateOf(false) }
|
||||
var showUrlDialog by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
if (!tunnelsState.stateInitialized) return
|
||||
|
||||
@Composable
|
||||
fun TunnelActionBar() {
|
||||
val selectedCount by
|
||||
remember(tunnelsState.selectedTunnels) {
|
||||
derivedStateOf { tunnelsState.selectedTunnels.size }
|
||||
}
|
||||
val disableDelete by
|
||||
remember(tunnelsState.activeTunnels, tunnelsState.selectedTunnels) {
|
||||
derivedStateOf {
|
||||
tunnelsState.activeTunnels.any { active ->
|
||||
tunnelsState.selectedTunnels.any { it.id == active.key.id }
|
||||
}
|
||||
val disableDelete by
|
||||
remember(tunnelsState.activeTunnels, tunnelsState.selectedTunnels) {
|
||||
derivedStateOf {
|
||||
tunnelsState.activeTunnels.any { active ->
|
||||
tunnelsState.selectedTunnels.any { it.id == active.key.id }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
if (selectedCount == 0) {
|
||||
val showSort by
|
||||
remember(tunnelsState.tunnels) {
|
||||
derivedStateOf { tunnelsState.tunnels.size > 1 }
|
||||
sharedViewModel.collectSideEffect { sideEffect ->
|
||||
when (sideEffect) {
|
||||
LocalSideEffect.Sheet.ImportTunnels -> showImportSheet = true
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(tunnelsState.selectedTunnels) {
|
||||
when (val count = tunnelsState.selectedTunnels.count()) {
|
||||
0 -> sharedViewModel.updateTopNavActions(null)
|
||||
else -> {
|
||||
sharedViewModel.updateTopNavActions {
|
||||
Row {
|
||||
ActionIconButton(Icons.Rounded.SelectAll, R.string.select_all) {
|
||||
viewModel.toggleSelectAllTunnels()
|
||||
}
|
||||
// due to permissions, and SAF issues on TV, not support less than Android
|
||||
// 10
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
ActionIconButton(Icons.Rounded.Download, R.string.download) {
|
||||
showExportSheet = true
|
||||
}
|
||||
}
|
||||
|
||||
if (count == 1) {
|
||||
ActionIconButton(Icons.Rounded.CopyAll, R.string.copy) {
|
||||
viewModel.copySelectedTunnel()
|
||||
}
|
||||
}
|
||||
|
||||
if (!disableDelete) {
|
||||
ActionIconButton(Icons.Rounded.Delete, R.string.delete_tunnel) {
|
||||
showDeleteModal = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if (showSort)
|
||||
ActionIconButton(Icons.AutoMirrored.Rounded.Sort, R.string.sort) {
|
||||
navController.navigate(Route.Sort)
|
||||
}
|
||||
ActionIconButton(Icons.Rounded.Add, R.string.add_tunnel) { showImportSheet = true }
|
||||
return@Row
|
||||
}
|
||||
ActionIconButton(Icons.Rounded.SelectAll, R.string.select_all) {
|
||||
viewModel.toggleSelectAllTunnels()
|
||||
}
|
||||
// due to permissions, and SAF issues on TV, not support less than Android 10 on
|
||||
// Android TV for file exports
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
ActionIconButton(Icons.Rounded.Download, R.string.download) {
|
||||
showExportSheet = true
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedCount == 1) {
|
||||
ActionIconButton(Icons.Rounded.CopyAll, R.string.copy) {
|
||||
viewModel.copySelectedTunnel()
|
||||
}
|
||||
}
|
||||
|
||||
if (!disableDelete) {
|
||||
ActionIconButton(Icons.Rounded.Delete, R.string.delete_tunnel) {
|
||||
showDeleteModal = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
sharedViewModel.updateNavbarState(
|
||||
NavbarState(
|
||||
topTitle = { Text(stringResource(R.string.tunnels)) },
|
||||
topTrailing = { TunnelActionBar() },
|
||||
showBottomItems = true,
|
||||
)
|
||||
)
|
||||
}
|
||||
if (!tunnelsState.stateInitialized) return
|
||||
|
||||
val tunnelFileImportResultLauncher =
|
||||
rememberFileImportLauncherForResult(
|
||||
@@ -203,3 +193,9 @@ fun TunnelsScreen(viewModel: TunnelsViewModel) {
|
||||
navController,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TunnelActionBar() {
|
||||
|
||||
Row {}
|
||||
}
|
||||
|
||||
+4
-11
@@ -6,24 +6,23 @@ import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.zaneschepke.wireguardautotunnel.ui.LocalSharedVm
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SurfaceSelectionGroupButton
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.autotunnel.components.MobileDataTunnelItem
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.autotunnel.components.PingRestartItem
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.autotunnel.components.WifiTunnelItem
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.autotunnel.components.ethernetTunnelItem
|
||||
import com.zaneschepke.wireguardautotunnel.ui.state.NavbarState
|
||||
import com.zaneschepke.wireguardautotunnel.viewmodel.TunnelsViewModel
|
||||
|
||||
@Composable
|
||||
fun TunnelAutoTunnelScreen(tunnelId: Int, viewModel: TunnelsViewModel) {
|
||||
val sharedViewModel = LocalSharedVm.current
|
||||
val tunnelsState by viewModel.container.stateFlow.collectAsStateWithLifecycle()
|
||||
|
||||
val tunnelConf by
|
||||
@@ -31,12 +30,6 @@ fun TunnelAutoTunnelScreen(tunnelId: Int, viewModel: TunnelsViewModel) {
|
||||
derivedStateOf { tunnelsState.tunnels.find { it.id == tunnelId }!! }
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
sharedViewModel.updateNavbarState(
|
||||
NavbarState(showBottomItems = true, topTitle = { Text(tunnelConf.name) })
|
||||
)
|
||||
}
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.Start,
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp, Alignment.Top),
|
||||
|
||||
+8
-25
@@ -6,37 +6,30 @@ import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Save
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.ui.LocalIsAndroidTV
|
||||
import com.zaneschepke.wireguardautotunnel.ui.LocalSharedVm
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.SecureScreenFromRecording
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.ActionIconButton
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.components.AuthorizationPromptWrapper
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.config.components.AddPeerButton
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.config.components.InterfaceSection
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.config.components.PeersSection
|
||||
import com.zaneschepke.wireguardautotunnel.ui.sideeffect.LocalSideEffect
|
||||
import com.zaneschepke.wireguardautotunnel.ui.state.ConfigProxy
|
||||
import com.zaneschepke.wireguardautotunnel.ui.state.NavbarState
|
||||
import com.zaneschepke.wireguardautotunnel.ui.state.PeerProxy
|
||||
import com.zaneschepke.wireguardautotunnel.viewmodel.TunnelsViewModel
|
||||
import org.orbitmvi.orbit.compose.collectSideEffect
|
||||
|
||||
@Composable
|
||||
fun ConfigScreen(tunnelId: Int? = null, viewModel: TunnelsViewModel = hiltViewModel()) {
|
||||
val sharedViewModel = LocalSharedVm.current
|
||||
val isTv = LocalIsAndroidTV.current
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
val sharedViewModel = LocalSharedVm.current
|
||||
|
||||
val tunnelsState by viewModel.container.stateFlow.collectAsStateWithLifecycle()
|
||||
|
||||
@@ -51,21 +44,6 @@ fun ConfigScreen(tunnelId: Int? = null, viewModel: TunnelsViewModel = hiltViewMo
|
||||
|
||||
var tunnelName by remember { mutableStateOf(tunnelConf?.name ?: "") }
|
||||
|
||||
LaunchedEffect(key1 = Unit) {
|
||||
sharedViewModel.updateNavbarState(
|
||||
NavbarState(
|
||||
showBottomItems = true,
|
||||
topTitle = { Text(tunnelConf?.name ?: stringResource(R.string.new_tunnel)) },
|
||||
topTrailing = {
|
||||
ActionIconButton(Icons.Rounded.Save, R.string.save) {
|
||||
keyboardController?.hide()
|
||||
viewModel.saveConfigProxy(tunnelId, configProxy, tunnelName)
|
||||
}
|
||||
},
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
val isTunnelNameTaken by
|
||||
remember(tunnelName, tunnelsState.tunnels) {
|
||||
derivedStateOf {
|
||||
@@ -76,6 +54,11 @@ fun ConfigScreen(tunnelId: Int? = null, viewModel: TunnelsViewModel = hiltViewMo
|
||||
var showAuthPrompt by rememberSaveable { mutableStateOf(false) }
|
||||
var isAuthorized by rememberSaveable { mutableStateOf(isTv) }
|
||||
|
||||
sharedViewModel.collectSideEffect { sideEffect ->
|
||||
if (sideEffect is LocalSideEffect.SaveChanges)
|
||||
viewModel.saveConfigProxy(tunnelId, configProxy, tunnelName)
|
||||
}
|
||||
|
||||
SecureScreenFromRecording()
|
||||
|
||||
if (showAuthPrompt) {
|
||||
|
||||
+26
-33
@@ -11,13 +11,13 @@ import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowDownward
|
||||
import androidx.compose.material.icons.filled.ArrowUpward
|
||||
import androidx.compose.material.icons.filled.DragHandle
|
||||
import androidx.compose.material.icons.rounded.Save
|
||||
import androidx.compose.material.icons.rounded.SortByAlpha
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||
@@ -30,10 +30,10 @@ import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.ui.LocalIsAndroidTV
|
||||
import com.zaneschepke.wireguardautotunnel.ui.LocalSharedVm
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.ExpandingRowListItem
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.ActionIconButton
|
||||
import com.zaneschepke.wireguardautotunnel.ui.state.NavbarState
|
||||
import com.zaneschepke.wireguardautotunnel.ui.sideeffect.LocalSideEffect
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.isSortedBy
|
||||
import com.zaneschepke.wireguardautotunnel.viewmodel.TunnelsViewModel
|
||||
import org.orbitmvi.orbit.compose.collectSideEffect
|
||||
import sh.calvin.reorderable.DragGestureDetector
|
||||
import sh.calvin.reorderable.ReorderableItem
|
||||
import sh.calvin.reorderable.rememberReorderableLazyListState
|
||||
@@ -48,34 +48,27 @@ fun SortScreen(viewModel: TunnelsViewModel) {
|
||||
var sortAscending by rememberSaveable { mutableStateOf<Boolean?>(null) }
|
||||
var editableTunnels by rememberSaveable { mutableStateOf(tunnelsState.tunnels) }
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
sharedViewModel.updateNavbarState(
|
||||
NavbarState(
|
||||
showBottomItems = true,
|
||||
topTitle = { Text(stringResource(R.string.sort)) },
|
||||
topTrailing = {
|
||||
Row {
|
||||
ActionIconButton(Icons.Rounded.SortByAlpha, R.string.sort) {
|
||||
sortAscending =
|
||||
when (sortAscending) {
|
||||
null -> !editableTunnels.isSortedBy { it.name }
|
||||
true -> false
|
||||
false -> null
|
||||
}
|
||||
editableTunnels =
|
||||
when (sortAscending) {
|
||||
true -> editableTunnels.sortedBy { it.name }
|
||||
false -> editableTunnels.sortedByDescending { it.name }
|
||||
null -> tunnelsState.tunnels
|
||||
}
|
||||
}
|
||||
ActionIconButton(Icons.Rounded.Save, R.string.save) {
|
||||
viewModel.saveSortChanges(editableTunnels)
|
||||
}
|
||||
sharedViewModel.collectSideEffect { sideEffect ->
|
||||
when (sideEffect) {
|
||||
LocalSideEffect.SaveChanges -> {
|
||||
viewModel.saveSortChanges(editableTunnels)
|
||||
}
|
||||
LocalSideEffect.Sort -> {
|
||||
sortAscending =
|
||||
when (sortAscending) {
|
||||
null -> !editableTunnels.isSortedBy { it.name }
|
||||
true -> false
|
||||
false -> null
|
||||
}
|
||||
},
|
||||
)
|
||||
)
|
||||
editableTunnels =
|
||||
when (sortAscending) {
|
||||
true -> editableTunnels.sortedBy { it.name }
|
||||
false -> editableTunnels.sortedByDescending { it.name }
|
||||
null -> tunnelsState.tunnels
|
||||
}
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
val lazyListState = rememberLazyListState()
|
||||
|
||||
+6
-19
@@ -3,28 +3,24 @@ package com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.splittunnel
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Save
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.ui.LocalSharedVm
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.ActionIconButton
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.splittunnel.components.SplitTunnelContent
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.splittunnel.state.SplitOption
|
||||
import com.zaneschepke.wireguardautotunnel.ui.state.NavbarState
|
||||
import com.zaneschepke.wireguardautotunnel.ui.sideeffect.LocalSideEffect
|
||||
import com.zaneschepke.wireguardautotunnel.viewmodel.SplitTunnelViewModel
|
||||
import org.orbitmvi.orbit.compose.collectSideEffect
|
||||
|
||||
@Composable
|
||||
fun SplitTunnelScreen(tunnelId: Int, viewModel: SplitTunnelViewModel = hiltViewModel()) {
|
||||
val splitTunnelState by viewModel.container.stateFlow.collectAsStateWithLifecycle()
|
||||
val sharedViewModel = LocalSharedVm.current
|
||||
val splitTunnelState by viewModel.container.stateFlow.collectAsStateWithLifecycle()
|
||||
|
||||
if (!splitTunnelState.stateInitialized) {
|
||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
@@ -51,18 +47,9 @@ fun SplitTunnelScreen(tunnelId: Int, viewModel: SplitTunnelViewModel = hiltViewM
|
||||
)
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
sharedViewModel.updateNavbarState(
|
||||
NavbarState(
|
||||
topTitle = { Text(tunnelConf.name) },
|
||||
topTrailing = {
|
||||
ActionIconButton(Icons.Rounded.Save, R.string.save) {
|
||||
viewModel.saveSplitTunnelSelection(tunnelId, splitConfig)
|
||||
}
|
||||
},
|
||||
showBottomItems = true,
|
||||
)
|
||||
)
|
||||
sharedViewModel.collectSideEffect { sideEffect ->
|
||||
if (sideEffect is LocalSideEffect.SaveChanges)
|
||||
viewModel.saveSplitTunnelSelection(tunnelId, splitConfig)
|
||||
}
|
||||
|
||||
SplitTunnelContent(
|
||||
|
||||
+8
-29
@@ -1,30 +1,27 @@
|
||||
package com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.tunneloptions
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Edit
|
||||
import androidx.compose.material.icons.rounded.QrCode2
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.ui.LocalIsAndroidTV
|
||||
import com.zaneschepke.wireguardautotunnel.ui.LocalNavController
|
||||
import com.zaneschepke.wireguardautotunnel.ui.LocalSharedVm
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.SectionDivider
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.ActionIconButton
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SurfaceSelectionGroupButton
|
||||
import com.zaneschepke.wireguardautotunnel.ui.navigation.Route.Config
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.components.AuthorizationPromptWrapper
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.tunneloptions.components.*
|
||||
import com.zaneschepke.wireguardautotunnel.ui.state.NavbarState
|
||||
import com.zaneschepke.wireguardautotunnel.ui.sideeffect.LocalSideEffect
|
||||
import com.zaneschepke.wireguardautotunnel.viewmodel.TunnelsViewModel
|
||||
import org.orbitmvi.orbit.compose.collectSideEffect
|
||||
|
||||
@Composable
|
||||
fun TunnelOptionsScreen(tunnelId: Int, viewModel: TunnelsViewModel) {
|
||||
@@ -43,26 +40,8 @@ fun TunnelOptionsScreen(tunnelId: Int, viewModel: TunnelsViewModel) {
|
||||
var isAuthorized by rememberSaveable { mutableStateOf(isTv) }
|
||||
var showQrModal by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
sharedViewModel.updateNavbarState(
|
||||
NavbarState(
|
||||
showBottomItems = true,
|
||||
topTitle = { Text(tunnelConf.name) },
|
||||
topTrailing = {
|
||||
Row {
|
||||
ActionIconButton(
|
||||
Icons.Rounded.QrCode2,
|
||||
com.zaneschepke.wireguardautotunnel.R.string.show_qr,
|
||||
) {
|
||||
showQrModal = true
|
||||
}
|
||||
ActionIconButton(Icons.Rounded.Edit, R.string.edit_tunnel) {
|
||||
navController.navigate(Config(tunnelId))
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
)
|
||||
sharedViewModel.collectSideEffect { sideEffect ->
|
||||
if (sideEffect is LocalSideEffect.Modal.QR) showQrModal = true
|
||||
}
|
||||
|
||||
if (showQrModal) {
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package com.zaneschepke.wireguardautotunnel.ui.sideeffect
|
||||
|
||||
sealed class LocalSideEffect {
|
||||
data object Sort : LocalSideEffect()
|
||||
|
||||
data object SaveChanges : LocalSideEffect()
|
||||
|
||||
sealed class Sheet : LocalSideEffect() {
|
||||
|
||||
data object ImportTunnels : Sheet()
|
||||
|
||||
data object BackupApp : Sheet()
|
||||
|
||||
data object LoggerActions : Sheet()
|
||||
}
|
||||
|
||||
sealed class Modal : LocalSideEffect() {
|
||||
data object QR : Modal()
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.zaneschepke.wireguardautotunnel.ui.state
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.GeneralSettings
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.ui.theme.Theme
|
||||
import com.zaneschepke.wireguardautotunnel.util.LocaleUtil
|
||||
|
||||
@@ -12,6 +14,7 @@ data class SharedAppUiState(
|
||||
val isAuthorized: Boolean = false,
|
||||
val isAutoTunnelActive: Boolean = false,
|
||||
val isLocationDisclosureShown: Boolean = false,
|
||||
val tunnels: List<TunnelConf> = emptyList(),
|
||||
val settings: GeneralSettings = GeneralSettings(),
|
||||
val navBarState: NavbarState = NavbarState(),
|
||||
val topNavActions: (@Composable () -> Unit)? = null,
|
||||
)
|
||||
|
||||
@@ -11,13 +11,13 @@ import androidx.annotation.RequiresApi
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.QuickConfig
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.TunnelName
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.getInputStreamFromUri
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
import java.io.*
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipInputStream
|
||||
import java.util.zip.ZipOutputStream
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
|
||||
class FileUtils(private val context: Context, private val ioDispatcher: CoroutineDispatcher) {
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package com.zaneschepke.wireguardautotunnel.util
|
||||
|
||||
import com.vdurmont.semver4j.Semver
|
||||
import timber.log.Timber
|
||||
import java.math.BigDecimal
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
import kotlin.math.pow
|
||||
import timber.log.Timber
|
||||
|
||||
object NumberUtils {
|
||||
private const val BYTES_IN_KB = 1024.0
|
||||
|
||||
+1
-1
@@ -24,11 +24,11 @@ import com.zaneschepke.wireguardautotunnel.core.service.tile.TunnelControlTile
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.splittunnel.state.TunnelApp
|
||||
import com.zaneschepke.wireguardautotunnel.util.Constants
|
||||
import com.zaneschepke.wireguardautotunnel.util.FileUtils
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
import java.util.*
|
||||
import kotlin.system.exitProcess
|
||||
import timber.log.Timber
|
||||
|
||||
fun Context.openWebUrl(url: String): Result<Unit> {
|
||||
return kotlin
|
||||
|
||||
-20
@@ -1,28 +1,8 @@
|
||||
package com.zaneschepke.wireguardautotunnel.util.extensions
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
fun <K, V> Flow<Map<K, V>>.distinctByKeys(): Flow<Map<K, V>> {
|
||||
return distinctUntilChanged { old, new -> old.keys == new.keys }
|
||||
}
|
||||
|
||||
fun <T> debounce(
|
||||
scope: CoroutineScope,
|
||||
delayMillis: Long = 300L,
|
||||
onDebounced: (T) -> Unit,
|
||||
): (T) -> Unit {
|
||||
var job: Job? = null
|
||||
return { param: T ->
|
||||
job?.cancel()
|
||||
job =
|
||||
scope.launch {
|
||||
delay(delayMillis)
|
||||
onDebounced(param)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,14 +3,14 @@ package com.zaneschepke.wireguardautotunnel.util.network
|
||||
import com.marsounjan.icmp4a.Icmp
|
||||
import com.marsounjan.icmp4a.Icmp4a
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.round
|
||||
import java.io.IOException
|
||||
import java.time.Instant
|
||||
import kotlin.math.sqrt
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import timber.log.Timber
|
||||
import java.io.IOException
|
||||
import java.time.Instant
|
||||
import kotlin.math.sqrt
|
||||
|
||||
class NetworkUtils(private val ioDispatcher: CoroutineDispatcher) {
|
||||
|
||||
|
||||
+1
-1
@@ -12,11 +12,11 @@ import com.zaneschepke.wireguardautotunnel.domain.sideeffect.GlobalSideEffect
|
||||
import com.zaneschepke.wireguardautotunnel.ui.state.AutoTunnelUiState
|
||||
import com.zaneschepke.wireguardautotunnel.util.StringValue
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.map
|
||||
import org.orbitmvi.orbit.ContainerHost
|
||||
import org.orbitmvi.orbit.viewmodel.container
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class AutoTunnelViewModel
|
||||
|
||||
@@ -11,14 +11,14 @@ import com.zaneschepke.wireguardautotunnel.util.Constants
|
||||
import com.zaneschepke.wireguardautotunnel.util.FileUtils
|
||||
import com.zaneschepke.wireguardautotunnel.util.StringValue
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import java.time.Instant
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.*
|
||||
import org.orbitmvi.orbit.ContainerHost
|
||||
import org.orbitmvi.orbit.viewmodel.container
|
||||
import timber.log.Timber
|
||||
import java.time.Instant
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class LoggerViewModel
|
||||
|
||||
+1
-1
@@ -10,9 +10,9 @@ import com.zaneschepke.wireguardautotunnel.ui.state.ProxySettingsUiState
|
||||
import com.zaneschepke.wireguardautotunnel.util.StringValue
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.isValidAndroidProxyBindAddress
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
import org.orbitmvi.orbit.ContainerHost
|
||||
import org.orbitmvi.orbit.viewmodel.container
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class ProxySettingsViewModel
|
||||
|
||||
+2
-2
@@ -10,11 +10,11 @@ import com.zaneschepke.wireguardautotunnel.domain.repository.GlobalEffectReposit
|
||||
import com.zaneschepke.wireguardautotunnel.domain.sideeffect.GlobalSideEffect
|
||||
import com.zaneschepke.wireguardautotunnel.ui.state.SettingUiState
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import org.orbitmvi.orbit.ContainerHost
|
||||
import org.orbitmvi.orbit.viewmodel.container
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class SettingsViewModel
|
||||
|
||||
+17
-8
@@ -1,5 +1,6 @@
|
||||
package com.zaneschepke.wireguardautotunnel.viewmodel
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.core.content.PermissionChecker.PERMISSION_GRANTED
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.wireguard.android.util.RootShell
|
||||
@@ -14,15 +15,14 @@ import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.AppStateRepository
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.GeneralSettingRepository
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.GlobalEffectRepository
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.TunnelRepository
|
||||
import com.zaneschepke.wireguardautotunnel.domain.sideeffect.GlobalSideEffect
|
||||
import com.zaneschepke.wireguardautotunnel.ui.state.NavbarState
|
||||
import com.zaneschepke.wireguardautotunnel.ui.sideeffect.LocalSideEffect
|
||||
import com.zaneschepke.wireguardautotunnel.ui.state.SharedAppUiState
|
||||
import com.zaneschepke.wireguardautotunnel.ui.theme.Theme
|
||||
import com.zaneschepke.wireguardautotunnel.util.LocaleUtil
|
||||
import com.zaneschepke.wireguardautotunnel.util.StringValue
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Provider
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.map
|
||||
@@ -31,6 +31,8 @@ import org.orbitmvi.orbit.ContainerHost
|
||||
import org.orbitmvi.orbit.viewmodel.container
|
||||
import rikka.shizuku.Shizuku
|
||||
import xyz.teamgravity.pin_lock_compose.PinManager
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Provider
|
||||
|
||||
@HiltViewModel
|
||||
class SharedAppViewModel
|
||||
@@ -40,15 +42,16 @@ constructor(
|
||||
private val serviceManager: ServiceManager,
|
||||
private val tunnelManager: TunnelManager,
|
||||
private val globalEffectRepository: GlobalEffectRepository,
|
||||
private val tunnelRepository: TunnelRepository,
|
||||
private val settingsRepository: GeneralSettingRepository,
|
||||
@AppShell private val rootShell: Provider<RootShell>,
|
||||
@IoDispatcher private val ioDispatcher: CoroutineDispatcher,
|
||||
) : ContainerHost<SharedAppUiState, Nothing>, ViewModel() {
|
||||
) : ContainerHost<SharedAppUiState, LocalSideEffect>, ViewModel() {
|
||||
|
||||
val globalSideEffect = globalEffectRepository.flow
|
||||
|
||||
override val container =
|
||||
container<SharedAppUiState, Nothing>(
|
||||
container<SharedAppUiState, LocalSideEffect>(
|
||||
SharedAppUiState(),
|
||||
buildSettings = { repeatOnSubscribedStopTimeout = 5000L },
|
||||
) {
|
||||
@@ -57,12 +60,14 @@ constructor(
|
||||
appStateRepository.flow,
|
||||
serviceManager.autoTunnelService.map { it != null },
|
||||
settingsRepository.flow,
|
||||
) { appState, autoTunnelActive, settings ->
|
||||
tunnelRepository.flow,
|
||||
) { appState, autoTunnelActive, settings, tunnels ->
|
||||
state.copy(
|
||||
theme = appState.theme,
|
||||
locale = appState.locale ?: LocaleUtil.OPTION_PHONE_LANGUAGE,
|
||||
pinLockEnabled = appState.isPinLockEnabled,
|
||||
isAutoTunnelActive = autoTunnelActive,
|
||||
tunnels = tunnels,
|
||||
settings = settings,
|
||||
isLocationDisclosureShown = appState.isLocationDisclosureShown,
|
||||
isAppLoaded = true,
|
||||
@@ -95,6 +100,10 @@ constructor(
|
||||
tunnelManager.startTunnel(tunnelConf)
|
||||
}
|
||||
|
||||
fun postSideEffect(localSideEffect: LocalSideEffect) = intent {
|
||||
postSideEffect(localSideEffect)
|
||||
}
|
||||
|
||||
fun setTheme(theme: Theme) = intent { appStateRepository.setTheme(theme) }
|
||||
|
||||
fun setLocale(locale: String) = intent {
|
||||
@@ -125,8 +134,8 @@ constructor(
|
||||
settingsRepository.save(state.settings.copy(appMode = appMode))
|
||||
}
|
||||
|
||||
fun updateNavbarState(newState: NavbarState) = intent {
|
||||
reduce { state.copy(navBarState = newState) }
|
||||
fun updateTopNavActions(actions: (@Composable () -> Unit)?) = intent {
|
||||
reduce { state.copy(topNavActions = actions) }
|
||||
}
|
||||
|
||||
suspend fun postSideEffect(globalSideEffect: GlobalSideEffect) {
|
||||
|
||||
+1
-1
@@ -12,11 +12,11 @@ import com.zaneschepke.wireguardautotunnel.ui.state.InterfaceProxy
|
||||
import com.zaneschepke.wireguardautotunnel.ui.state.SplitTunnelUiState
|
||||
import com.zaneschepke.wireguardautotunnel.util.StringValue
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import org.orbitmvi.orbit.ContainerHost
|
||||
import org.orbitmvi.orbit.viewmodel.container
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class SplitTunnelViewModel
|
||||
|
||||
@@ -13,11 +13,11 @@ import com.zaneschepke.wireguardautotunnel.util.Constants
|
||||
import com.zaneschepke.wireguardautotunnel.util.StringValue
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import jakarta.inject.Inject
|
||||
import javax.inject.Provider
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.orbitmvi.orbit.ContainerHost
|
||||
import org.orbitmvi.orbit.viewmodel.container
|
||||
import javax.inject.Provider
|
||||
|
||||
@HiltViewModel
|
||||
class SupportViewModel
|
||||
|
||||
@@ -23,11 +23,6 @@ import com.zaneschepke.wireguardautotunnel.util.extensions.TunnelName
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.asStringValue
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.saveTunnelsUniquely
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.net.URL
|
||||
import java.time.Instant
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.withContext
|
||||
@@ -35,6 +30,11 @@ import org.amnezia.awg.config.BadConfigException
|
||||
import org.orbitmvi.orbit.ContainerHost
|
||||
import org.orbitmvi.orbit.viewmodel.container
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.net.URL
|
||||
import java.time.Instant
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class TunnelsViewModel
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package com.zaneschepke.logcatter
|
||||
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.BufferedOutputStream
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipOutputStream
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class LogFileManager(
|
||||
private val logDir: String,
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package com.zaneschepke.logcatter
|
||||
|
||||
import com.zaneschepke.logcatter.model.LogMessage
|
||||
import java.io.BufferedReader
|
||||
import java.io.IOException
|
||||
import java.io.InputStreamReader
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import java.io.BufferedReader
|
||||
import java.io.IOException
|
||||
import java.io.InputStreamReader
|
||||
|
||||
class LogcatStreamReader(private val pid: Int, private val fileManager: LogFileManager) {
|
||||
private val bufferSize = 1024
|
||||
|
||||
+1
-1
@@ -17,13 +17,13 @@ import androidx.core.content.ContextCompat
|
||||
import com.wireguard.android.util.RootShell
|
||||
import com.zaneschepke.networkmonitor.shizuku.ShizukuShell
|
||||
import com.zaneschepke.networkmonitor.util.*
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
class AndroidNetworkMonitor(
|
||||
private val appContext: Context,
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
package com.zaneschepke.networkmonitor.shizuku
|
||||
|
||||
import android.os.ParcelFileDescriptor
|
||||
import java.io.BufferedReader
|
||||
import java.io.FileInputStream
|
||||
import java.io.InputStreamReader
|
||||
import kotlin.coroutines.resumeWithException
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
@@ -12,6 +8,10 @@ import moe.shizuku.server.IRemoteProcess
|
||||
import moe.shizuku.server.IShizukuService
|
||||
import rikka.shizuku.Shizuku
|
||||
import timber.log.Timber
|
||||
import java.io.BufferedReader
|
||||
import java.io.FileInputStream
|
||||
import java.io.InputStreamReader
|
||||
import kotlin.coroutines.resumeWithException
|
||||
|
||||
class ShizukuShell(private val applicationScope: CoroutineScope) {
|
||||
interface CommandResultListener {
|
||||
|
||||
Reference in New Issue
Block a user