mirror of
https://github.com/wgtunnel/android.git
synced 2026-07-03 14:07:49 +02:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7ca5de1836 | |||
| 509d22a98c | |||
| 68b0902398 |
@@ -5,25 +5,25 @@ plugins {
|
|||||||
id("org.jetbrains.kotlin.android")
|
id("org.jetbrains.kotlin.android")
|
||||||
kotlin("kapt")
|
kotlin("kapt")
|
||||||
id("com.google.dagger.hilt.android")
|
id("com.google.dagger.hilt.android")
|
||||||
id("io.objectbox")
|
|
||||||
id("com.google.gms.google-services")
|
id("com.google.gms.google-services")
|
||||||
id("com.google.firebase.crashlytics")
|
id("com.google.firebase.crashlytics")
|
||||||
id("org.jetbrains.kotlin.plugin.serialization")
|
id("org.jetbrains.kotlin.plugin.serialization")
|
||||||
|
id("io.objectbox")
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "com.zaneschepke.wireguardautotunnel"
|
namespace = "com.zaneschepke.wireguardautotunnel"
|
||||||
compileSdk = 33
|
compileSdk = 34
|
||||||
|
|
||||||
val versionMajor = 2
|
val versionMajor = 2
|
||||||
val versionMinor = 1
|
val versionMinor = 1
|
||||||
val versionPatch = 1
|
val versionPatch = 3
|
||||||
val versionBuild = 0
|
val versionBuild = 0
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "com.zaneschepke.wireguardautotunnel"
|
applicationId = "com.zaneschepke.wireguardautotunnel"
|
||||||
minSdk = 29
|
minSdk = 29
|
||||||
targetSdk = 33
|
targetSdk = 34
|
||||||
versionCode = versionMajor * 10000 + versionMinor * 1000 + versionPatch * 100 + versionBuild
|
versionCode = versionMajor * 10000 + versionMinor * 1000 + versionPatch * 100 + versionBuild
|
||||||
versionName = "${versionMajor}.${versionMinor}.${versionPatch}"
|
versionName = "${versionMajor}.${versionMinor}.${versionPatch}"
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
<?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" />
|
||||||
@@ -70,5 +69,8 @@
|
|||||||
<meta-data
|
<meta-data
|
||||||
android:name="com.google.mlkit.vision.DEPENDENCIES"
|
android:name="com.google.mlkit.vision.DEPENDENCIES"
|
||||||
android:value="barcode_ui"/>
|
android:value="barcode_ui"/>
|
||||||
|
<meta-data
|
||||||
|
android:name="firebase_crashlytics_collection_enabled"
|
||||||
|
android:value="true" />
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel
|
package com.zaneschepke.wireguardautotunnel
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
|
import com.google.android.datatransport.BuildConfig
|
||||||
|
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||||
import com.zaneschepke.wireguardautotunnel.repository.Repository
|
import com.zaneschepke.wireguardautotunnel.repository.Repository
|
||||||
import com.zaneschepke.wireguardautotunnel.service.tunnel.model.Settings
|
import com.zaneschepke.wireguardautotunnel.service.tunnel.model.Settings
|
||||||
import dagger.hilt.android.HiltAndroidApp
|
import dagger.hilt.android.HiltAndroidApp
|
||||||
@@ -15,7 +17,10 @@ class WireGuardAutoTunnel : Application() {
|
|||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
if(BuildConfig.DEBUG) Timber.plant(Timber.DebugTree())
|
if(BuildConfig.DEBUG) {
|
||||||
|
FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(false);
|
||||||
|
Timber.plant(Timber.DebugTree())
|
||||||
|
}
|
||||||
settingsRepo.init()
|
settingsRepo.init()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+129
-96
@@ -3,10 +3,8 @@ package com.zaneschepke.wireguardautotunnel.ui.screens.config
|
|||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
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.fillMaxHeight
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
@@ -72,127 +70,162 @@ fun ConfigScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(tunnel != null) {
|
if(tunnel != null) {
|
||||||
Column(
|
LazyColumn(
|
||||||
horizontalAlignment = Alignment.Start,
|
horizontalAlignment = Alignment.Start,
|
||||||
verticalArrangement = Arrangement.Top,
|
verticalArrangement = Arrangement.Top,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.padding(padding)
|
.padding(padding)
|
||||||
) {
|
) {
|
||||||
Row(
|
item {
|
||||||
modifier = Modifier
|
Row(
|
||||||
.fillMaxWidth()
|
modifier = Modifier
|
||||||
.padding(horizontal = 20.dp, vertical = 7.dp),
|
.fillMaxWidth()
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
.padding(horizontal = 20.dp, vertical = 7.dp),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween
|
|
||||||
) {
|
|
||||||
OutlinedTextField(
|
|
||||||
value = tunnelName.value,
|
|
||||||
onValueChange = {
|
|
||||||
viewModel.onTunnelNameChange(it)
|
|
||||||
},
|
|
||||||
label = { Text(stringResource(id = R.string.tunnel_name)) },
|
|
||||||
maxLines = 1,
|
|
||||||
keyboardOptions = KeyboardOptions(
|
|
||||||
capitalization = KeyboardCapitalization.None,
|
|
||||||
imeAction = ImeAction.Done
|
|
||||||
),
|
|
||||||
keyboardActions = KeyboardActions(
|
|
||||||
onDone = {
|
|
||||||
focusManager.clearFocus()
|
|
||||||
keyboardController?.hide()
|
|
||||||
viewModel.onTunnelNameChange(tunnelName.value)
|
|
||||||
}
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Row(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(horizontal = 20.dp, vertical = 7.dp),
|
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
|
||||||
horizontalArrangement = Arrangement.SpaceBetween
|
|
||||||
) {
|
|
||||||
Text(stringResource(id = R.string.tunnel_all))
|
|
||||||
Switch(
|
|
||||||
checked = allApplications,
|
|
||||||
onCheckedChange = {
|
|
||||||
viewModel.onAllApplicationsChange(!allApplications)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if(!allApplications) {
|
|
||||||
Row(modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(horizontal = 20.dp, vertical = 7.dp),
|
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
horizontalArrangement = Arrangement.SpaceBetween) {
|
horizontalArrangement = Arrangement.SpaceBetween
|
||||||
Row(verticalAlignment = Alignment.CenterVertically,
|
) {
|
||||||
horizontalArrangement = Arrangement.SpaceBetween){
|
OutlinedTextField(
|
||||||
Text(stringResource(id = R.string.include))
|
value = tunnelName.value,
|
||||||
Checkbox(
|
onValueChange = {
|
||||||
checked = include,
|
viewModel.onTunnelNameChange(it)
|
||||||
onCheckedChange = {
|
},
|
||||||
viewModel.onIncludeChange(!include)
|
label = { Text(stringResource(id = R.string.tunnel_name)) },
|
||||||
|
maxLines = 1,
|
||||||
|
keyboardOptions = KeyboardOptions(
|
||||||
|
capitalization = KeyboardCapitalization.None,
|
||||||
|
imeAction = ImeAction.Done
|
||||||
|
),
|
||||||
|
keyboardActions = KeyboardActions(
|
||||||
|
onDone = {
|
||||||
|
focusManager.clearFocus()
|
||||||
|
keyboardController?.hide()
|
||||||
|
viewModel.onTunnelNameChange(tunnelName.value)
|
||||||
}
|
}
|
||||||
)
|
),
|
||||||
}
|
)
|
||||||
Row(verticalAlignment = Alignment.CenterVertically,
|
|
||||||
horizontalArrangement = Arrangement.SpaceBetween){
|
|
||||||
Text(stringResource(id = R.string.exclude))
|
|
||||||
Checkbox(
|
|
||||||
checked = !include,
|
|
||||||
onCheckedChange = {
|
|
||||||
viewModel.onIncludeChange(!include)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
LazyColumn(modifier = Modifier
|
}
|
||||||
.fillMaxWidth()
|
item {
|
||||||
.fillMaxHeight(.75f)
|
Row(
|
||||||
.padding(horizontal = 14.dp, vertical = 7.dp),
|
modifier = Modifier
|
||||||
verticalArrangement = Arrangement.Center,
|
.fillMaxWidth()
|
||||||
horizontalAlignment = Alignment.Start) {
|
.padding(horizontal = 20.dp, vertical = 7.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween
|
||||||
|
) {
|
||||||
|
Text(stringResource(id = R.string.tunnel_all))
|
||||||
|
Switch(
|
||||||
|
checked = allApplications,
|
||||||
|
onCheckedChange = {
|
||||||
|
viewModel.onAllApplicationsChange(!allApplications)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!allApplications) {
|
||||||
|
item {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 20.dp, vertical = 7.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween
|
||||||
|
) {
|
||||||
|
Text(stringResource(id = R.string.include))
|
||||||
|
Checkbox(
|
||||||
|
checked = include,
|
||||||
|
onCheckedChange = {
|
||||||
|
viewModel.onIncludeChange(!include)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween
|
||||||
|
) {
|
||||||
|
Text(stringResource(id = R.string.exclude))
|
||||||
|
Checkbox(
|
||||||
|
checked = !include,
|
||||||
|
onCheckedChange = {
|
||||||
|
viewModel.onIncludeChange(!include)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// LazyColumn(
|
||||||
|
// modifier = Modifier
|
||||||
|
// .fillMaxWidth()
|
||||||
|
// .fillMaxHeight(.75f)
|
||||||
|
// .padding(horizontal = 14.dp, vertical = 7.dp),
|
||||||
|
// verticalArrangement = Arrangement.Center,
|
||||||
|
// horizontalAlignment = Alignment.Start
|
||||||
|
// ) {
|
||||||
items(packages) { pack ->
|
items(packages) { pack ->
|
||||||
Row(verticalAlignment = Alignment.CenterVertically,
|
Row(
|
||||||
horizontalArrangement = Arrangement.SpaceBetween) {
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween
|
||||||
|
) {
|
||||||
Row(
|
Row(
|
||||||
horizontalArrangement = Arrangement.Center,
|
horizontalArrangement = Arrangement.Center,
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier.padding(5.dp)
|
modifier = Modifier.padding(5.dp)
|
||||||
) {
|
) {
|
||||||
val drawable = pack.applicationInfo?.loadIcon(context.packageManager)
|
val drawable =
|
||||||
if(drawable != null) {
|
pack.applicationInfo?.loadIcon(context.packageManager)
|
||||||
Image(painter = DrawablePainter(drawable), stringResource(id = R.string.icon), modifier = Modifier.size(50.dp, 50.dp))
|
if (drawable != null) {
|
||||||
|
Image(
|
||||||
|
painter = DrawablePainter(drawable),
|
||||||
|
stringResource(id = R.string.icon),
|
||||||
|
modifier = Modifier.size(50.dp, 50.dp)
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
Icon(Icons.Rounded.Android, stringResource(id = R.string.edit), modifier = Modifier.size(50.dp, 50.dp))
|
Icon(
|
||||||
|
Icons.Rounded.Android,
|
||||||
|
stringResource(id = R.string.edit),
|
||||||
|
modifier = Modifier.size(50.dp, 50.dp)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Text(pack.applicationInfo.loadLabel(context.packageManager).toString(), modifier = Modifier.padding(5.dp))
|
Text(
|
||||||
|
pack.applicationInfo.loadLabel(context.packageManager)
|
||||||
|
.toString(), modifier = Modifier.padding(5.dp)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Checkbox(
|
Checkbox(
|
||||||
checked = (checkedPackages.contains(pack.packageName)),
|
checked = (checkedPackages.contains(pack.packageName)),
|
||||||
onCheckedChange = {
|
onCheckedChange = {
|
||||||
if(it) viewModel.onAddCheckedPackage(pack.packageName) else viewModel.onRemoveCheckedPackage(pack.packageName)
|
if (it) viewModel.onAddCheckedPackage(pack.packageName) else viewModel.onRemoveCheckedPackage(
|
||||||
|
pack.packageName
|
||||||
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
item {
|
||||||
Row(
|
Row(
|
||||||
horizontalArrangement = Arrangement.Center,
|
horizontalArrangement = Arrangement.Center,
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
) {
|
) {
|
||||||
Button(onClick = {
|
Button(onClick = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
viewModel.onSaveAllChanges()
|
viewModel.onSaveAllChanges()
|
||||||
Toast.makeText(context, context.resources.getString(R.string.config_changes_saved), Toast.LENGTH_LONG).show()
|
Toast.makeText(
|
||||||
navController.navigate(Routes.Main.name)
|
context,
|
||||||
|
context.resources.getString(R.string.config_changes_saved),
|
||||||
|
Toast.LENGTH_LONG
|
||||||
|
).show()
|
||||||
|
navController.navigate(Routes.Main.name)
|
||||||
|
}
|
||||||
|
}, Modifier.padding(25.dp)) {
|
||||||
|
Text(stringResource(id = R.string.save_changes))
|
||||||
}
|
}
|
||||||
}, Modifier.padding(25.dp)) {
|
|
||||||
Text(stringResource(id = R.string.save_changes))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-2
@@ -9,6 +9,8 @@ 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.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
@@ -43,6 +45,7 @@ fun DetailScreen(
|
|||||||
val tunnelName by viewModel.tunnelName.collectAsStateWithLifecycle()
|
val tunnelName by viewModel.tunnelName.collectAsStateWithLifecycle()
|
||||||
val lastHandshake by viewModel.lastHandshake.collectAsStateWithLifecycle(emptyMap())
|
val lastHandshake by viewModel.lastHandshake.collectAsStateWithLifecycle(emptyMap())
|
||||||
|
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
viewModel.getTunnelById(id)
|
viewModel.getTunnelById(id)
|
||||||
}
|
}
|
||||||
@@ -51,12 +54,14 @@ fun DetailScreen(
|
|||||||
val interfaceKey = tunnel?.`interface`?.keyPair?.publicKey?.toBase64().toString()
|
val interfaceKey = tunnel?.`interface`?.keyPair?.publicKey?.toBase64().toString()
|
||||||
val addresses = tunnel?.`interface`?.addresses!!.joinToString()
|
val addresses = tunnel?.`interface`?.addresses!!.joinToString()
|
||||||
val dnsServers = tunnel?.`interface`?.dnsServers!!.joinToString()
|
val dnsServers = tunnel?.`interface`?.dnsServers!!.joinToString()
|
||||||
val mtu = tunnel?.`interface`?.mtu?.get().toString()
|
val optionalMtu = tunnel?.`interface`?.mtu
|
||||||
|
val mtu = if(optionalMtu?.isPresent == true) optionalMtu.get().toString() else "None"
|
||||||
Column(
|
Column(
|
||||||
horizontalAlignment = Alignment.Start,
|
horizontalAlignment = Alignment.Start,
|
||||||
verticalArrangement = Arrangement.Top,
|
verticalArrangement = Arrangement.Top,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
|
.verticalScroll(rememberScrollState())
|
||||||
.padding(padding)
|
.padding(padding)
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
@@ -92,7 +97,7 @@ fun DetailScreen(
|
|||||||
tunnel?.peers?.forEach{
|
tunnel?.peers?.forEach{
|
||||||
val peerKey = it.publicKey.toBase64().toString()
|
val peerKey = it.publicKey.toBase64().toString()
|
||||||
val allowedIps = it.allowedIps.joinToString()
|
val allowedIps = it.allowedIps.joinToString()
|
||||||
val endpoint = it.endpoint.get().toString()
|
val endpoint = if(it.endpoint.isPresent) it.endpoint.get().toString() else "None"
|
||||||
Text(stringResource(R.string.peer), fontWeight = FontWeight.Bold, fontSize = 20.sp)
|
Text(stringResource(R.string.peer), fontWeight = FontWeight.Bold, fontSize = 20.sp)
|
||||||
Text(stringResource(R.string.public_key), fontStyle = FontStyle.Italic)
|
Text(stringResource(R.string.public_key), fontStyle = FontStyle.Italic)
|
||||||
Text(text = peerKey, modifier = Modifier.clickable {
|
Text(text = peerKey, modifier = Modifier.clickable {
|
||||||
|
|||||||
+47
-15
@@ -3,6 +3,9 @@ package com.zaneschepke.wireguardautotunnel.ui.screens.main
|
|||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
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.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.slideInVertically
|
||||||
|
import androidx.compose.animation.slideOutVertically
|
||||||
import androidx.compose.foundation.clickable
|
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
|
||||||
@@ -43,11 +46,16 @@ import androidx.compose.runtime.getValue
|
|||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.geometry.Offset
|
||||||
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.nestedscroll.NestedScrollConnection
|
||||||
|
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
|
||||||
|
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.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||||
@@ -65,7 +73,6 @@ import com.zaneschepke.wireguardautotunnel.ui.Routes
|
|||||||
import com.zaneschepke.wireguardautotunnel.ui.common.RowListItem
|
import com.zaneschepke.wireguardautotunnel.ui.common.RowListItem
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.theme.brickRed
|
import com.zaneschepke.wireguardautotunnel.ui.theme.brickRed
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.theme.mint
|
import com.zaneschepke.wireguardautotunnel.ui.theme.mint
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.theme.pinkRed
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
|
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
|
||||||
@@ -78,6 +85,7 @@ fun MainScreen(
|
|||||||
|
|
||||||
val haptic = LocalHapticFeedback.current
|
val haptic = LocalHapticFeedback.current
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
val isVisible = rememberSaveable { mutableStateOf(true) }
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
val sheetState = rememberModalBottomSheetState()
|
val sheetState = rememberModalBottomSheetState()
|
||||||
@@ -89,6 +97,23 @@ fun MainScreen(
|
|||||||
val state by viewModel.state.collectAsStateWithLifecycle(Tunnel.State.DOWN)
|
val state by viewModel.state.collectAsStateWithLifecycle(Tunnel.State.DOWN)
|
||||||
val tunnelName by viewModel.tunnelName.collectAsStateWithLifecycle("")
|
val tunnelName by viewModel.tunnelName.collectAsStateWithLifecycle("")
|
||||||
|
|
||||||
|
// Nested scroll for control FAB
|
||||||
|
val nestedScrollConnection = remember {
|
||||||
|
object : NestedScrollConnection {
|
||||||
|
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
|
||||||
|
// Hide FAB
|
||||||
|
if (available.y < -1) {
|
||||||
|
isVisible.value = false
|
||||||
|
}
|
||||||
|
// Show FAB
|
||||||
|
if (available.y > 1) {
|
||||||
|
isVisible.value = true
|
||||||
|
}
|
||||||
|
return Offset.Zero
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LaunchedEffect(viewState.value) {
|
LaunchedEffect(viewState.value) {
|
||||||
if (viewState.value.showSnackbarMessage) {
|
if (viewState.value.showSnackbarMessage) {
|
||||||
val result = snackbarHostState.showSnackbar(
|
val result = snackbarHostState.showSnackbar(
|
||||||
@@ -118,20 +143,26 @@ fun MainScreen(
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
floatingActionButtonPosition = FabPosition.End,
|
floatingActionButtonPosition = FabPosition.End,
|
||||||
floatingActionButton = {
|
floatingActionButton = {
|
||||||
FloatingActionButton(
|
AnimatedVisibility(
|
||||||
modifier = Modifier.padding(bottom = 90.dp),
|
visible = isVisible.value,
|
||||||
onClick = {
|
enter = slideInVertically(initialOffsetY = { it * 2 }),
|
||||||
showBottomSheet = true
|
exit = slideOutVertically(targetOffsetY = { it * 2 }),
|
||||||
},
|
|
||||||
containerColor = MaterialTheme.colorScheme.secondary,
|
|
||||||
shape = RoundedCornerShape(16.dp),
|
|
||||||
) {
|
) {
|
||||||
Icon(
|
FloatingActionButton(
|
||||||
imageVector = Icons.Rounded.Add,
|
modifier = Modifier.padding(bottom = 90.dp),
|
||||||
contentDescription = stringResource(id = R.string.add_tunnel),
|
onClick = {
|
||||||
tint = Color.DarkGray,
|
showBottomSheet = true
|
||||||
)
|
},
|
||||||
|
containerColor = MaterialTheme.colorScheme.secondary,
|
||||||
|
shape = RoundedCornerShape(16.dp),
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Rounded.Add,
|
||||||
|
contentDescription = stringResource(id = R.string.add_tunnel),
|
||||||
|
tint = Color.DarkGray,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
@@ -204,7 +235,8 @@ fun MainScreen(
|
|||||||
.padding(padding)
|
.padding(padding)
|
||||||
) {
|
) {
|
||||||
|
|
||||||
LazyColumn(modifier = Modifier.fillMaxSize()) {
|
LazyColumn(modifier = Modifier.fillMaxSize()
|
||||||
|
.nestedScroll(nestedScrollConnection),) {
|
||||||
items(tunnels.toList()) { tunnel ->
|
items(tunnels.toList()) { tunnel ->
|
||||||
RowListItem(leadingIcon = Icons.Rounded.Circle,
|
RowListItem(leadingIcon = Icons.Rounded.Circle,
|
||||||
leadingIconColor = when (handshakeStatus) {
|
leadingIconColor = when (handshakeStatus) {
|
||||||
|
|||||||
+5
@@ -16,8 +16,10 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||||||
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.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.text.KeyboardActions
|
import androidx.compose.foundation.text.KeyboardActions
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Close
|
import androidx.compose.material.icons.filled.Close
|
||||||
import androidx.compose.material.icons.outlined.Add
|
import androidx.compose.material.icons.outlined.Add
|
||||||
@@ -91,6 +93,7 @@ fun SettingsScreen(
|
|||||||
val backgroundLocationState =
|
val backgroundLocationState =
|
||||||
rememberPermissionState(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
|
rememberPermissionState(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
|
||||||
var currentText by remember { mutableStateOf("") }
|
var currentText by remember { mutableStateOf("") }
|
||||||
|
val scrollState = rememberScrollState()
|
||||||
|
|
||||||
LaunchedEffect(viewState) {
|
LaunchedEffect(viewState) {
|
||||||
if (viewState.showSnackbarMessage) {
|
if (viewState.showSnackbarMessage) {
|
||||||
@@ -120,6 +123,7 @@ fun SettingsScreen(
|
|||||||
verticalArrangement = Arrangement.Top,
|
verticalArrangement = Arrangement.Top,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
|
.verticalScroll(scrollState)
|
||||||
.padding(padding)) {
|
.padding(padding)) {
|
||||||
Icon(Icons.Rounded.LocationOff, contentDescription = stringResource(id = R.string.map), modifier = Modifier
|
Icon(Icons.Rounded.LocationOff, contentDescription = stringResource(id = R.string.map), modifier = Modifier
|
||||||
.padding(30.dp)
|
.padding(30.dp)
|
||||||
@@ -178,6 +182,7 @@ fun SettingsScreen(
|
|||||||
verticalArrangement = Arrangement.Top,
|
verticalArrangement = Arrangement.Top,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
|
.verticalScroll(scrollState)
|
||||||
.clickable(indication = null, interactionSource = interactionSource) {
|
.clickable(indication = null, interactionSource = interactionSource) {
|
||||||
focusManager.clearFocus()
|
focusManager.clearFocus()
|
||||||
}
|
}
|
||||||
|
|||||||
+3
@@ -11,6 +11,8 @@ import androidx.compose.foundation.layout.Spacer
|
|||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
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.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
@@ -43,6 +45,7 @@ fun SupportScreen(padding : PaddingValues) {
|
|||||||
verticalArrangement = Arrangement.Top,
|
verticalArrangement = Arrangement.Top,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
|
.verticalScroll(rememberScrollState())
|
||||||
.padding(padding)) {
|
.padding(padding)) {
|
||||||
Text(stringResource(R.string.support_text), textAlign = TextAlign.Center, modifier = Modifier.padding(30.dp), fontSize = 15.sp)
|
Text(stringResource(R.string.support_text), textAlign = TextAlign.Center, modifier = Modifier.padding(30.dp), fontSize = 15.sp)
|
||||||
Row(
|
Row(
|
||||||
|
|||||||
Reference in New Issue
Block a user