fix: auto tunneling and backup
Fixes a bug where android backups can cause app crashes due to pin lock feature keystore. Fixes a bug where auto tunneling to SSID tunnel was not working correctly. Fixes a mobile data tunneling bug which was causing mobile data tunneling to not perform correctly. Additional strictmode improvements.
This commit is contained in:
parent
6448386f76
commit
1d74d0984e
|
@ -51,7 +51,7 @@
|
||||||
</queries>
|
</queries>
|
||||||
<application
|
<application
|
||||||
android:name=".WireGuardAutoTunnel"
|
android:name=".WireGuardAutoTunnel"
|
||||||
android:allowBackup="true"
|
android:allowBackup="false"
|
||||||
android:banner="@drawable/ic_banner"
|
android:banner="@drawable/ic_banner"
|
||||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
android:enableOnBackInvokedCallback="true"
|
android:enableOnBackInvokedCallback="true"
|
||||||
|
|
|
@ -17,6 +17,7 @@ import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import dagger.hilt.components.SingletonComponent
|
import dagger.hilt.components.SingletonComponent
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import javax.inject.Provider
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
|
@ -51,9 +52,9 @@ class TunnelModule {
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideVpnService(
|
fun provideVpnService(
|
||||||
amneziaBackend: org.amnezia.awg.backend.Backend,
|
amneziaBackend: Provider<org.amnezia.awg.backend.Backend>,
|
||||||
@Userspace userspaceBackend: Backend,
|
@Userspace userspaceBackend: Provider<Backend>,
|
||||||
@Kernel kernelBackend: Backend,
|
@Kernel kernelBackend: Provider<Backend>,
|
||||||
appDataRepository: AppDataRepository,
|
appDataRepository: AppDataRepository,
|
||||||
@ApplicationScope applicationScope: CoroutineScope,
|
@ApplicationScope applicationScope: CoroutineScope,
|
||||||
@IoDispatcher ioDispatcher: CoroutineDispatcher
|
@IoDispatcher ioDispatcher: CoroutineDispatcher
|
||||||
|
|
|
@ -390,19 +390,14 @@ class WireGuardConnectivityWatcherService : ForegroundService() {
|
||||||
}
|
}
|
||||||
|
|
||||||
watcherState.isMobileDataConditionMet() -> {
|
watcherState.isMobileDataConditionMet() -> {
|
||||||
Timber.i("$autoTunnel - tunnel on on mobile data condition met")
|
Timber.i("$autoTunnel - tunnel on mobile data condition met")
|
||||||
val mobileDataTunnel = getMobileDataTunnel()
|
val mobileDataTunnel = getMobileDataTunnel()
|
||||||
val tunnel =
|
val tunnel =
|
||||||
mobileDataTunnel ?: appDataRepository.getPrimaryOrFirstTunnel()
|
mobileDataTunnel ?: appDataRepository.getPrimaryOrFirstTunnel()
|
||||||
if (isTunnelDown()) return@collectLatest serviceManager.startVpnServiceForeground(
|
if (isTunnelDown() || tunnelConfig?.isMobileDataTunnel == false) {
|
||||||
context,
|
|
||||||
tunnel?.id,
|
|
||||||
)
|
|
||||||
if (tunnelConfig?.isMobileDataTunnel == false && mobileDataTunnel != null) {
|
|
||||||
Timber.i("$autoTunnel - tunnel connected on mobile data is not preferred condition met, switching to preferred")
|
|
||||||
serviceManager.startVpnServiceForeground(
|
serviceManager.startVpnServiceForeground(
|
||||||
context,
|
context,
|
||||||
mobileDataTunnel.id,
|
tunnel?.id,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -417,8 +412,8 @@ class WireGuardConnectivityWatcherService : ForegroundService() {
|
||||||
tunnelConfig == null) {
|
tunnelConfig == null) {
|
||||||
Timber.i("$autoTunnel - tunnel on ssid not associated with current tunnel condition met")
|
Timber.i("$autoTunnel - tunnel on ssid not associated with current tunnel condition met")
|
||||||
getSsidTunnel(watcherState.currentNetworkSSID)?.let {
|
getSsidTunnel(watcherState.currentNetworkSSID)?.let {
|
||||||
Timber.i("Found tunnel associated with this SSID, bringing tunnel up")
|
Timber.i("Found tunnel associated with this SSID, bringing tunnel up: ${it.name}")
|
||||||
if (isTunnelDown()) serviceManager.startVpnServiceForeground(
|
if (isTunnelDown() || tunnelConfig?.id != it.id) serviceManager.startVpnServiceForeground(
|
||||||
context,
|
context,
|
||||||
it.id,
|
it.id,
|
||||||
)
|
)
|
||||||
|
|
|
@ -27,13 +27,14 @@ import kotlinx.coroutines.withContext
|
||||||
import org.amnezia.awg.backend.Tunnel
|
import org.amnezia.awg.backend.Tunnel
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Provider
|
||||||
|
|
||||||
class WireGuardTunnel
|
class WireGuardTunnel
|
||||||
@Inject
|
@Inject
|
||||||
constructor(
|
constructor(
|
||||||
private val userspaceAmneziaBackend: org.amnezia.awg.backend.Backend,
|
private val userspaceAmneziaBackend: Provider<org.amnezia.awg.backend.Backend>,
|
||||||
@Userspace private val userspaceBackend: Backend,
|
@Userspace private val userspaceBackend: Provider<Backend>,
|
||||||
@Kernel private val kernelBackend: Backend,
|
@Kernel private val kernelBackend: Provider<Backend>,
|
||||||
private val appDataRepository: AppDataRepository,
|
private val appDataRepository: AppDataRepository,
|
||||||
@ApplicationScope private val applicationScope: CoroutineScope,
|
@ApplicationScope private val applicationScope: CoroutineScope,
|
||||||
@IoDispatcher private val ioDispatcher: CoroutineDispatcher
|
@IoDispatcher private val ioDispatcher: CoroutineDispatcher
|
||||||
|
@ -44,7 +45,7 @@ constructor(
|
||||||
|
|
||||||
private var statsJob: Job? = null
|
private var statsJob: Job? = null
|
||||||
|
|
||||||
private var backend: Backend = userspaceBackend
|
private lateinit var backend: Backend;
|
||||||
|
|
||||||
private var backendIsWgUserspace = true
|
private var backendIsWgUserspace = true
|
||||||
|
|
||||||
|
@ -52,15 +53,16 @@ constructor(
|
||||||
|
|
||||||
init {
|
init {
|
||||||
applicationScope.launch(ioDispatcher) {
|
applicationScope.launch(ioDispatcher) {
|
||||||
|
backend = userspaceBackend.get()
|
||||||
appDataRepository.settings.getSettingsFlow().collect {
|
appDataRepository.settings.getSettingsFlow().collect {
|
||||||
if (it.isKernelEnabled && (backendIsWgUserspace || backendIsAmneziaUserspace)) {
|
if (it.isKernelEnabled && (backendIsWgUserspace || backendIsAmneziaUserspace)) {
|
||||||
Timber.i("Setting kernel backend")
|
Timber.i("Setting kernel backend")
|
||||||
backend = kernelBackend
|
backend = kernelBackend.get()
|
||||||
backendIsWgUserspace = false
|
backendIsWgUserspace = false
|
||||||
backendIsAmneziaUserspace = false
|
backendIsAmneziaUserspace = false
|
||||||
} else if (!it.isKernelEnabled && !it.isAmneziaEnabled && !backendIsWgUserspace) {
|
} else if (!it.isKernelEnabled && !it.isAmneziaEnabled && !backendIsWgUserspace) {
|
||||||
Timber.i("Setting WireGuard userspace backend")
|
Timber.i("Setting WireGuard userspace backend")
|
||||||
backend = userspaceBackend
|
backend = userspaceBackend.get()
|
||||||
backendIsWgUserspace = true
|
backendIsWgUserspace = true
|
||||||
backendIsAmneziaUserspace = false
|
backendIsAmneziaUserspace = false
|
||||||
} else if (it.isAmneziaEnabled && !backendIsAmneziaUserspace) {
|
} else if (it.isAmneziaEnabled && !backendIsAmneziaUserspace) {
|
||||||
|
@ -81,7 +83,8 @@ constructor(
|
||||||
TunnelConfig.configFromAmQuick(it.wgQuick)
|
TunnelConfig.configFromAmQuick(it.wgQuick)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val state = userspaceAmneziaBackend.setState(this, tunnelState.toAmState(), config)
|
val state =
|
||||||
|
userspaceAmneziaBackend.get().setState(this, tunnelState.toAmState(), config)
|
||||||
TunnelState.from(state)
|
TunnelState.from(state)
|
||||||
} else {
|
} else {
|
||||||
Timber.i("Using Wg backend")
|
Timber.i("Using Wg backend")
|
||||||
|
@ -156,7 +159,9 @@ constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getState(): TunnelState {
|
override fun getState(): TunnelState {
|
||||||
return if (backendIsAmneziaUserspace) TunnelState.from(userspaceAmneziaBackend.getState(this))
|
return if (backendIsAmneziaUserspace) TunnelState.from(
|
||||||
|
userspaceAmneziaBackend.get().getState(this),
|
||||||
|
)
|
||||||
else TunnelState.from(backend.getState(this))
|
else TunnelState.from(backend.getState(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,7 +192,11 @@ constructor(
|
||||||
private fun startTunnelStatisticsJob() = applicationScope.launch(ioDispatcher) {
|
private fun startTunnelStatisticsJob() = applicationScope.launch(ioDispatcher) {
|
||||||
while (true) {
|
while (true) {
|
||||||
if (backendIsAmneziaUserspace) {
|
if (backendIsAmneziaUserspace) {
|
||||||
emitBackendStatistics(AmneziaStatistics(userspaceAmneziaBackend.getStatistics(this@WireGuardTunnel)))
|
emitBackendStatistics(
|
||||||
|
AmneziaStatistics(
|
||||||
|
userspaceAmneziaBackend.get().getStatistics(this@WireGuardTunnel),
|
||||||
|
),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
emitBackendStatistics(WireGuardStatistics(backend.getStatistics(this@WireGuardTunnel)))
|
emitBackendStatistics(WireGuardStatistics(backend.getStatistics(this@WireGuardTunnel)))
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,7 @@ import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.SideEffect
|
import androidx.compose.runtime.SideEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
@ -70,7 +71,6 @@ import androidx.navigation.NavController
|
||||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||||
import com.google.accompanist.permissions.isGranted
|
import com.google.accompanist.permissions.isGranted
|
||||||
import com.google.accompanist.permissions.rememberPermissionState
|
import com.google.accompanist.permissions.rememberPermissionState
|
||||||
import com.wireguard.android.backend.WgQuickBackend
|
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
import com.zaneschepke.wireguardautotunnel.WireGuardAutoTunnel
|
import com.zaneschepke.wireguardautotunnel.WireGuardAutoTunnel
|
||||||
import com.zaneschepke.wireguardautotunnel.data.domain.TunnelConfig
|
import com.zaneschepke.wireguardautotunnel.data.domain.TunnelConfig
|
||||||
|
@ -106,6 +106,7 @@ fun SettingsScreen(
|
||||||
val pinExists = remember { mutableStateOf(PinManager.pinExists()) }
|
val pinExists = remember { mutableStateOf(PinManager.pinExists()) }
|
||||||
|
|
||||||
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||||
|
val kernelSupport by viewModel.kernelSupport.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
val fineLocationState = rememberPermissionState(Manifest.permission.ACCESS_FINE_LOCATION)
|
val fineLocationState = rememberPermissionState(Manifest.permission.ACCESS_FINE_LOCATION)
|
||||||
var currentText by remember { mutableStateOf("") }
|
var currentText by remember { mutableStateOf("") }
|
||||||
|
@ -117,6 +118,10 @@ fun SettingsScreen(
|
||||||
val screenPadding = 5.dp
|
val screenPadding = 5.dp
|
||||||
val fillMaxWidth = .85f
|
val fillMaxWidth = .85f
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
viewModel.checkKernelSupport()
|
||||||
|
}
|
||||||
|
|
||||||
val startForResult =
|
val startForResult =
|
||||||
rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult ->
|
rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult ->
|
||||||
if (result.resultCode == Activity.RESULT_OK) {
|
if (result.resultCode == Activity.RESULT_OK) {
|
||||||
|
@ -591,7 +596,7 @@ fun SettingsScreen(
|
||||||
viewModel.onToggleAmnezia()
|
viewModel.onToggleAmnezia()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if (WgQuickBackend.hasKernelSupport()) {
|
if (kernelSupport) {
|
||||||
ConfigurationToggle(
|
ConfigurationToggle(
|
||||||
stringResource(R.string.use_kernel),
|
stringResource(R.string.use_kernel),
|
||||||
enabled =
|
enabled =
|
||||||
|
@ -601,8 +606,10 @@ fun SettingsScreen(
|
||||||
checked = uiState.settings.isKernelEnabled,
|
checked = uiState.settings.isKernelEnabled,
|
||||||
padding = screenPadding,
|
padding = screenPadding,
|
||||||
onCheckChanged = {
|
onCheckChanged = {
|
||||||
viewModel.onToggleKernelMode().onFailure {
|
scope.launch {
|
||||||
appViewModel.showSnackbarMessage(it.getMessage(context))
|
viewModel.onToggleKernelMode().onFailure {
|
||||||
|
appViewModel.showSnackbarMessage(it.getMessage(context))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -9,5 +9,5 @@ data class SettingsUiState(
|
||||||
val tunnels: List<TunnelConfig> = emptyList(),
|
val tunnels: List<TunnelConfig> = emptyList(),
|
||||||
val vpnState: VpnState = VpnState(),
|
val vpnState: VpnState = VpnState(),
|
||||||
val isLocationDisclosureShown: Boolean = true,
|
val isLocationDisclosureShown: Boolean = true,
|
||||||
val isBatteryOptimizeDisableShown: Boolean = false
|
val isBatteryOptimizeDisableShown: Boolean = false,
|
||||||
)
|
)
|
||||||
|
|
|
@ -5,23 +5,31 @@ import android.location.LocationManager
|
||||||
import androidx.core.location.LocationManagerCompat
|
import androidx.core.location.LocationManagerCompat
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.wireguard.android.backend.WgQuickBackend
|
||||||
import com.wireguard.android.util.RootShell
|
import com.wireguard.android.util.RootShell
|
||||||
import com.zaneschepke.wireguardautotunnel.WireGuardAutoTunnel
|
import com.zaneschepke.wireguardautotunnel.WireGuardAutoTunnel
|
||||||
import com.zaneschepke.wireguardautotunnel.data.domain.Settings
|
import com.zaneschepke.wireguardautotunnel.data.domain.Settings
|
||||||
import com.zaneschepke.wireguardautotunnel.data.repository.AppDataRepository
|
import com.zaneschepke.wireguardautotunnel.data.repository.AppDataRepository
|
||||||
|
import com.zaneschepke.wireguardautotunnel.module.IoDispatcher
|
||||||
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager
|
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager
|
||||||
import com.zaneschepke.wireguardautotunnel.service.tunnel.VpnService
|
import com.zaneschepke.wireguardautotunnel.service.tunnel.VpnService
|
||||||
import com.zaneschepke.wireguardautotunnel.util.Constants
|
import com.zaneschepke.wireguardautotunnel.util.Constants
|
||||||
import com.zaneschepke.wireguardautotunnel.util.FileUtils
|
import com.zaneschepke.wireguardautotunnel.util.FileUtils
|
||||||
import com.zaneschepke.wireguardautotunnel.util.WgTunnelExceptions
|
import com.zaneschepke.wireguardautotunnel.util.WgTunnelExceptions
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Provider
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class SettingsViewModel
|
class SettingsViewModel
|
||||||
|
@ -29,11 +37,15 @@ class SettingsViewModel
|
||||||
constructor(
|
constructor(
|
||||||
private val appDataRepository: AppDataRepository,
|
private val appDataRepository: AppDataRepository,
|
||||||
private val serviceManager: ServiceManager,
|
private val serviceManager: ServiceManager,
|
||||||
private val rootShell: RootShell,
|
private val rootShell: Provider<RootShell>,
|
||||||
private val fileUtils: FileUtils,
|
private val fileUtils: FileUtils,
|
||||||
|
@IoDispatcher private val ioDispatcher: CoroutineDispatcher,
|
||||||
vpnService: VpnService
|
vpnService: VpnService
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
|
private val _kernelSupport = MutableStateFlow(false)
|
||||||
|
val kernelSupport = _kernelSupport.asStateFlow()
|
||||||
|
|
||||||
val uiState =
|
val uiState =
|
||||||
combine(
|
combine(
|
||||||
appDataRepository.settings.getSettingsFlow(),
|
appDataRepository.settings.getSettingsFlow(),
|
||||||
|
@ -181,27 +193,29 @@ constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onToggleKernelMode(): Result<Unit> {
|
suspend fun onToggleKernelMode(): Result<Unit> {
|
||||||
if (!uiState.value.settings.isKernelEnabled) {
|
return withContext(ioDispatcher) {
|
||||||
try {
|
if (!uiState.value.settings.isKernelEnabled) {
|
||||||
rootShell.start()
|
try {
|
||||||
Timber.i("Root shell accepted!")
|
rootShell.get().start()
|
||||||
saveSettings(
|
Timber.i("Root shell accepted!")
|
||||||
uiState.value.settings.copy(
|
saveSettings(
|
||||||
isKernelEnabled = true,
|
uiState.value.settings.copy(
|
||||||
isAmneziaEnabled = false,
|
isKernelEnabled = true,
|
||||||
),
|
isAmneziaEnabled = false,
|
||||||
)
|
),
|
||||||
|
)
|
||||||
|
|
||||||
} catch (e: RootShell.RootShellException) {
|
} catch (e: RootShell.RootShellException) {
|
||||||
Timber.e(e)
|
Timber.e(e)
|
||||||
|
saveKernelMode(on = false)
|
||||||
|
return@withContext Result.failure(WgTunnelExceptions.RootDenied())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
saveKernelMode(on = false)
|
saveKernelMode(on = false)
|
||||||
return Result.failure(WgTunnelExceptions.RootDenied())
|
|
||||||
}
|
}
|
||||||
} else {
|
Result.success(Unit)
|
||||||
saveKernelMode(on = false)
|
|
||||||
}
|
}
|
||||||
return Result.success(Unit)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onToggleRestartOnPing() = viewModelScope.launch {
|
fun onToggleRestartOnPing() = viewModelScope.launch {
|
||||||
|
@ -211,4 +225,13 @@ constructor(
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun checkKernelSupport() = viewModelScope.launch {
|
||||||
|
val kernelSupport = withContext(ioDispatcher) {
|
||||||
|
WgQuickBackend.hasKernelSupport()
|
||||||
|
}
|
||||||
|
_kernelSupport.update {
|
||||||
|
kernelSupport
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
object Constants {
|
object Constants {
|
||||||
const val VERSION_NAME = "3.4.5"
|
const val VERSION_NAME = "3.4.6"
|
||||||
const val JVM_TARGET = "17"
|
const val JVM_TARGET = "17"
|
||||||
const val VERSION_CODE = 34500
|
const val VERSION_CODE = 34600
|
||||||
const val TARGET_SDK = 34
|
const val TARGET_SDK = 34
|
||||||
const val MIN_SDK = 26
|
const val MIN_SDK = 26
|
||||||
const val APP_ID = "com.zaneschepke.wireguardautotunnel"
|
const val APP_ID = "com.zaneschepke.wireguardautotunnel"
|
||||||
const val APP_NAME = "wgtunnel"
|
const val APP_NAME = "wgtunnel"
|
||||||
const val COMPOSE_COMPILER_EXTENSION_VERSION = "1.5.11"
|
const val COMPOSE_COMPILER_EXTENSION_VERSION = "1.5.14"
|
||||||
|
|
||||||
|
|
||||||
const val STORE_PASS_VAR = "SIGNING_STORE_PASSWORD"
|
const val STORE_PASS_VAR = "SIGNING_STORE_PASSWORD"
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
What's new:
|
||||||
|
- Fixes auto tunneling bugs
|
||||||
|
- Fixes android backup bug
|
||||||
|
- Bump versions
|
|
@ -3,7 +3,7 @@ accompanist = "0.34.0"
|
||||||
activityCompose = "1.9.0"
|
activityCompose = "1.9.0"
|
||||||
amneziawgAndroid = "1.2.0"
|
amneziawgAndroid = "1.2.0"
|
||||||
androidx-junit = "1.1.5"
|
androidx-junit = "1.1.5"
|
||||||
appcompat = "1.6.1"
|
appcompat = "1.7.0"
|
||||||
biometricKtx = "1.2.0-alpha05"
|
biometricKtx = "1.2.0-alpha05"
|
||||||
coreGoogleShortcuts = "1.1.0"
|
coreGoogleShortcuts = "1.1.0"
|
||||||
coreKtx = "1.13.1"
|
coreKtx = "1.13.1"
|
||||||
|
@ -23,14 +23,14 @@ roomVersion = "2.6.1"
|
||||||
timber = "5.0.1"
|
timber = "5.0.1"
|
||||||
tunnel = "1.0.20230706"
|
tunnel = "1.0.20230706"
|
||||||
androidGradlePlugin = "8.4.1"
|
androidGradlePlugin = "8.4.1"
|
||||||
kotlin = "1.9.23"
|
kotlin = "1.9.24"
|
||||||
ksp = "1.9.23-1.0.19"
|
ksp = "1.9.24-1.0.20"
|
||||||
composeBom = "2024.05.00"
|
composeBom = "2024.05.00"
|
||||||
compose = "1.6.7"
|
compose = "1.6.7"
|
||||||
zxingAndroidEmbedded = "4.3.0"
|
zxingAndroidEmbedded = "4.3.0"
|
||||||
|
|
||||||
#plugins
|
#plugins
|
||||||
gradlePlugins-kotlinxSerialization = "1.9.23"
|
gradlePlugins-kotlinxSerialization = "1.9.24"
|
||||||
material = "1.12.0"
|
material = "1.12.0"
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue