feat: support Android 9

Added support for Android 9 by updating permission checks and wifi SSID logic.

Fix bug where setting screen was cut off on AndroidTV by updating padding values.

Bump wireguard-android library version.

Closes #13, Closes #16
This commit is contained in:
Zane Schepke 2023-08-04 16:43:36 -04:00
parent 689c97f452
commit 2abf681d17
6 changed files with 51 additions and 36 deletions

View File

@ -17,12 +17,12 @@ android {
val versionMajor = 2 val versionMajor = 2
val versionMinor = 3 val versionMinor = 3
val versionPatch = 2 val versionPatch = 3
val versionBuild = 0 val versionBuild = 0
defaultConfig { defaultConfig {
applicationId = "com.zaneschepke.wireguardautotunnel" applicationId = "com.zaneschepke.wireguardautotunnel"
minSdk = 29 minSdk = 28
targetSdk = 34 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}"
@ -83,7 +83,7 @@ dependencies {
debugImplementation("androidx.compose.ui:ui-test-manifest") debugImplementation("androidx.compose.ui:ui-test-manifest")
//wireguard tunnel //wireguard tunnel
implementation("com.wireguard.android:tunnel:1.0.20230427") implementation("com.wireguard.android:tunnel:1.0.20230706")
//logging //logging
implementation("com.jakewharton.timber:timber:5.0.1") implementation("com.jakewharton.timber:timber:5.0.1")

View File

@ -1,6 +1,8 @@
package com.zaneschepke.wireguardautotunnel package com.zaneschepke.wireguardautotunnel
import android.app.Application import android.app.Application
import android.content.Context
import android.content.pm.PackageManager
import com.google.firebase.crashlytics.FirebaseCrashlytics 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
@ -22,4 +24,10 @@ class WireGuardAutoTunnel : Application() {
} }
settingsRepo.init() settingsRepo.init()
} }
companion object {
fun isRunningOnAndroidTv(context : Context) : Boolean {
return context.packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
}
}
} }

View File

