diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index b47383b2..6ce5264a 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -17,7 +17,7 @@ android {
val versionMajor = 2
val versionMinor = 3
- val versionPatch = 5
+ val versionPatch = 6
val versionBuild = 0
defaultConfig {
@@ -89,7 +89,7 @@ dependencies {
implementation("com.jakewharton.timber:timber:5.0.1")
// compose navigation
- implementation("androidx.navigation:navigation-compose:2.7.0")
+ implementation("androidx.navigation:navigation-compose:2.7.1")
implementation("androidx.hilt:hilt-navigation-compose:1.0.0")
// hilt
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 951bb88f..716f9878 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -64,6 +64,20 @@
android:foregroundServiceType="remoteMessaging"
android:exported="false">
+
+
+
+
+
+
+
+
+ @Inject
+ lateinit var configRepo : Repository
+
+ @Inject
+ lateinit var vpnService : VpnService
+
+ private val scope = CoroutineScope(Dispatchers.Main);
+
+ private lateinit var job : Job
+
+ override fun onStartListening() {
+ if (!this::job.isInitialized) {
+ job = scope.launch {
+ updateTileState()
+ }
+ }
+ Timber.d("On start listening")
+ super.onStartListening()
+ }
+
+ override fun onTileAdded() {
+ super.onTileAdded()
+ qsTile.contentDescription = "Toggle VPN"
+ scope.launch {
+ updateTileState();
+ }
+ }
+
+ override fun onTileRemoved() {
+ super.onTileRemoved()
+ cancelJob()
+ }
+
+ override fun onClick() {
+ unlockAndRun {
+ scope.launch {
+ try {
+ if(vpnService.getState() == Tunnel.State.UP) {
+ stopTunnel();
+ return@launch
+ }
+ val settings = settingsRepo.getAll()
+ if (!settings.isNullOrEmpty()) {
+ val setting = settings.first()
+ if (setting.defaultTunnel != null) {
+ startTunnel(setting.defaultTunnel!!)
+ } else {
+ val config = configRepo.getAll()?.first();
+ if(config != null) {
+ startTunnel(config.toString());
+ }
+ }
+ }
+ } finally {
+ cancel()
+ }
+ }
+ super.onClick()
+ }
+ }
+
+ private fun stopTunnel() {
+ ServiceTracker.actionOnService(
+ Action.STOP, this@TunnelControlTile,
+ WireGuardConnectivityWatcherService::class.java)
+ ServiceTracker.actionOnService(
+ Action.STOP, this@TunnelControlTile,
+ WireGuardTunnelService::class.java)
+ }
+
+ private fun startTunnel(tunnelConfig : String) {
+ ServiceTracker.actionOnService(
+ Action.START, this.applicationContext,
+ WireGuardTunnelService::class.java,
+ mapOf(this.applicationContext.resources.
+ getString(R.string.tunnel_extras_key) to
+ tunnelConfig))
+ }
+
+ private suspend fun updateTileState() {
+ vpnService.state.collect {
+ when(it) {
+ Tunnel.State.UP -> {
+ setTileOn()
+ }
+ Tunnel.State.DOWN -> {
+ setTileOff()
+ }
+ else -> {
+ qsTile.state = Tile.STATE_UNAVAILABLE
+ }
+ }
+ qsTile.updateTile()
+ }
+ }
+
+ private fun setTileOff() {
+ qsTile.state = Tile.STATE_INACTIVE;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ qsTile.subtitle = "Off"
+ }
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ qsTile.stateDescription = "VPN Off";
+ }
+ }
+
+ private fun setTileOn() {
+ qsTile.state = Tile.STATE_ACTIVE;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ qsTile.subtitle = "On"
+ }
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ qsTile.stateDescription = "VPN On";
+ }
+ }
+ private fun cancelJob() {
+ if(this::job.isInitialized) {
+ job.cancel();
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/MainActivity.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/MainActivity.kt
index c25dc78f..4e5758af 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/MainActivity.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/MainActivity.kt
@@ -152,7 +152,7 @@ class MainActivity : AppCompatActivity() {
}
}
}) {
- MainScreen(padding = padding, snackbarHostState = snackbarHostState, navController = navController, focusRequester = focusRequester)
+ MainScreen(padding = padding, snackbarHostState = snackbarHostState, navController = navController)
}
composable(Routes.Settings.name, enterTransition = {
when (initialState.destination.route) {
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/MainScreen.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/MainScreen.kt
index 2cb3e0ad..162c4975 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/MainScreen.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/MainScreen.kt
@@ -18,6 +18,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.FileOpen
@@ -85,7 +86,6 @@ import kotlinx.coroutines.launch
@Composable
fun MainScreen(
viewModel: MainViewModel = hiltViewModel(), padding: PaddingValues,
- focusRequester: FocusRequester,
snackbarHostState: SnackbarHostState, navController: NavController
) {
@@ -149,7 +149,7 @@ fun MainScreen(
})
},
floatingActionButtonPosition = FabPosition.End,
- floatingActionButton = {
+ floatingActionButton = {
AnimatedVisibility(
visible = isVisible.value,
enter = slideInVertically(initialOffsetY = { it * 2 }),
@@ -241,9 +241,12 @@ fun MainScreen(
.padding(padding)
) {
- LazyColumn(modifier = Modifier.fillMaxSize()
- .nestedScroll(nestedScrollConnection),) {
- items(tunnels.toList()) { tunnel ->
+ LazyColumn(
+ modifier = Modifier.fillMaxSize()
+ .nestedScroll(nestedScrollConnection),
+ ) {
+ itemsIndexed(tunnels.toList()) { index, tunnel ->
+ val focusRequester = FocusRequester();
RowListItem(leadingIcon = Icons.Rounded.Circle,
leadingIconColor = if (tunnelName == tunnel.name) when (handshakeStatus) {
HandshakeStatus.HEALTHY -> mint
@@ -263,15 +266,15 @@ fun MainScreen(
selectedTunnel = tunnel;
},
onClick = {
- if(!WireGuardAutoTunnel.isRunningOnAndroidTv(context)){
+ if (!WireGuardAutoTunnel.isRunningOnAndroidTv(context)) {
navController.navigate("${Routes.Detail.name}/${tunnel.id}")
} else {
focusRequester.requestFocus()
}
- },
+ },
rowButton = {
if (tunnel.id == selectedTunnel?.id) {
- Row() {
+ Row {
IconButton(onClick = {
navController.navigate("${Routes.Config.name}/${selectedTunnel?.id}")
}) {
@@ -287,30 +290,43 @@ fun MainScreen(
}
}
} else {
- if(WireGuardAutoTunnel.isRunningOnAndroidTv(context)){
- Row() {
- IconButton(modifier = Modifier.focusRequester(focusRequester),onClick = {
- navController.navigate("${Routes.Detail.name}/${tunnel.id}")
- }) {
+ if (WireGuardAutoTunnel.isRunningOnAndroidTv(context)) {
+ Row {
+ IconButton(
+ modifier = Modifier.focusRequester(focusRequester),
+ onClick = {
+ navController.navigate("${Routes.Detail.name}/${tunnel.id}")
+ }) {
Icon(Icons.Rounded.Info, "Info")
}
IconButton(onClick = {
if (state == Tunnel.State.UP && tunnel.name == tunnelName)
scope.launch {
- viewModel.showSnackBarMessage(context.resources.getString(R.string.turn_off_tunnel))
+ viewModel.showSnackBarMessage(
+ context.resources.getString(
+ R.string.turn_off_tunnel
+ )
+ )
} else {
- navController.navigate("${Routes.Config.name}/${tunnel.id}")
- }
+ navController.navigate("${Routes.Config.name}/${tunnel.id}")
+ }
}) {
- Icon(Icons.Rounded.Edit, stringResource(id = R.string.edit))
+ Icon(
+ Icons.Rounded.Edit,
+ stringResource(id = R.string.edit)
+ )
}
IconButton(onClick = {
if (state == Tunnel.State.UP && tunnel.name == tunnelName)
scope.launch {
- viewModel.showSnackBarMessage(context.resources.getString(R.string.turn_off_tunnel))
+ viewModel.showSnackBarMessage(
+ context.resources.getString(
+ R.string.turn_off_tunnel
+ )
+ )
} else {
- viewModel.onDelete(tunnel)
- }
+ viewModel.onDelete(tunnel)
+ }
}) {
Icon(
Icons.Rounded.Delete,
diff --git a/app/src/main/res/drawable/shield.xml b/app/src/main/res/drawable/shield.xml
new file mode 100644
index 00000000..e49a1f26
--- /dev/null
+++ b/app/src/main/res/drawable/shield.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/build.gradle.kts b/build.gradle.kts
index 1acbd182..f9d7e05a 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -13,7 +13,7 @@ buildscript {
}
plugins {
- id("com.android.application") version "8.2.0-alpha15" apply false
+ id("com.android.application") version "8.2.0-beta01" apply false
id("org.jetbrains.kotlin.android") version "1.8.22" apply false
id("com.google.dagger.hilt.android") version "2.44" apply false
kotlin("plugin.serialization") version "1.8.22" apply false