From 4cdc9747788d47f8442755d5cbebaf4c4c2ac709 Mon Sep 17 00:00:00 2001 From: Zane Schepke Date: Sat, 29 Jul 2023 18:18:24 -0400 Subject: [PATCH] fix: AndroidTV D-pad support and precise location check Allows app be fully navigated via D-pad on AndroidTV (with some quirks) Adds precise location check to setting screen as WiFi-SSID is not readable without this permission. Closes #2 --- app/build.gradle.kts | 5 +- app/src/main/AndroidManifest.xml | 5 +- .../ui/screens/config/ConfigScreen.kt | 20 +++-- .../ui/screens/main/MainScreen.kt | 70 ++++++++++++++-- .../ui/screens/settings/SettingsScreen.kt | 77 +++++++++++++++--- .../ui/screens/support/SupportScreen.kt | 15 +++- .../main/res/mipmap-anydpi-v26/ic_banner.xml | 5 ++ app/src/main/res/mipmap-xhdpi/ic_banner.png | Bin 0 -> 9366 bytes .../res/mipmap-xhdpi/ic_banner_foreground.png | Bin 0 -> 10471 bytes .../main/res/values/ic_banner_background.xml | 4 + app/src/main/res/values/strings.xml | 2 + 11 files changed, 170 insertions(+), 33 deletions(-) create mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_banner.xml create mode 100644 app/src/main/res/mipmap-xhdpi/ic_banner.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_banner_foreground.png create mode 100644 app/src/main/res/values/ic_banner_background.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 842779e..58ad66a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -16,7 +16,7 @@ android { compileSdk = 34 val versionMajor = 2 - val versionMinor = 2 + val versionMinor = 3 val versionPatch = 0 val versionBuild = 0 @@ -123,9 +123,6 @@ dependencies { //barcode scanning implementation("com.google.android.gms:play-services-code-scanner:16.0.0") - - - } kapt { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c10fa56..951bb88 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -28,6 +28,9 @@ + @@ -39,7 +42,7 @@ android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" - android:banner="@mipmap/ic_launcher_foreground" + android:banner="@mipmap/ic_banner" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/config/ConfigScreen.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/config/ConfigScreen.kt index f22727c..7c5c2ad 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/config/ConfigScreen.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/config/ConfigScreen.kt @@ -1,5 +1,6 @@ package com.zaneschepke.wireguardautotunnel.ui.screens.config +import android.content.pm.PackageManager import android.widget.Toast import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement @@ -24,10 +25,13 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.LocalSoftwareKeyboardController @@ -54,6 +58,8 @@ fun ConfigScreen( val context = LocalContext.current val focusManager = LocalFocusManager.current + val focusRequester = remember { FocusRequester() } + val keyboardController = LocalSoftwareKeyboardController.current val scope = rememberCoroutineScope() val tunnel by viewModel.tunnel.collectAsStateWithLifecycle(null) @@ -86,6 +92,7 @@ fun ConfigScreen( horizontalArrangement = Arrangement.SpaceBetween ) { OutlinedTextField( + modifier = Modifier.focusRequester(focusRequester), value = tunnelName.value, onValueChange = { viewModel.onTunnelNameChange(it) @@ -158,14 +165,6 @@ fun ConfigScreen( } } } -// LazyColumn( -// modifier = Modifier -// .fillMaxWidth() -// .fillMaxHeight(.75f) -// .padding(horizontal = 14.dp, vertical = 7.dp), -// verticalArrangement = Arrangement.Center, -// horizontalAlignment = Alignment.Start -// ) { items(packages) { pack -> Row( verticalAlignment = Alignment.CenterVertically, @@ -229,5 +228,10 @@ fun ConfigScreen( } } } + LaunchedEffect(Unit) { + if(context.packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) { + focusRequester.requestFocus() + } + } } } \ No newline at end of file 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 ebdb8d5..a1747bb 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 @@ -1,12 +1,14 @@ package com.zaneschepke.wireguardautotunnel.ui.screens.main import android.annotation.SuppressLint +import android.content.pm.PackageManager import androidx.activity.compose.rememberLauncherForActivityResult 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.focusable import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -25,6 +27,7 @@ import androidx.compose.material.icons.rounded.Add import androidx.compose.material.icons.rounded.Circle import androidx.compose.material.icons.rounded.Delete import androidx.compose.material.icons.rounded.Edit +import androidx.compose.material.icons.rounded.Info import androidx.compose.material3.Divider import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FabPosition @@ -49,7 +52,10 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment +import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.hapticfeedback.HapticFeedbackType @@ -76,7 +82,7 @@ import com.zaneschepke.wireguardautotunnel.ui.theme.mint import kotlinx.coroutines.launch @SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") -@OptIn(ExperimentalMaterial3Api::class) +@OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class) @Composable fun MainScreen( viewModel: MainViewModel = hiltViewModel(), padding: PaddingValues, @@ -86,6 +92,7 @@ fun MainScreen( val haptic = LocalHapticFeedback.current val context = LocalContext.current val isVisible = rememberSaveable { mutableStateOf(true) } + val focusRequester = remember { FocusRequester() } val scope = rememberCoroutineScope() val sheetState = rememberModalBottomSheetState() @@ -256,7 +263,13 @@ fun MainScreen( haptic.performHapticFeedback(HapticFeedbackType.LongPress) selectedTunnel = tunnel; }, - onClick = { navController.navigate("${Routes.Detail.name}/${tunnel.id}") }, + onClick = { + if(!context.packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)){ + navController.navigate("${Routes.Detail.name}/${tunnel.id}") + } else { + focusRequester.requestFocus() + } + }, rowButton = { if (tunnel.id == selectedTunnel?.id) { Row() { @@ -265,7 +278,9 @@ fun MainScreen( }) { Icon(Icons.Rounded.Edit, stringResource(id = R.string.edit)) } - IconButton(onClick = { viewModel.onDelete(tunnel) }) { + IconButton( + modifier = Modifier.focusable(), + onClick = { viewModel.onDelete(tunnel) }) { Icon( Icons.Rounded.Delete, stringResource(id = R.string.delete) @@ -273,12 +288,51 @@ fun MainScreen( } } } else { - Switch( - checked = (state == Tunnel.State.UP && tunnel.name == tunnelName), - onCheckedChange = { checked -> - if (checked) viewModel.onTunnelStart(tunnel) else viewModel.onTunnelStop() + if(context.packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)){ + 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)) + } else { + navController.navigate("${Routes.Config.name}/${tunnel.id}") + } + }) { + 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)) + } else { + viewModel.onDelete(tunnel) + } + }) { + Icon( + Icons.Rounded.Delete, + stringResource(id = R.string.delete) + ) + } + Switch( + checked = (state == Tunnel.State.UP && tunnel.name == tunnelName), + onCheckedChange = { checked -> + if (checked) viewModel.onTunnelStart(tunnel) else viewModel.onTunnelStop() + } + ) } - ) + } else { + Switch( + checked = (state == Tunnel.State.UP && tunnel.name == tunnelName), + onCheckedChange = { checked -> + if (checked) viewModel.onTunnelStart(tunnel) else viewModel.onTunnelStop() + } + ) + } } }) } diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/SettingsScreen.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/SettingsScreen.kt index 8feb52e..87cfdd4 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/SettingsScreen.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/settings/SettingsScreen.kt @@ -2,6 +2,7 @@ package com.zaneschepke.wireguardautotunnel.ui.screens.settings import android.Manifest import android.content.Intent +import android.content.pm.PackageManager import android.net.Uri import android.provider.Settings import androidx.compose.foundation.clickable @@ -47,6 +48,8 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalFocusManager @@ -82,6 +85,7 @@ fun SettingsScreen( val scope = rememberCoroutineScope() val context = LocalContext.current + val focusRequester = remember { FocusRequester() } val focusManager = LocalFocusManager.current val interactionSource = remember { MutableInteractionSource() } @@ -92,6 +96,7 @@ fun SettingsScreen( val tunnels by viewModel.tunnels.collectAsStateWithLifecycle(mutableListOf()) val backgroundLocationState = rememberPermissionState(Manifest.permission.ACCESS_BACKGROUND_LOCATION) + val fineLocationState = rememberPermissionState(Manifest.permission.ACCESS_FINE_LOCATION) var currentText by remember { mutableStateOf("") } val scrollState = rememberScrollState() var isLocationServicesEnabled by remember { mutableStateOf(viewModel.checkLocationServicesEnabled())} @@ -119,6 +124,16 @@ fun SettingsScreen( } } + fun openSettings() { + scope.launch { + val intentSettings = + Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) + intentSettings.data = + Uri.fromParts("package", context.packageName, null) + context.startActivity(intentSettings) + } + } + if(!backgroundLocationState.status.isGranted) { Column(horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Top, @@ -131,7 +146,6 @@ fun SettingsScreen( .size(128.dp)) Text(stringResource(R.string.prominent_background_location_title), textAlign = TextAlign.Center, modifier = Modifier.padding(30.dp), fontSize = 20.sp) Text(stringResource(R.string.prominent_background_location_message), textAlign = TextAlign.Center, modifier = Modifier.padding(30.dp), fontSize = 15.sp) - //Spacer(modifier = Modifier.weight(1f)) Row( modifier = Modifier .fillMaxWidth() @@ -144,22 +158,50 @@ fun SettingsScreen( }) { Text(stringResource(id = R.string.no_thanks)) } - Button(onClick = { - scope.launch { - val intentSettings = - Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) - intentSettings.data = - Uri.fromParts("package", context.packageName, null) - context.startActivity(intentSettings) - } + Button(modifier = Modifier.focusRequester(focusRequester), onClick = { + openSettings() }) { Text(stringResource(id = R.string.turn_on)) } + LaunchedEffect(Unit) { + if(context.packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) { + focusRequester.requestFocus() + } + } } } return } + if(!fineLocationState.status.isGranted) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + modifier = Modifier + .fillMaxSize() + .padding(padding) + ) { + Text( + stringResource(id = R.string.precise_location_message), + textAlign = TextAlign.Center, + modifier = Modifier.padding(15.dp), + fontStyle = FontStyle.Italic + ) + Button(modifier = Modifier.focusRequester(focusRequester),onClick = { + fineLocationState.launchPermissionRequest() + }) { + Text(stringResource(id = R.string.request)) + } + LaunchedEffect(Unit) { + if(context.packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) { + focusRequester.requestFocus() + } + } + + } + return + } + if (tunnels.isEmpty()) { Column( horizontalAlignment = Alignment.CenterHorizontally, @@ -191,7 +233,7 @@ fun SettingsScreen( modifier = Modifier.padding(15.dp), fontStyle = FontStyle.Italic ) - Button(onClick = { + Button(modifier = Modifier.focusRequester(focusRequester), onClick = { val locationServicesEnabled = viewModel.checkLocationServicesEnabled() isLocationServicesEnabled = locationServicesEnabled if(!locationServicesEnabled) { @@ -202,6 +244,11 @@ fun SettingsScreen( }) { Text(stringResource(id = R.string.check_again)) } + LaunchedEffect(Unit) { + if(context.packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) { + focusRequester.requestFocus() + } + } } return } @@ -226,6 +273,7 @@ fun SettingsScreen( ) { Text(stringResource(R.string.enable_auto_tunnel)) Switch( + modifier = Modifier.focusRequester(focusRequester), enabled = !settings.isAlwaysOnVpnEnabled, checked = settings.isAutoTunnelEnabled, onCheckedChange = { @@ -234,6 +282,11 @@ fun SettingsScreen( } } ) + LaunchedEffect(Unit) { + if(context.packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) { + focusRequester.requestFocus() + } + } } Text( stringResource(id = R.string.select_tunnel), @@ -245,7 +298,9 @@ fun SettingsScreen( onExpandedChange = { if(!(settings.isAutoTunnelEnabled || settings.isAlwaysOnVpnEnabled)) { expanded = !expanded }}, - modifier = Modifier.padding(start = 15.dp, top = 5.dp, bottom = 10.dp), + modifier = Modifier.padding(start = 15.dp, top = 5.dp, bottom = 10.dp).clickable { + expanded = !expanded + }, ) { TextField( enabled = !(settings.isAutoTunnelEnabled || settings.isAlwaysOnVpnEnabled), diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/support/SupportScreen.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/support/SupportScreen.kt index 93147e4..3c264a9 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/support/SupportScreen.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/support/SupportScreen.kt @@ -1,8 +1,10 @@ package com.zaneschepke.wireguardautotunnel.ui.screens.support import android.content.Intent +import android.content.pm.PackageManager import android.net.Uri import androidx.compose.foundation.clickable +import androidx.compose.foundation.focusable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues @@ -17,8 +19,12 @@ import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource @@ -34,6 +40,7 @@ import com.zaneschepke.wireguardautotunnel.R fun SupportScreen(padding : PaddingValues) { val context = LocalContext.current + val focusRequester = remember { FocusRequester() } fun openWebPage(url: String) { val webpage: Uri = Uri.parse(url) @@ -46,6 +53,7 @@ fun SupportScreen(padding : PaddingValues) { modifier = Modifier .fillMaxSize() .verticalScroll(rememberScrollState()) + .focusable() .padding(padding)) { Text(stringResource(R.string.support_text), textAlign = TextAlign.Center, modifier = Modifier.padding(30.dp), fontSize = 15.sp) Row( @@ -60,11 +68,16 @@ fun SupportScreen(padding : PaddingValues) { }) { Icon(imageVector = ImageVector.vectorResource(R.drawable.discord), "Discord") } - IconButton(onClick = { + IconButton(modifier = Modifier.focusRequester(focusRequester),onClick = { openWebPage(context.resources.getString(R.string.github_url)) }) { Icon(imageVector = ImageVector.vectorResource(R.drawable.github), "Github") } + LaunchedEffect(Unit) { + if(context.packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) { + focusRequester.requestFocus() + } + } } Spacer(modifier = Modifier.weight(1f)) Text(stringResource(id = R.string.privacy_policy), style = TextStyle(textDecoration = TextDecoration.Underline), diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_banner.xml b/app/src/main/res/mipmap-anydpi-v26/ic_banner.xml new file mode 100644 index 0000000..cf3108b --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_banner.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-xhdpi/ic_banner.png b/app/src/main/res/mipmap-xhdpi/ic_banner.png new file mode 100644 index 0000000000000000000000000000000000000000..b85f129e8dddb43b31ee62bccaa5f2a8f97890f5 GIT binary patch literal 9366 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 C{$kev literal 0 HcmV?d00001 diff --git a/app/src/main/res/values/ic_banner_background.xml b/app/src/main/res/values/ic_banner_background.xml new file mode 100644 index 0000000..5274b8a --- /dev/null +++ b/app/src/main/res/values/ic_banner_background.xml @@ -0,0 +1,4 @@ + + + #121212 + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ddee92f..a06e840 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -82,4 +82,6 @@ Unable to detect Location Services which are required for this feature. Please enable Location Services. Check again Detecting Location Services disabled + This feature requires precise location to access Wi-Fi SSID name. Please enable precise location here or in the app settings. + Request \ No newline at end of file