parent
2f53a8f3b4
commit
49bf7fa8b9
|
@ -16,8 +16,8 @@ android {
|
||||||
compileSdk = 33
|
compileSdk = 33
|
||||||
|
|
||||||
val versionMajor = 1
|
val versionMajor = 1
|
||||||
val versionMinor = 1
|
val versionMinor = 2
|
||||||
val versionPatch = 6
|
val versionPatch = 0
|
||||||
val versionBuild = 0
|
val versionBuild = 0
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
|
@ -71,7 +71,7 @@ dependencies {
|
||||||
implementation("androidx.compose.ui:ui")
|
implementation("androidx.compose.ui:ui")
|
||||||
implementation("androidx.compose.ui:ui-graphics")
|
implementation("androidx.compose.ui:ui-graphics")
|
||||||
implementation("androidx.compose.ui:ui-tooling-preview")
|
implementation("androidx.compose.ui:ui-tooling-preview")
|
||||||
implementation("androidx.compose.material3:material3")
|
implementation("androidx.compose.material3:material3:1.1.1")
|
||||||
implementation("androidx.appcompat:appcompat:1.6.1")
|
implementation("androidx.appcompat:appcompat:1.6.1")
|
||||||
|
|
||||||
testImplementation("junit:junit:4.13.2")
|
testImplementation("junit:junit:4.13.2")
|
||||||
|
@ -89,7 +89,7 @@ dependencies {
|
||||||
implementation("com.jakewharton.timber:timber:5.0.1")
|
implementation("com.jakewharton.timber:timber:5.0.1")
|
||||||
|
|
||||||
// compose navigation
|
// compose navigation
|
||||||
implementation("androidx.navigation:navigation-compose:2.5.3")
|
implementation("androidx.navigation:navigation-compose:2.6.0")
|
||||||
implementation("androidx.hilt:hilt-navigation-compose:1.0.0")
|
implementation("androidx.hilt:hilt-navigation-compose:1.0.0")
|
||||||
|
|
||||||
// hilt
|
// hilt
|
||||||
|
@ -120,6 +120,11 @@ dependencies {
|
||||||
implementation("com.google.firebase:firebase-crashlytics-ktx")
|
implementation("com.google.firebase:firebase-crashlytics-ktx")
|
||||||
implementation("com.google.firebase:firebase-analytics-ktx")
|
implementation("com.google.firebase:firebase-analytics-ktx")
|
||||||
|
|
||||||
|
//barcode scanning
|
||||||
|
implementation("com.google.android.gms:play-services-code-scanner:16.0.0")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
kapt {
|
kapt {
|
||||||
correctErrorTypes = true
|
correctErrorTypes = true
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
|
||||||
android:maxSdkVersion="32" />
|
android:maxSdkVersion="32" />
|
||||||
|
@ -59,5 +60,8 @@
|
||||||
<action android:name="android.intent.action.BOOT_COMPLETED"/>
|
<action android:name="android.intent.action.BOOT_COMPLETED"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
<meta-data
|
||||||
|
android:name="com.google.mlkit.vision.DEPENDENCIES"
|
||||||
|
android:value="barcode_ui"/>
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
|
@ -0,0 +1,41 @@
|
||||||
|
package com.zaneschepke.wireguardautotunnel.module
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.google.mlkit.vision.barcode.common.Barcode
|
||||||
|
import com.google.mlkit.vision.codescanner.GmsBarcodeScanner
|
||||||
|
import com.google.mlkit.vision.codescanner.GmsBarcodeScannerOptions
|
||||||
|
import com.google.mlkit.vision.codescanner.GmsBarcodeScanning
|
||||||
|
import com.zaneschepke.wireguardautotunnel.service.barcode.CodeScanner
|
||||||
|
import com.zaneschepke.wireguardautotunnel.service.barcode.QRScanner
|
||||||
|
import dagger.Binds
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.Provides
|
||||||
|
import dagger.hilt.InstallIn
|
||||||
|
import dagger.hilt.android.components.ViewModelComponent
|
||||||
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import dagger.hilt.android.scopes.ViewModelScoped
|
||||||
|
|
||||||
|
@Module
|
||||||
|
@InstallIn(ViewModelComponent::class)
|
||||||
|
class ScannerModule {
|
||||||
|
|
||||||
|
@ViewModelScoped
|
||||||
|
@Provides
|
||||||
|
fun provideBarCodeOptions() : GmsBarcodeScannerOptions {
|
||||||
|
return GmsBarcodeScannerOptions.Builder()
|
||||||
|
.setBarcodeFormats(Barcode.FORMAT_QR_CODE)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewModelScoped
|
||||||
|
@Provides
|
||||||
|
fun provideBarCodeScanner(@ApplicationContext context: Context, options: GmsBarcodeScannerOptions) : GmsBarcodeScanner {
|
||||||
|
return GmsBarcodeScanning.getClient(context, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewModelScoped
|
||||||
|
@Provides
|
||||||
|
fun provideQRScanner(gmsBarcodeScanner: GmsBarcodeScanner) : CodeScanner {
|
||||||
|
return QRScanner(gmsBarcodeScanner)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
package com.zaneschepke.wireguardautotunnel.module
|
package com.zaneschepke.wireguardautotunnel.module
|
||||||
|
|
||||||
|
import com.zaneschepke.wireguardautotunnel.service.barcode.CodeScanner
|
||||||
|
import com.zaneschepke.wireguardautotunnel.service.barcode.QRScanner
|
||||||
import com.zaneschepke.wireguardautotunnel.service.network.MobileDataService
|
import com.zaneschepke.wireguardautotunnel.service.network.MobileDataService
|
||||||
import com.zaneschepke.wireguardautotunnel.service.network.NetworkService
|
import com.zaneschepke.wireguardautotunnel.service.network.NetworkService
|
||||||
import com.zaneschepke.wireguardautotunnel.service.network.WifiService
|
import com.zaneschepke.wireguardautotunnel.service.network.WifiService
|
||||||
|
@ -10,6 +12,7 @@ import dagger.Module
|
||||||
import dagger.hilt.InstallIn
|
import dagger.hilt.InstallIn
|
||||||
import dagger.hilt.android.components.ServiceComponent
|
import dagger.hilt.android.components.ServiceComponent
|
||||||
import dagger.hilt.android.scopes.ServiceScoped
|
import dagger.hilt.android.scopes.ServiceScoped
|
||||||
|
import dagger.hilt.android.scopes.ViewModelScoped
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
@InstallIn(ServiceComponent::class)
|
@InstallIn(ServiceComponent::class)
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.zaneschepke.wireguardautotunnel.service.barcode
|
||||||
|
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
interface CodeScanner {
|
||||||
|
fun scan() : Flow<String?>
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.zaneschepke.wireguardautotunnel.service.barcode
|
||||||
|
|
||||||
|
import com.google.mlkit.vision.codescanner.GmsBarcodeScanner
|
||||||
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.callbackFlow
|
||||||
|
import timber.log.Timber
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class QRScanner @Inject constructor(private val gmsBarcodeScanner: GmsBarcodeScanner) : CodeScanner {
|
||||||
|
override fun scan(): Flow<String?> {
|
||||||
|
return callbackFlow {
|
||||||
|
gmsBarcodeScanner.startScan().addOnSuccessListener {
|
||||||
|
trySend(it.rawValue)
|
||||||
|
}.addOnFailureListener {
|
||||||
|
Timber.e(it.message)
|
||||||
|
}
|
||||||
|
awaitClose {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,7 +24,7 @@ class WireGuardTunnel @Inject constructor(private val backend : Backend) : VpnSe
|
||||||
override val state get() = _state.asSharedFlow()
|
override val state get() = _state.asSharedFlow()
|
||||||
|
|
||||||
override suspend fun startTunnel(tunnelConfig: TunnelConfig) : Tunnel.State{
|
override suspend fun startTunnel(tunnelConfig: TunnelConfig) : Tunnel.State{
|
||||||
try {
|
return try {
|
||||||
if(getState() == Tunnel.State.UP && _tunnelName.value != tunnelConfig.name) {
|
if(getState() == Tunnel.State.UP && _tunnelName.value != tunnelConfig.name) {
|
||||||
stopTunnel()
|
stopTunnel()
|
||||||
}
|
}
|
||||||
|
@ -33,10 +33,10 @@ class WireGuardTunnel @Inject constructor(private val backend : Backend) : VpnSe
|
||||||
val state = backend.setState(
|
val state = backend.setState(
|
||||||
this, Tunnel.State.UP, config)
|
this, Tunnel.State.UP, config)
|
||||||
_state.emit(state)
|
_state.emit(state)
|
||||||
return state;
|
state;
|
||||||
} catch (e : Exception) {
|
} catch (e : Exception) {
|
||||||
Timber.e("Failed to start tunnel with error: ${e.message}")
|
Timber.e("Failed to start tunnel with error: ${e.message}")
|
||||||
return Tunnel.State.DOWN
|
Tunnel.State.DOWN
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,28 +4,38 @@ import android.annotation.SuppressLint
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.gestures.detectTapGestures
|
import androidx.compose.foundation.gestures.detectTapGestures
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.FileOpen
|
||||||
|
import androidx.compose.material.icons.filled.QrCode
|
||||||
import androidx.compose.material.icons.rounded.Add
|
import androidx.compose.material.icons.rounded.Add
|
||||||
import androidx.compose.material.icons.rounded.Delete
|
import androidx.compose.material.icons.rounded.Delete
|
||||||
import androidx.compose.material.icons.rounded.Edit
|
import androidx.compose.material.icons.rounded.Edit
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.Divider
|
||||||
|
import androidx.compose.material3.DrawerValue
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.FabPosition
|
import androidx.compose.material3.FabPosition
|
||||||
import androidx.compose.material3.FloatingActionButton
|
import androidx.compose.material3.FloatingActionButton
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.ModalBottomSheet
|
||||||
|
import androidx.compose.material3.ModalDrawerSheet
|
||||||
|
import androidx.compose.material3.ModalNavigationDrawer
|
||||||
|
import androidx.compose.material3.NavigationDrawerItem
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.SnackbarDuration
|
import androidx.compose.material3.SnackbarDuration
|
||||||
|
@ -33,6 +43,8 @@ import androidx.compose.material3.SnackbarHostState
|
||||||
import androidx.compose.material3.SnackbarResult
|
import androidx.compose.material3.SnackbarResult
|
||||||
import androidx.compose.material3.Switch
|
import androidx.compose.material3.Switch
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.rememberDrawerState
|
||||||
|
import androidx.compose.material3.rememberModalBottomSheetState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
@ -45,6 +57,7 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||||
import androidx.compose.ui.input.pointer.pointerInput
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
|
import androidx.compose.ui.modifier.modifierLocalConsumer
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
@ -68,6 +81,8 @@ fun MainScreen(viewModel: MainViewModel = hiltViewModel(), padding : PaddingValu
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
val sheetState = rememberModalBottomSheetState()
|
||||||
|
var showBottomSheet by remember { mutableStateOf(false) }
|
||||||
val tunnels by viewModel.tunnels.collectAsStateWithLifecycle(mutableListOf())
|
val tunnels by viewModel.tunnels.collectAsStateWithLifecycle(mutableListOf())
|
||||||
val viewState = viewModel.viewState.collectAsStateWithLifecycle()
|
val viewState = viewModel.viewState.collectAsStateWithLifecycle()
|
||||||
var showAlertDialog by remember { mutableStateOf(false) }
|
var showAlertDialog by remember { mutableStateOf(false) }
|
||||||
|
@ -109,7 +124,7 @@ fun MainScreen(viewModel: MainViewModel = hiltViewModel(), padding : PaddingValu
|
||||||
FloatingActionButton(
|
FloatingActionButton(
|
||||||
modifier = Modifier.padding(bottom = 90.dp),
|
modifier = Modifier.padding(bottom = 90.dp),
|
||||||
onClick = {
|
onClick = {
|
||||||
pickFileLauncher.launch("*/*")
|
showBottomSheet = true
|
||||||
},
|
},
|
||||||
containerColor = MaterialTheme.colorScheme.secondary,
|
containerColor = MaterialTheme.colorScheme.secondary,
|
||||||
shape = RoundedCornerShape(16.dp),
|
shape = RoundedCornerShape(16.dp),
|
||||||
|
@ -133,6 +148,36 @@ fun MainScreen(viewModel: MainViewModel = hiltViewModel(), padding : PaddingValu
|
||||||
Text(text = stringResource(R.string.no_tunnels), fontStyle = FontStyle.Italic)
|
Text(text = stringResource(R.string.no_tunnels), fontStyle = FontStyle.Italic)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (showBottomSheet) {
|
||||||
|
ModalBottomSheet(
|
||||||
|
onDismissRequest = {
|
||||||
|
showBottomSheet = false
|
||||||
|
},
|
||||||
|
sheetState = sheetState
|
||||||
|
) {
|
||||||
|
// Sheet content
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth().clickable {
|
||||||
|
showBottomSheet = false
|
||||||
|
pickFileLauncher.launch("*/*")
|
||||||
|
}.padding(10.dp)
|
||||||
|
) {
|
||||||
|
Icon(Icons.Filled.FileOpen, contentDescription = "File Open", modifier = Modifier.padding(10.dp))
|
||||||
|
Text("Add tunnel from files", modifier = Modifier.padding(10.dp))
|
||||||
|
}
|
||||||
|
Divider()
|
||||||
|
Row(modifier = Modifier.fillMaxWidth().clickable {
|
||||||
|
scope.launch {
|
||||||
|
showBottomSheet = false
|
||||||
|
viewModel.onTunnelQRSelected()
|
||||||
|
}
|
||||||
|
}.padding(10.dp)
|
||||||
|
) {
|
||||||
|
Icon(Icons.Filled.QrCode, contentDescription = "QR Scan", modifier = Modifier.padding(10.dp))
|
||||||
|
Text("Add tunnel from QR code", modifier = Modifier.padding(10.dp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Column(
|
Column(
|
||||||
horizontalAlignment = Alignment.Start,
|
horizontalAlignment = Alignment.Start,
|
||||||
verticalArrangement = Arrangement.Top,
|
verticalArrangement = Arrangement.Top,
|
||||||
|
@ -181,7 +226,11 @@ fun MainScreen(viewModel: MainViewModel = hiltViewModel(), padding : PaddingValu
|
||||||
}, confirmButton = {
|
}, confirmButton = {
|
||||||
Button(onClick = {
|
Button(onClick = {
|
||||||
if (tunnels.any { it.name == selectedTunnel?.name }) {
|
if (tunnels.any { it.name == selectedTunnel?.name }) {
|
||||||
Toast.makeText(context, context.resources.getString(R.string.tunnel_exists), Toast.LENGTH_LONG)
|
Toast.makeText(
|
||||||
|
context,
|
||||||
|
context.resources.getString(R.string.tunnel_exists),
|
||||||
|
Toast.LENGTH_LONG
|
||||||
|
)
|
||||||
.show()
|
.show()
|
||||||
return@Button
|
return@Button
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import androidx.lifecycle.viewModelScope
|
||||||
import com.wireguard.config.Config
|
import com.wireguard.config.Config
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
import com.zaneschepke.wireguardautotunnel.R
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.Repository
|
import com.zaneschepke.wireguardautotunnel.repository.Repository
|
||||||
|
import com.zaneschepke.wireguardautotunnel.service.barcode.CodeScanner
|
||||||
import com.zaneschepke.wireguardautotunnel.service.foreground.Action
|
import com.zaneschepke.wireguardautotunnel.service.foreground.Action
|
||||||
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceState
|
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceState
|
||||||
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceTracker
|
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceTracker
|
||||||
|
@ -32,7 +33,8 @@ import javax.inject.Inject
|
||||||
class MainViewModel @Inject constructor(private val application : Application,
|
class MainViewModel @Inject constructor(private val application : Application,
|
||||||
private val tunnelRepo : Repository<TunnelConfig>,
|
private val tunnelRepo : Repository<TunnelConfig>,
|
||||||
private val settingsRepo : Repository<Settings>,
|
private val settingsRepo : Repository<Settings>,
|
||||||
private val vpnService: VpnService
|
private val vpnService: VpnService,
|
||||||
|
private val codeScanner: CodeScanner
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val _viewState = MutableStateFlow(ViewState())
|
private val _viewState = MutableStateFlow(ViewState())
|
||||||
|
@ -43,7 +45,9 @@ class MainViewModel @Inject constructor(private val application : Application,
|
||||||
private val _settings = MutableStateFlow(Settings())
|
private val _settings = MutableStateFlow(Settings())
|
||||||
val settings get() = _settings.asStateFlow()
|
val settings get() = _settings.asStateFlow()
|
||||||
|
|
||||||
private val defaultConfigName = "tunnel${(Math.random() * 1000).toInt()}"
|
private val defaultConfigName = {
|
||||||
|
"tunnel${(Math.random() * 100000).toInt()}"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
@ -111,6 +115,15 @@ class MainViewModel @Inject constructor(private val application : Application,
|
||||||
ServiceTracker.actionOnService( Action.STOP, application, WireGuardTunnelService::class.java)
|
ServiceTracker.actionOnService( Action.STOP, application, WireGuardTunnelService::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun onTunnelQRSelected() {
|
||||||
|
codeScanner.scan().collect {
|
||||||
|
Timber.d(it)
|
||||||
|
if(!it.isNullOrEmpty()) {
|
||||||
|
tunnelRepo.save(TunnelConfig(name = defaultConfigName(), wgQuick = it))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun onTunnelFileSelected(uri : Uri) {
|
fun onTunnelFileSelected(uri : Uri) {
|
||||||
val fileName = getFileName(application.applicationContext, uri)
|
val fileName = getFileName(application.applicationContext, uri)
|
||||||
val extension = getFileExtensionFromFileName(fileName)
|
val extension = getFileExtensionFromFileName(fileName)
|
||||||
|
@ -135,14 +148,14 @@ class MainViewModel @Inject constructor(private val application : Application,
|
||||||
private fun getFileName(context: Context, uri: Uri): String {
|
private fun getFileName(context: Context, uri: Uri): String {
|
||||||
if (uri.scheme == "content") {
|
if (uri.scheme == "content") {
|
||||||
val cursor = context.contentResolver.query(uri, null, null, null, null)
|
val cursor = context.contentResolver.query(uri, null, null, null, null)
|
||||||
cursor ?: return defaultConfigName
|
cursor ?: return defaultConfigName()
|
||||||
cursor.use {
|
cursor.use {
|
||||||
if(cursor.moveToFirst()) {
|
if(cursor.moveToFirst()) {
|
||||||
return cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME))
|
return cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return defaultConfigName
|
return defaultConfigName()
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun showSnackBarMessage(message : String) {
|
suspend fun showSnackBarMessage(message : String) {
|
||||||
|
|
|
@ -4,6 +4,7 @@ buildscript {
|
||||||
val objectBoxVersion by extra("3.5.1")
|
val objectBoxVersion by extra("3.5.1")
|
||||||
val hiltVersion by extra("2.44")
|
val hiltVersion by extra("2.44")
|
||||||
val accompanistVersion by extra("0.31.2-alpha")
|
val accompanistVersion by extra("0.31.2-alpha")
|
||||||
|
val cameraVersion by extra("1.3.0-beta01")
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath("io.objectbox:objectbox-gradle-plugin:$objectBoxVersion")
|
classpath("io.objectbox:objectbox-gradle-plugin:$objectBoxVersion")
|
||||||
|
|
Loading…
Reference in New Issue