mirror of
https://github.com/wgtunnel/android.git
synced 2026-06-02 08:33:40 +02:00
fix: auto-tunnel screen not loading without wifi
Fixes auto tunnel screen failing to load if you haven't connected to wifi once. Fixes import via url. Closes #1108 Closes #1105
This commit is contained in:
+26
-21
@@ -1,42 +1,47 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.components
|
package com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.material3.AlertDialog
|
|
||||||
import androidx.compose.material3.OutlinedTextField
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
|
import com.zaneschepke.wireguardautotunnel.ui.common.dialog.InfoDialog
|
||||||
|
import com.zaneschepke.wireguardautotunnel.ui.common.textbox.ConfigurationTextBox
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun UrlImportDialog(onDismiss: () -> Unit, onConfirm: (String) -> Unit) {
|
fun UrlImportDialog(onDismiss: () -> Unit, onConfirm: (String) -> Unit) {
|
||||||
var url by remember { mutableStateOf("") }
|
var url by remember { mutableStateOf("") }
|
||||||
|
var isError by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
AlertDialog(
|
LaunchedEffect(url) { isError = false }
|
||||||
onDismissRequest = onDismiss,
|
|
||||||
title = { Text(stringResource(R.string.add_from_url)) },
|
InfoDialog(
|
||||||
text = {
|
onDismiss = onDismiss,
|
||||||
Column(modifier = Modifier.fillMaxWidth().padding(vertical = 8.dp)) {
|
title = stringResource(R.string.add_from_url),
|
||||||
OutlinedTextField(
|
body = {
|
||||||
|
Column(verticalArrangement = Arrangement.spacedBy(24.dp)) {
|
||||||
|
Text(
|
||||||
|
stringResource(R.string.import_url_description),
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
)
|
||||||
|
ConfigurationTextBox(
|
||||||
value = url,
|
value = url,
|
||||||
|
label = stringResource(R.string.enter_config_url),
|
||||||
|
hint = stringResource(R.string.example_import_url),
|
||||||
onValueChange = { url = it },
|
onValueChange = { url = it },
|
||||||
label = { Text(stringResource(R.string.enter_config_url)) },
|
isError = isError,
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
confirmButton = {
|
confirmText = stringResource(R.string.okay),
|
||||||
TextButton(onClick = { onConfirm(url) }, enabled = url.isNotBlank()) {
|
onAttest = {
|
||||||
Text(stringResource(R.string.okay))
|
if (url.isNotBlank() && url.startsWith("https://")) {
|
||||||
}
|
onConfirm(url)
|
||||||
},
|
} else isError = true
|
||||||
dismissButton = {
|
|
||||||
TextButton(onClick = onDismiss) { Text(stringResource(R.string.cancel)) }
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-12
@@ -1,7 +1,6 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.viewmodel
|
package com.zaneschepke.wireguardautotunnel.viewmodel
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.core.net.toUri
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import com.wireguard.android.backend.WgQuickBackend
|
import com.wireguard.android.backend.WgQuickBackend
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
@@ -24,9 +23,11 @@ import com.zaneschepke.wireguardautotunnel.util.extensions.TunnelName
|
|||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.asStringValue
|
import com.zaneschepke.wireguardautotunnel.util.extensions.asStringValue
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.saveTunnelsUniquely
|
import com.zaneschepke.wireguardautotunnel.util.extensions.saveTunnelsUniquely
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import io.ktor.client.HttpClient
|
||||||
|
import io.ktor.client.request.prepareGet
|
||||||
|
import io.ktor.client.statement.bodyAsText
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.net.URL
|
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
@@ -50,6 +51,7 @@ constructor(
|
|||||||
private val settingsRepository: GeneralSettingRepository,
|
private val settingsRepository: GeneralSettingRepository,
|
||||||
private val monitoringSettingsRepository: MonitoringSettingsRepository,
|
private val monitoringSettingsRepository: MonitoringSettingsRepository,
|
||||||
private val rootShellUtils: RootShellUtils,
|
private val rootShellUtils: RootShellUtils,
|
||||||
|
private val httpClient: HttpClient,
|
||||||
private val fileUtils: FileUtils,
|
private val fileUtils: FileUtils,
|
||||||
) : ContainerHost<SharedAppUiState, LocalSideEffect>, ViewModel() {
|
) : ContainerHost<SharedAppUiState, LocalSideEffect>, ViewModel() {
|
||||||
|
|
||||||
@@ -239,18 +241,23 @@ constructor(
|
|||||||
fun importFromQr(conf: String) = intent { importFromClipboard(conf) }
|
fun importFromQr(conf: String) = intent { importFromClipboard(conf) }
|
||||||
|
|
||||||
fun importFromUrl(url: String) = intent {
|
fun importFromUrl(url: String) = intent {
|
||||||
runCatching {
|
try {
|
||||||
val url = URL(url)
|
httpClient.prepareGet(url).execute { response ->
|
||||||
val uri = url.toURI().toString().toUri()
|
if (response.status.value in 200..299) {
|
||||||
importFromUri(uri)
|
val body = response.bodyAsText()
|
||||||
}
|
importFromClipboard(body)
|
||||||
.onFailure {
|
} else {
|
||||||
postSideEffect(
|
throw IOException(
|
||||||
GlobalSideEffect.Toast(
|
"Failed to download file with error status: ${response.status.value}"
|
||||||
StringValue.StringResource(R.string.error_download_failed)
|
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.e(e)
|
||||||
|
postSideEffect(
|
||||||
|
GlobalSideEffect.Toast(StringValue.StringResource(R.string.error_download_failed))
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun importFromUri(uri: Uri) = intent {
|
fun importFromUri(uri: Uri) = intent {
|
||||||
|
|||||||
@@ -459,4 +459,6 @@
|
|||||||
<string name="copy_from">Copy from</string>
|
<string name="copy_from">Copy from</string>
|
||||||
<string name="mode">Mode</string>
|
<string name="mode">Mode</string>
|
||||||
<string name="app_selection">App selection</string>
|
<string name="app_selection">App selection</string>
|
||||||
|
<string name="example_import_url" translatable="false">https://123.com/tun.conf</string>
|
||||||
|
<string name="import_url_description">The URL must be secure and serve a .conf file.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
+4
-2
@@ -15,8 +15,6 @@ import com.wireguard.android.util.RootShell
|
|||||||
import com.zaneschepke.networkmonitor.AndroidNetworkMonitor.WifiDetectionMethod.*
|
import com.zaneschepke.networkmonitor.AndroidNetworkMonitor.WifiDetectionMethod.*
|
||||||
import com.zaneschepke.networkmonitor.shizuku.ShizukuShell
|
import com.zaneschepke.networkmonitor.shizuku.ShizukuShell
|
||||||
import com.zaneschepke.networkmonitor.util.*
|
import com.zaneschepke.networkmonitor.util.*
|
||||||
import kotlin.concurrent.atomics.AtomicReference
|
|
||||||
import kotlin.concurrent.atomics.ExperimentalAtomicApi
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.FlowPreview
|
import kotlinx.coroutines.FlowPreview
|
||||||
@@ -24,6 +22,8 @@ import kotlinx.coroutines.channels.awaitClose
|
|||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
import kotlinx.coroutines.withTimeoutOrNull
|
import kotlinx.coroutines.withTimeoutOrNull
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
import kotlin.concurrent.atomics.AtomicReference
|
||||||
|
import kotlin.concurrent.atomics.ExperimentalAtomicApi
|
||||||
|
|
||||||
class AndroidNetworkMonitor(
|
class AndroidNetworkMonitor(
|
||||||
private val appContext: Context,
|
private val appContext: Context,
|
||||||
@@ -212,6 +212,8 @@ class AndroidNetworkMonitor(
|
|||||||
|
|
||||||
connectivityManager?.registerNetworkCallback(request, wifiCallback!!)
|
connectivityManager?.registerNetworkCallback(request, wifiCallback!!)
|
||||||
|
|
||||||
|
trySend(TransportEvent.Unknown)
|
||||||
|
|
||||||
awaitClose {
|
awaitClose {
|
||||||
runCatching { connectivityManager?.unregisterNetworkCallback(wifiCallback!!) }
|
runCatching { connectivityManager?.unregisterNetworkCallback(wifiCallback!!) }
|
||||||
.onFailure { Timber.e(it, "Error unregistering WiFi network callback") }
|
.onFailure { Timber.e(it, "Error unregistering WiFi network callback") }
|
||||||
|
|||||||
Reference in New Issue
Block a user