@ -45,6 +45,7 @@ abstract class BaseNetworkService<T : BaseNetworkService<T>>(val context: Contex
} }
} }
} }
else -> { else -> {
object : ConnectivityManager.NetworkCallback() { object : ConnectivityManager.NetworkCallback() {
@ -77,8 +78,8 @@ abstract class BaseNetworkService<T : BaseNetworkService<T>>(val context: Contex
override fun getNetworkName(networkCapabilities: NetworkCapabilities): String? { override fun getNetworkName(networkCapabilities: NetworkCapabilities): String? {
var ssid : String? = getWifiNameFromCapabilities(networkCapabilities) var ssid: String? = getWifiNameFromCapabilities(networkCapabilities)
if((Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) || (Build.VERSION.SDK_INT == Build.VERSION_CODES.R)) { if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) {
val info = wifiManager.connectionInfo val info = wifiManager.connectionInfo
if (info.supplicantState === SupplicantState.COMPLETED) { if (info.supplicantState === SupplicantState.COMPLETED) {
ssid = info.ssid ssid = info.ssid
@ -89,14 +90,15 @@ abstract class BaseNetworkService<T : BaseNetworkService<T>>(val context: Contex
companion object { companion object {
private fun getWifiNameFromCapabilities(networkCapabilities: NetworkCapabilities) : String? { private fun getWifiNameFromCapabilities(networkCapabilities: NetworkCapabilities): String? {
val info : WifiInfo if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if(networkCapabilities.transportInfo is WifiInfo) { val info: WifiInfo
info = networkCapabilities.transportInfo as WifiInfo if (networkCapabilities.transportInfo is WifiInfo) {
} else { info = networkCapabilities.transportInfo as WifiInfo
return null return info.ssid
}
} }
return info.ssid return null
} }
} }
} }

View File

@ -1,7 +1,6 @@
package com.zaneschepke.wireguardautotunnel.ui.screens.main package com.zaneschepke.wireguardautotunnel.ui.screens.main
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.pm.PackageManager
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.AnimatedVisibility
@ -52,7 +51,6 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable 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.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.focusRequester
@ -73,6 +71,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavController import androidx.navigation.NavController
import com.wireguard.android.backend.Tunnel import com.wireguard.android.backend.Tunnel
import com.zaneschepke.wireguardautotunnel.R import com.zaneschepke.wireguardautotunnel.R
import com.zaneschepke.wireguardautotunnel.WireGuardAutoTunnel
import com.zaneschepke.wireguardautotunnel.service.tunnel.HandshakeStatus import com.zaneschepke.wireguardautotunnel.service.tunnel.HandshakeStatus
import com.zaneschepke.wireguardautotunnel.service.tunnel.model.TunnelConfig import com.zaneschepke.wireguardautotunnel.service.tunnel.model.TunnelConfig
import com.zaneschepke.wireguardautotunnel.ui.Routes import com.zaneschepke.wireguardautotunnel.ui.Routes
@ -246,12 +245,12 @@ fun MainScreen(
.nestedScroll(nestedScrollConnection),) { .nestedScroll(nestedScrollConnection),) {
items(tunnels.toList()) { tunnel -> items(tunnels.toList()) { tunnel ->
RowListItem(leadingIcon = Icons.Rounded.Circle, RowListItem(leadingIcon = Icons.Rounded.Circle,
leadingIconColor = when (handshakeStatus) { leadingIconColor = if (tunnelName == tunnel.name) when (handshakeStatus) {
HandshakeStatus.HEALTHY -> mint HandshakeStatus.HEALTHY -> mint
HandshakeStatus.UNHEALTHY -> brickRed HandshakeStatus.UNHEALTHY -> brickRed
HandshakeStatus.NOT_STARTED -> Color.Gray HandshakeStatus.NOT_STARTED -> Color.Gray
HandshakeStatus.NEVER_CONNECTED -> brickRed HandshakeStatus.NEVER_CONNECTED -> brickRed
}, } else Color.Gray,
text = tunnel.name, text = tunnel.name,
onHold = { onHold = {
if (state == Tunnel.State.UP && tunnel.name == tunnelName) { if (state == Tunnel.State.UP && tunnel.name == tunnelName) {
@ -264,7 +263,7 @@ fun MainScreen(
selectedTunnel = tunnel; selectedTunnel = tunnel;
}, },
onClick = { onClick = {
if(!context.packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)){ if(!WireGuardAutoTunnel.isRunningOnAndroidTv(context)){
navController.navigate("${Routes.Detail.name}/${tunnel.id}") navController.navigate("${Routes.Detail.name}/${tunnel.id}")
} else { } else {
focusRequester.requestFocus() focusRequester.requestFocus()
@ -288,7 +287,7 @@ fun MainScreen(
} }
} }
} else { } else {
if(context.packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)){ if(WireGuardAutoTunnel.isRunningOnAndroidTv(context)){
Row() { Row() {
IconButton(modifier = Modifier.focusRequester(focusRequester),onClick = { IconButton(modifier = Modifier.focusRequester(focusRequester),onClick = {
navController.navigate("${Routes.Detail.name}/${tunnel.id}") navController.navigate("${Routes.Detail.name}/${tunnel.id}")

View File

@ -2,8 +2,8 @@ package com.zaneschepke.wireguardautotunnel.ui.screens.settings
import android.Manifest import android.Manifest
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri import android.net.Uri
import android.os.Build
import android.provider.Settings import android.provider.Settings
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.FlowRow
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
@ -67,6 +68,7 @@ import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.isGranted import com.google.accompanist.permissions.isGranted
import com.google.accompanist.permissions.rememberPermissionState import com.google.accompanist.permissions.rememberPermissionState
import com.zaneschepke.wireguardautotunnel.R import com.zaneschepke.wireguardautotunnel.R
import com.zaneschepke.wireguardautotunnel.WireGuardAutoTunnel
import com.zaneschepke.wireguardautotunnel.service.tunnel.model.TunnelConfig import com.zaneschepke.wireguardautotunnel.service.tunnel.model.TunnelConfig
import com.zaneschepke.wireguardautotunnel.ui.Routes import com.zaneschepke.wireguardautotunnel.ui.Routes
import com.zaneschepke.wireguardautotunnel.ui.common.ClickableIconButton import com.zaneschepke.wireguardautotunnel.ui.common.ClickableIconButton
@ -134,7 +136,7 @@ fun SettingsScreen(
} }
} }
if(!backgroundLocationState.status.isGranted) { if(!backgroundLocationState.status.isGranted && Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
Column(horizontalAlignment = Alignment.CenterHorizontally, Column(horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Top, verticalArrangement = Arrangement.Top,
modifier = Modifier modifier = Modifier
@ -147,7 +149,9 @@ fun SettingsScreen(
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_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) Text(stringResource(R.string.prominent_background_location_message), textAlign = TextAlign.Center, modifier = Modifier.padding(30.dp), fontSize = 15.sp)
Row( Row(
modifier = Modifier modifier = if(WireGuardAutoTunnel.isRunningOnAndroidTv(context)) Modifier
.fillMaxWidth()
.padding(10.dp) else Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(30.dp), .padding(30.dp),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
@ -209,7 +213,7 @@ fun SettingsScreen(
} }
return return
} }
if(!isLocationServicesEnabled) { if(!isLocationServicesEnabled && Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
Column( Column(
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center, verticalArrangement = Arrangement.Center,
@ -237,11 +241,18 @@ fun SettingsScreen(
} }
return return
} }
val screenPadding = if(WireGuardAutoTunnel.isRunningOnAndroidTv(context)) 5.dp else 15.dp
Column( Column(
horizontalAlignment = Alignment.Start, horizontalAlignment = Alignment.Start,
verticalArrangement = Arrangement.Top, verticalArrangement = Arrangement.Top,
modifier = Modifier modifier = if(WireGuardAutoTunnel.isRunningOnAndroidTv(context)) Modifier
.fillMaxHeight(.85f)
.fillMaxWidth()
.verticalScroll(scrollState)
.clickable(indication = null, interactionSource = interactionSource) {
focusManager.clearFocus()
}
.padding(padding) else Modifier
.fillMaxSize() .fillMaxSize()
.verticalScroll(scrollState) .verticalScroll(scrollState)
.clickable(indication = null, interactionSource = interactionSource) { .clickable(indication = null, interactionSource = interactionSource) {
@ -252,7 +263,7 @@ fun SettingsScreen(
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(14.dp), .padding(screenPadding),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween horizontalArrangement = Arrangement.SpaceBetween
) { ) {
@ -271,7 +282,7 @@ fun SettingsScreen(
Text( Text(
stringResource(id = R.string.select_tunnel), stringResource(id = R.string.select_tunnel),
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
modifier = Modifier.padding(15.dp, bottom = 5.dp, top = 5.dp) modifier = Modifier.padding(screenPadding, bottom = 5.dp, top = 5.dp)
) )
ExposedDropdownMenuBox( ExposedDropdownMenuBox(
expanded = expanded, expanded = expanded,
@ -319,10 +330,10 @@ fun SettingsScreen(
Text( Text(
stringResource(R.string.trusted_ssid), stringResource(R.string.trusted_ssid),
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
modifier = Modifier.padding(15.dp, bottom = 5.dp, top = 5.dp) modifier = Modifier.padding(screenPadding, bottom = 5.dp, top = 5.dp)
) )
FlowRow( FlowRow(
modifier = Modifier.padding(15.dp), modifier = Modifier.padding(screenPadding),
horizontalArrangement = Arrangement.spacedBy(8.dp), horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
@ -339,7 +350,7 @@ fun SettingsScreen(
value = currentText, value = currentText,
onValueChange = { currentText = it }, onValueChange = { currentText = it },
label = { Text(stringResource(R.string.add_trusted_ssid)) }, label = { Text(stringResource(R.string.add_trusted_ssid)) },
modifier = Modifier.padding(start = 15.dp, top = 5.dp), modifier = Modifier.padding(start = screenPadding, top = 5.dp),
maxLines = 1, maxLines = 1,
keyboardOptions = KeyboardOptions( keyboardOptions = KeyboardOptions(
capitalization = KeyboardCapitalization.None, capitalization = KeyboardCapitalization.None,
@ -365,7 +376,7 @@ fun SettingsScreen(
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(14.dp), .padding(screenPadding),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween horizontalArrangement = Arrangement.SpaceBetween
) { ) {
@ -383,7 +394,7 @@ fun SettingsScreen(
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(14.dp), .padding(screenPadding),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween horizontalArrangement = Arrangement.SpaceBetween
) { ) {

View File

@ -72,11 +72,6 @@ fun SupportScreen(padding : PaddingValues, focusRequester: FocusRequester) {
}) { }) {
Icon(imageVector = ImageVector.vectorResource(R.drawable.github), "Github") Icon(imageVector = ImageVector.vectorResource(R.drawable.github), "Github")
} }
// LaunchedEffect(Unit) {
// if(context.packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
// focusRequester.requestFocus()
// }
// }
} }
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
Text(stringResource(id = R.string.privacy_policy), style = TextStyle(textDecoration = TextDecoration.Underline), Text(stringResource(id = R.string.privacy_policy), style = TextStyle(textDecoration = TextDecoration.Underline),