parent
9a2d77c8bf
commit
9bb30069fe
|
@ -5,6 +5,7 @@ import androidx.room.Entity
|
||||||
import androidx.room.Index
|
import androidx.room.Index
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
import com.wireguard.config.Config
|
import com.wireguard.config.Config
|
||||||
|
import com.zaneschepke.wireguardautotunnel.util.extensions.toWgQuickString
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
|
|
||||||
@Entity(indices = [Index(value = ["name"], unique = true)])
|
@Entity(indices = [Index(value = ["name"], unique = true)])
|
||||||
|
@ -79,6 +80,12 @@ data class TunnelConfig(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun tunnelConfigFromAmConfig(config: org.amnezia.awg.config.Config, name: String): TunnelConfig {
|
||||||
|
val amQuick = config.toAwgQuickString(true)
|
||||||
|
val wgQuick = config.toWgQuickString()
|
||||||
|
return TunnelConfig(name = name, wgQuick = wgQuick, amQuick = amQuick)
|
||||||
|
}
|
||||||
|
|
||||||
const val AM_QUICK_DEFAULT = ""
|
const val AM_QUICK_DEFAULT = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,7 @@ import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import androidx.compose.ui.input.pointer.pointerInput
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
|
import androidx.compose.ui.platform.LocalClipboardManager
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
@ -68,6 +69,7 @@ import com.zaneschepke.wireguardautotunnel.util.extensions.scaledHeight
|
||||||
fun MainScreen(viewModel: MainViewModel = hiltViewModel(), uiState: AppUiState) {
|
fun MainScreen(viewModel: MainViewModel = hiltViewModel(), uiState: AppUiState) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val navController = LocalNavController.current
|
val navController = LocalNavController.current
|
||||||
|
val clipboard = LocalClipboardManager.current
|
||||||
val snackbar = SnackbarController.current
|
val snackbar = SnackbarController.current
|
||||||
|
|
||||||
var showBottomSheet by remember { mutableStateOf(false) }
|
var showBottomSheet by remember { mutableStateOf(false) }
|
||||||
|
@ -201,12 +203,17 @@ fun MainScreen(viewModel: MainViewModel = hiltViewModel(), uiState: AppUiState)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
) {
|
) { padding ->
|
||||||
TunnelImportSheet(
|
TunnelImportSheet(
|
||||||
showBottomSheet,
|
showBottomSheet,
|
||||||
onDismiss = { showBottomSheet = false },
|
onDismiss = { showBottomSheet = false },
|
||||||
onFileClick = { tunnelFileImportResultLauncher.launch(Constants.ALLOWED_TV_FILE_TYPES) },
|
onFileClick = { tunnelFileImportResultLauncher.launch(Constants.ALLOWED_TV_FILE_TYPES) },
|
||||||
onQrClick = { requestPermissionLauncher.launch(android.Manifest.permission.CAMERA) },
|
onQrClick = { requestPermissionLauncher.launch(android.Manifest.permission.CAMERA) },
|
||||||
|
onClipboardClick = {
|
||||||
|
clipboard.getText()?.text?.let {
|
||||||
|
viewModel.onClipboardImport(it)
|
||||||
|
}
|
||||||
|
},
|
||||||
onManualImportClick = {
|
onManualImportClick = {
|
||||||
navController.navigate(
|
navController.navigate(
|
||||||
Route.Config(Constants.MANUAL_TUNNEL_CONFIG_ID),
|
Route.Config(Constants.MANUAL_TUNNEL_CONFIG_ID),
|
||||||
|
@ -218,7 +225,7 @@ fun MainScreen(viewModel: MainViewModel = hiltViewModel(), uiState: AppUiState)
|
||||||
verticalArrangement = Arrangement.spacedBy(5.dp.scaledHeight(), Alignment.Top),
|
verticalArrangement = Arrangement.spacedBy(5.dp.scaledHeight(), Alignment.Top),
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxSize().padding(it)
|
.fillMaxSize().padding(padding)
|
||||||
.overscroll(ScrollableDefaults.overscrollEffect())
|
.overscroll(ScrollableDefaults.overscrollEffect())
|
||||||
.nestedScroll(nestedScrollConnection),
|
.nestedScroll(nestedScrollConnection),
|
||||||
state = rememberLazyListState(0, uiState.tunnels.count()),
|
state = rememberLazyListState(0, uiState.tunnels.count()),
|
||||||
|
|
|
@ -261,4 +261,14 @@ constructor(
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onClipboardImport(config: String) = viewModelScope.launch(ioDispatcher) {
|
||||||
|
runCatching {
|
||||||
|
val amConfig = TunnelConfig.configFromAmQuick(config)
|
||||||
|
val tunnelConfig = TunnelConfig.tunnelConfigFromAmConfig(amConfig, makeTunnelNameUnique(generateQrCodeDefaultName(config)))
|
||||||
|
saveTunnel(tunnelConfig)
|
||||||
|
}.onFailure {
|
||||||
|
SnackbarController.showMessage(StringValue.StringResource(R.string.error_file_format))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.ContentPasteGo
|
||||||
import androidx.compose.material.icons.filled.Create
|
import androidx.compose.material.icons.filled.Create
|
||||||
import androidx.compose.material.icons.filled.FileOpen
|
import androidx.compose.material.icons.filled.FileOpen
|
||||||
import androidx.compose.material.icons.filled.QrCode
|
import androidx.compose.material.icons.filled.QrCode
|
||||||
|
@ -22,9 +23,17 @@ import androidx.compose.ui.unit.dp
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.isRunningOnTv
|
import com.zaneschepke.wireguardautotunnel.util.extensions.isRunningOnTv
|
||||||
|
|
||||||
|
// TODO refactor this component
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun TunnelImportSheet(show: Boolean, onDismiss: () -> Unit, onFileClick: () -> Unit, onQrClick: () -> Unit, onManualImportClick: () -> Unit) {
|
fun TunnelImportSheet(
|
||||||
|
show: Boolean,
|
||||||
|
onDismiss: () -> Unit,
|
||||||
|
onFileClick: () -> Unit,
|
||||||
|
onQrClick: () -> Unit,
|
||||||
|
onManualImportClick: () -> Unit,
|
||||||
|
onClipboardClick: () -> Unit,
|
||||||
|
) {
|
||||||
val sheetState = rememberModalBottomSheetState()
|
val sheetState = rememberModalBottomSheetState()
|
||||||
|
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
@ -77,6 +86,28 @@ fun TunnelImportSheet(show: Boolean, onDismiss: () -> Unit, onFileClick: () -> U
|
||||||
modifier = Modifier.padding(10.dp),
|
modifier = Modifier.padding(10.dp),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
HorizontalDivider()
|
||||||
|
Row(
|
||||||
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clickable {
|
||||||
|
onDismiss()
|
||||||
|
onClipboardClick()
|
||||||
|
}
|
||||||
|
.padding(10.dp),
|
||||||
|
) {
|
||||||
|
val icon = Icons.Filled.ContentPasteGo
|
||||||
|
Icon(
|
||||||
|
icon,
|
||||||
|
contentDescription = icon.name,
|
||||||
|
modifier = Modifier.padding(10.dp),
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
stringResource(id = R.string.add_from_clipboard),
|
||||||
|
modifier = Modifier.padding(10.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
HorizontalDivider()
|
HorizontalDivider()
|
||||||
Row(
|
Row(
|
||||||
|
|
|
@ -9,11 +9,9 @@ import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.viewinterop.AndroidView
|
import androidx.compose.ui.viewinterop.AndroidView
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
|
||||||
import com.journeyapps.barcodescanner.CompoundBarcodeView
|
import com.journeyapps.barcodescanner.CompoundBarcodeView
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.navigation.LocalNavController
|
import com.zaneschepke.wireguardautotunnel.ui.common.navigation.LocalNavController
|
||||||
|
|
||||||
@OptIn(ExperimentalPermissionsApi::class)
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ScannerScreen(viewModel: ScannerViewModel = hiltViewModel()) {
|
fun ScannerScreen(viewModel: ScannerViewModel = hiltViewModel()) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
|
@ -9,7 +9,6 @@ import com.zaneschepke.wireguardautotunnel.module.IoDispatcher
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.snackbar.SnackbarController
|
import com.zaneschepke.wireguardautotunnel.ui.common.snackbar.SnackbarController
|
||||||
import com.zaneschepke.wireguardautotunnel.util.NumberUtils
|
import com.zaneschepke.wireguardautotunnel.util.NumberUtils
|
||||||
import com.zaneschepke.wireguardautotunnel.util.StringValue
|
import com.zaneschepke.wireguardautotunnel.util.StringValue
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.toWgQuickString
|
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
@ -45,10 +44,7 @@ constructor(
|
||||||
fun onTunnelQrResult(result: String) = viewModelScope.launch(ioDispatcher) {
|
fun onTunnelQrResult(result: String) = viewModelScope.launch(ioDispatcher) {
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
val amConfig = TunnelConfig.configFromAmQuick(result)
|
val amConfig = TunnelConfig.configFromAmQuick(result)
|
||||||
val amQuick = amConfig.toAwgQuickString(true)
|
val tunnelConfig = TunnelConfig.tunnelConfigFromAmConfig(amConfig, makeTunnelNameUnique(generateQrCodeDefaultName(result)))
|
||||||
val wgQuick = amConfig.toWgQuickString()
|
|
||||||
val tunnelName = makeTunnelNameUnique(generateQrCodeDefaultName(result))
|
|
||||||
val tunnelConfig = TunnelConfig(name = tunnelName, wgQuick = wgQuick, amQuick = amQuick)
|
|
||||||
appDataRepository.tunnels.save(tunnelConfig)
|
appDataRepository.tunnels.save(tunnelConfig)
|
||||||
_success.emit(true)
|
_success.emit(true)
|
||||||
}.onFailure {
|
}.onFailure {
|
||||||
|
|
|
@ -177,4 +177,5 @@
|
||||||
<string name="enable_local_logging">Enable local logging</string>
|
<string name="enable_local_logging">Enable local logging</string>
|
||||||
<string name="configuration_change">Configuration change</string>
|
<string name="configuration_change">Configuration change</string>
|
||||||
<string name="requires_app_relaunch">This change requires an app relaunch. Would you like to proceed?</string>
|
<string name="requires_app_relaunch">This change requires an app relaunch. Would you like to proceed?</string>
|
||||||
|
<string name="add_from_clipboard">Add from clipboard</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Reference in New Issue