Compare commits

..

4 Commits

Author SHA1 Message Date
Zane Schepke f513297ba0 fix: file selection on older devices
Fixes bug where file selection was causing app to crash on older devices.
2023-08-11 21:13:54 -04:00
Zane Schepke 135f8c0459 chore: add tv assets 2023-08-11 20:56:03 -04:00
Zane Schepke 7a811f4152 fix: bug causing crashes on older devices
Fixes an issue where watcher service had the potential to crash on older devices if the job was not initialized fast enough.

Optimize imports.

Bump versions.
2023-08-11 20:21:28 -04:00
Zane Schepke 2abf681d17 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
2023-08-04 16:43:36 -04:00
18 changed files with 73 additions and 62 deletions
+6 -6
View File
@@ -17,12 +17,12 @@ android {
val versionMajor = 2 val versionMajor = 2
val versionMinor = 3 val versionMinor = 3
val versionPatch = 2 val versionPatch = 5
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,13 +83,13 @@ 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")
// compose navigation // compose navigation
implementation("androidx.navigation:navigation-compose:2.6.0") implementation("androidx.navigation:navigation-compose:2.7.0")
implementation("androidx.hilt:hilt-navigation-compose:1.0.0") implementation("androidx.hilt:hilt-navigation-compose:1.0.0")
// hilt // hilt
@@ -110,7 +110,7 @@ dependencies {
implementation("androidx.lifecycle:lifecycle-runtime-compose:2.6.1") implementation("androidx.lifecycle:lifecycle-runtime-compose:2.6.1")
//icons //icons
implementation("androidx.compose.material:material-icons-extended:1.4.3") implementation("androidx.compose.material:material-icons-extended:1.5.0")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
@@ -122,7 +122,7 @@ dependencies {
implementation("com.google.firebase:firebase-analytics-ktx") implementation("com.google.firebase:firebase-analytics-ktx")
//barcode scanning //barcode scanning
implementation("com.google.android.gms:play-services-code-scanner:16.0.0") implementation("com.google.android.gms:play-services-code-scanner:16.1.0")
} }
kapt { kapt {
@@ -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)
}
}
} }
@@ -1,7 +1,5 @@
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
@@ -12,7 +10,6 @@ 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)
@@ -9,10 +9,9 @@ import com.zaneschepke.wireguardautotunnel.service.foreground.Action
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceTracker import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceTracker
import com.zaneschepke.wireguardautotunnel.service.foreground.WireGuardConnectivityWatcherService import com.zaneschepke.wireguardautotunnel.service.foreground.WireGuardConnectivityWatcherService
import com.zaneschepke.wireguardautotunnel.service.tunnel.model.Settings import com.zaneschepke.wireguardautotunnel.service.tunnel.model.Settings
import com.zaneschepke.wireguardautotunnel.service.tunnel.model.TunnelConfig
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
@@ -25,7 +24,7 @@ class BootReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) { override fun onReceive(context: Context, intent: Intent) {
if (intent.action == Intent.ACTION_BOOT_COMPLETED) { if (intent.action == Intent.ACTION_BOOT_COMPLETED) {
CoroutineScope(SupervisorJob()).launch { CoroutineScope(Dispatchers.IO).launch {
try { try {
val settings = settingsRepo.getAll() val settings = settingsRepo.getAll()
if (!settings.isNullOrEmpty()) { if (!settings.isNullOrEmpty()) {
@@ -11,7 +11,7 @@ import com.zaneschepke.wireguardautotunnel.service.foreground.WireGuardTunnelSer
import com.zaneschepke.wireguardautotunnel.service.tunnel.model.Settings import com.zaneschepke.wireguardautotunnel.service.tunnel.model.Settings
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -23,7 +23,7 @@ class NotificationActionReceiver : BroadcastReceiver() {
@Inject @Inject
lateinit var settingsRepo : Repository<Settings> lateinit var settingsRepo : Repository<Settings>
override fun onReceive(context: Context, intent: Intent?) { override fun onReceive(context: Context, intent: Intent?) {
CoroutineScope(SupervisorJob()).launch { CoroutineScope(Dispatchers.IO).launch {
try { try {
val settings = settingsRepo.getAll() val settings = settingsRepo.getAll()
if (!settings.isNullOrEmpty()) { if (!settings.isNullOrEmpty()) {
@@ -8,8 +8,6 @@ import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.os.PowerManager import android.os.PowerManager
import android.os.SystemClock import android.os.SystemClock
import androidx.compose.runtime.collectAsState
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.wireguard.android.backend.Tunnel import com.wireguard.android.backend.Tunnel
import com.zaneschepke.wireguardautotunnel.Constants import com.zaneschepke.wireguardautotunnel.Constants
import com.zaneschepke.wireguardautotunnel.R import com.zaneschepke.wireguardautotunnel.R
@@ -23,11 +21,9 @@ import com.zaneschepke.wireguardautotunnel.service.tunnel.VpnService
import com.zaneschepke.wireguardautotunnel.service.tunnel.model.Settings import com.zaneschepke.wireguardautotunnel.service.tunnel.model.Settings
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.single
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@@ -128,20 +124,20 @@ class WireGuardConnectivityWatcherService : ForegroundService() {
} }
private fun startWatcherJob() { private fun startWatcherJob() {
watcherJob = CoroutineScope(SupervisorJob()).launch { watcherJob = CoroutineScope(Dispatchers.IO).launch {
val settings = settingsRepo.getAll(); val settings = settingsRepo.getAll();
if(!settings.isNullOrEmpty()) { if(!settings.isNullOrEmpty()) {
setting = settings[0] setting = settings[0]
} }
CoroutineScope(watcherJob).launch { launch {
watchForWifiConnectivityChanges() watchForWifiConnectivityChanges()
} }
if(setting.isTunnelOnMobileDataEnabled) { if(setting.isTunnelOnMobileDataEnabled) {
CoroutineScope(watcherJob).launch { launch {
watchForMobileDataConnectivityChanges() watchForMobileDataConnectivityChanges()
} }
} }
CoroutineScope(watcherJob).launch { launch {
manageVpn() manageVpn()
} }
} }
@@ -15,7 +15,6 @@ import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@@ -42,7 +41,7 @@ class WireGuardTunnelService : ForegroundService() {
super.startService(extras) super.startService(extras)
val tunnelConfigString = extras?.getString(getString(R.string.tunnel_extras_key)) val tunnelConfigString = extras?.getString(getString(R.string.tunnel_extras_key))
cancelJob() cancelJob()
job = CoroutineScope(SupervisorJob()).launch { job = CoroutineScope(Dispatchers.IO).launch {
if(tunnelConfigString != null) { if(tunnelConfigString != null) {
try { try {
val tunnelConfig = TunnelConfig.from(tunnelConfigString) val tunnelConfig = TunnelConfig.from(tunnelConfigString)
@@ -45,6 +45,7 @@ abstract class BaseNetworkService<T : BaseNetworkService<T>>(val context: Contex
} }
} }
} }
else -> { else -> {
object : ConnectivityManager.NetworkCallback() { object : ConnectivityManager.NetworkCallback() {
@@ -78,7 +79,7 @@ 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
@@ -90,15 +91,16 @@ 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? {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val info: WifiInfo val info: WifiInfo
if (networkCapabilities.transportInfo is WifiInfo) { if (networkCapabilities.transportInfo is WifiInfo) {
info = networkCapabilities.transportInfo as WifiInfo info = networkCapabilities.transportInfo as WifiInfo
} else {
return null
}
return info.ssid return info.ssid
} }
} }
return null
}
}
} }
inline fun <Result> Flow<NetworkStatus>.map( inline fun <Result> Flow<NetworkStatus>.map(
@@ -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}")
@@ -144,7 +144,12 @@ class MainViewModel @Inject constructor(private val application : Application,
@SuppressLint("Range") @SuppressLint("Range")
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 = try {
context.contentResolver.query(uri, null, null, null, null)
} catch (e : Exception) {
Timber.d("Exception getting config name")
null
}
cursor ?: return defaultConfigName() cursor ?: return defaultConfigName()
cursor.use { cursor.use {
if(cursor.moveToFirst()) { if(cursor.moveToFirst()) {
@@ -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,12 +330,12 @@ 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 verticalArrangement = Arrangement.SpaceEvenly
) { ) {
trustedSSIDs.forEach { ssid -> trustedSSIDs.forEach { ssid ->
ClickableIconButton(onIconClick = { ClickableIconButton(onIconClick = {
@@ -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
) { ) {
@@ -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),
Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 KiB

+1 -1
View File
@@ -13,7 +13,7 @@ buildscript {
} }
plugins { plugins {
id("com.android.application") version "8.2.0-alpha08" apply false id("com.android.application") version "8.2.0-alpha15" apply false
id("org.jetbrains.kotlin.android") version "1.8.22" apply false id("org.jetbrains.kotlin.android") version "1.8.22" apply false
id("com.google.dagger.hilt.android") version "2.44" apply false id("com.google.dagger.hilt.android") version "2.44" apply false
kotlin("plugin.serialization") version "1.8.22" apply false kotlin("plugin.serialization") version "1.8.22" apply false
+1 -1
View File
@@ -1,6 +1,6 @@
#Mon Apr 24 22:46:45 EDT 2023 #Mon Apr 24 22:46:45 EDT 2023
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists