Compare commits

..

3 Commits

Author SHA1 Message Date
Zane Schepke e81066f508 fix: FireTV file selection bug
Fixes bug where FireTV devices were unable to launch a proper file browser to select tunnel configs.
2023-09-11 11:15:44 -04:00
Zane Schepke 64bb9f3b82 fix: foreground service start crashes
Attempt to fix startForegrounService causing crashes on some devices by not meeting the 5 second notification rule. Add notification to onCreate of services.

Limit startForeground to only be called where it is truly necessary in the TileService to allow starting the VPN while app is not running.

Attempt to manually initialize mlkit for QR code scanning to remediate some crashes caused by config scanning.
2023-09-10 00:23:23 -04:00
Zane Schepke c1b560e822 chore: update README.md 2023-09-08 12:43:41 -04:00
11 changed files with 71 additions and 26 deletions
+1
View File
@@ -13,6 +13,7 @@ WG Tunnel
[![Google Play](https://img.shields.io/badge/Google_Play-414141?style=for-the-badge&logo=google-play&logoColor=white)](https://play.google.com/store/apps/details?id=com.zaneschepke.wireguardautotunnel) [![Google Play](https://img.shields.io/badge/Google_Play-414141?style=for-the-badge&logo=google-play&logoColor=white)](https://play.google.com/store/apps/details?id=com.zaneschepke.wireguardautotunnel)
[![Fire TV](https://img.shields.io/badge/fire%20tv-fc3b2d?style=for-the-badge&logo=amazon%20fire%20tv&logoColor=white)](https://www.amazon.com/gp/product/B0CFGGL7WK)
</span> </span>
+1 -1
View File
@@ -17,7 +17,7 @@ android {
val versionMajor = 2 val versionMajor = 2
val versionMinor = 4 val versionMinor = 4
val versionPatch = 2 val versionPatch = 4
val versionBuild = 0 val versionBuild = 0
defaultConfig { defaultConfig {
@@ -4,6 +4,7 @@ import android.app.Application
import android.content.Context import android.content.Context
import android.content.pm.PackageManager import android.content.pm.PackageManager
import com.google.firebase.crashlytics.FirebaseCrashlytics import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.google.mlkit.common.MlKit
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
@@ -22,6 +23,11 @@ class WireGuardAutoTunnel : Application() {
FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(false); FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(false);
Timber.plant(Timber.DebugTree()) Timber.plant(Timber.DebugTree())
} }
try {
MlKit.initialize(this)
} catch (e : Exception) {
Timber.e(e.message)
}
settingsRepo.init() settingsRepo.init()
} }
@@ -2,5 +2,6 @@ package com.zaneschepke.wireguardautotunnel.service.foreground
enum class Action { enum class Action {
START, START,
START_FOREGROUND,
STOP STOP
} }
@@ -22,7 +22,7 @@ open class ForegroundService : Service() {
val action = intent.action val action = intent.action
Timber.d("using an intent with action $action") Timber.d("using an intent with action $action")
when (action) { when (action) {
Action.START.name -> startService(intent.extras) Action.START.name, Action.START_FOREGROUND.name -> startService(intent.extras)
Action.STOP.name -> stopService(intent.extras) Action.STOP.name -> stopService(intent.extras)
"android.net.VpnService" -> { "android.net.VpnService" -> {
Timber.d("Always-on VPN starting service") Timber.d("Always-on VPN starting service")
@@ -1,7 +1,6 @@
package com.zaneschepke.wireguardautotunnel.service.foreground package com.zaneschepke.wireguardautotunnel.service.foreground
import android.app.ActivityManager import android.app.ActivityManager
import android.app.Application
import android.app.Service import android.app.Service
import android.content.Context import android.content.Context
import android.content.Context.ACTIVITY_SERVICE import android.content.Context.ACTIVITY_SERVICE
@@ -9,7 +8,6 @@ import android.content.Intent
import com.google.firebase.crashlytics.ktx.crashlytics import com.google.firebase.crashlytics.ktx.crashlytics
import com.google.firebase.ktx.Firebase import com.google.firebase.ktx.Firebase
import com.zaneschepke.wireguardautotunnel.R import com.zaneschepke.wireguardautotunnel.R
import timber.log.Timber
object ServiceManager { object ServiceManager {
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
@@ -36,13 +34,11 @@ object ServiceManager {
intent.component?.javaClass intent.component?.javaClass
try { try {
when(action) { when(action) {
Action.START -> { Action.START_FOREGROUND -> {
try {
context.startForegroundService(intent) context.startForegroundService(intent)
} catch (e : Exception) {
Timber.e("Unable to start service foreground ${e.message}")
context.startService(intent)
} }
Action.START -> {
context.startService(intent)
} }
Action.STOP -> context.startService(intent) Action.STOP -> context.startService(intent)
} }
@@ -66,6 +62,22 @@ object ServiceManager {
) )
} }
fun startVpnServiceForeground(context : Context, tunnelConfig : String) {
actionOnService(
Action.START_FOREGROUND,
context,
WireGuardTunnelService::class.java,
mapOf(context.getString(R.string.tunnel_extras_key) to tunnelConfig))
}
private fun startWatcherServiceForeground(context : Context, tunnelConfig : String) {
actionOnService(
Action.START, context,
WireGuardConnectivityWatcherService::class.java, mapOf(context.
getString(R.string.tunnel_extras_key) to
tunnelConfig))
}
fun startWatcherService(context : Context, tunnelConfig : String) { fun startWatcherService(context : Context, tunnelConfig : String) {
actionOnService( actionOnService(
Action.START, context, Action.START, context,
@@ -87,4 +99,12 @@ object ServiceManager {
ServiceState.STOPPED -> startWatcherService(context, tunnelConfig) ServiceState.STOPPED -> startWatcherService(context, tunnelConfig)
} }
} }
fun toggleWatcherServiceForeground(context: Context, tunnelConfig : String) {
when(getServiceState( context,
WireGuardConnectivityWatcherService::class.java,)) {
ServiceState.STARTED -> stopWatcherService(context)
ServiceState.STOPPED -> startWatcherServiceForeground(context, tunnelConfig)
}
}
} }
@@ -59,8 +59,16 @@ class WireGuardConnectivityWatcherService : ForegroundService() {
private val tag = this.javaClass.name; private val tag = this.javaClass.name;
override fun onCreate() {
super.onCreate()
CoroutineScope(Dispatchers.Main).launch {
launchWatcherNotification()
}
}
override fun startService(extras: Bundle?) { override fun startService(extras: Bundle?) {
super.startService(extras) super.startService(extras)
launchWatcherNotification()
val tunnelId = extras?.getString(getString(R.string.tunnel_extras_key)) val tunnelId = extras?.getString(getString(R.string.tunnel_extras_key))
if (tunnelId != null) { if (tunnelId != null) {
this.tunnelConfig = tunnelId this.tunnelConfig = tunnelId
@@ -68,7 +76,6 @@ class WireGuardConnectivityWatcherService : ForegroundService() {
// we need this lock so our service gets not affected by Doze Mode // we need this lock so our service gets not affected by Doze Mode
initWakeLock() initWakeLock()
cancelWatcherJob() cancelWatcherJob()
launchWatcherNotification()
if(this::tunnelConfig.isInitialized) { if(this::tunnelConfig.isInitialized) {
startWatcherJob() startWatcherJob()
} else { } else {
@@ -181,7 +188,7 @@ class WireGuardConnectivityWatcherService : ForegroundService() {
} }
private suspend fun manageVpn() { private suspend fun manageVpn() {
while(watcherJob.isActive) { while(true) {
if(setting.isTunnelOnMobileDataEnabled && if(setting.isTunnelOnMobileDataEnabled &&
!isWifiConnected && !isWifiConnected &&
isMobileDataConnected isMobileDataConnected
@@ -37,8 +37,16 @@ class WireGuardTunnelService : ForegroundService() {
private var tunnelName : String = "" private var tunnelName : String = ""
override fun onCreate() {
super.onCreate()
CoroutineScope(Dispatchers.Main).launch {
launchVpnStartingNotification()
}
}
override fun startService(extras : Bundle?) { override fun startService(extras : Bundle?) {
super.startService(extras) super.startService(extras)
launchVpnStartingNotification()
val tunnelConfigString = extras?.getString(getString(R.string.tunnel_extras_key)) val tunnelConfigString = extras?.getString(getString(R.string.tunnel_extras_key))
cancelJob() cancelJob()
job = CoroutineScope(Dispatchers.IO).launch { job = CoroutineScope(Dispatchers.IO).launch {
@@ -47,7 +55,6 @@ class WireGuardTunnelService : ForegroundService() {
val tunnelConfig = TunnelConfig.from(tunnelConfigString) val tunnelConfig = TunnelConfig.from(tunnelConfigString)
tunnelName = tunnelConfig.name tunnelName = tunnelConfig.name
vpnService.startTunnel(tunnelConfig) vpnService.startTunnel(tunnelConfig)
launchVpnStartingNotification()
} catch (e : Exception) { } catch (e : Exception) {
Timber.e("Problem starting tunnel: ${e.message}") Timber.e("Problem starting tunnel: ${e.message}")
stopService(extras) stopService(extras)
@@ -61,7 +68,6 @@ class WireGuardTunnelService : ForegroundService() {
val tunnelConfig = TunnelConfig.from(setting.defaultTunnel!!) val tunnelConfig = TunnelConfig.from(setting.defaultTunnel!!)
tunnelName = tunnelConfig.name tunnelName = tunnelConfig.name
vpnService.startTunnel(tunnelConfig) vpnService.startTunnel(tunnelConfig)
launchVpnStartingNotification()
} }
} }
} }
@@ -100,7 +106,7 @@ class WireGuardTunnelService : ForegroundService() {
override fun stopService(extras : Bundle?) { override fun stopService(extras : Bundle?) {
super.stopService(extras) super.stopService(extras)
CoroutineScope(Dispatchers.IO).launch() { CoroutineScope(Dispatchers.IO).launch {
vpnService.stopTunnel() vpnService.stopTunnel()
} }
cancelJob() cancelJob()
@@ -66,7 +66,7 @@ class TunnelControlTile : TileService() {
if(vpnService.getState() == Tunnel.State.UP) { if(vpnService.getState() == Tunnel.State.UP) {
ServiceManager.stopVpnService(this@TunnelControlTile) ServiceManager.stopVpnService(this@TunnelControlTile)
} else { } else {
ServiceManager.startVpnService(this@TunnelControlTile, tunnel.toString()) ServiceManager.startVpnServiceForeground(this@TunnelControlTile, tunnel.toString())
} }
} }
} catch (e : Exception) { } catch (e : Exception) {
@@ -100,7 +100,7 @@ class TunnelControlTile : TileService() {
if (!settings.isNullOrEmpty()) { if (!settings.isNullOrEmpty()) {
val setting = settings.first() val setting = settings.first()
if(setting.isAutoTunnelEnabled) { if(setting.isAutoTunnelEnabled) {
ServiceManager.toggleWatcherService(this@TunnelControlTile, tunnelConfig) ServiceManager.toggleWatcherServiceForeground(this@TunnelControlTile, tunnelConfig)
} }
} }
} }
@@ -1,6 +1,7 @@
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.Intent
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
@@ -17,7 +18,6 @@ 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.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
@@ -103,6 +103,7 @@ 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 // Nested scroll for control FAB
val nestedScrollConnection = remember { val nestedScrollConnection = remember {
object : NestedScrollConnection { object : NestedScrollConnection {
@@ -135,11 +136,9 @@ fun MainScreen(
} }
val pickFileLauncher = rememberLauncherForActivityResult( val pickFileLauncher = rememberLauncherForActivityResult(
ActivityResultContracts.GetContent() ActivityResultContracts.StartActivityForResult()
) { file -> ) { result ->
if (file != null) { result.data?.data?.let { viewModel.onTunnelFileSelected(it) }
viewModel.onTunnelFileSelected(file)
}
} }
Scaffold( Scaffold(
@@ -196,7 +195,11 @@ fun MainScreen(
.fillMaxWidth() .fillMaxWidth()
.clickable { .clickable {
showBottomSheet = false showBottomSheet = false
pickFileLauncher.launch("*/*") val fileSelectionIntent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "*/*"
}
pickFileLauncher.launch(fileSelectionIntent)
} }
.padding(10.dp) .padding(10.dp)
) { ) {
@@ -242,7 +245,8 @@ fun MainScreen(
) { ) {
LazyColumn( LazyColumn(
modifier = Modifier.fillMaxSize() modifier = Modifier
.fillMaxSize()
.nestedScroll(nestedScrollConnection), .nestedScroll(nestedScrollConnection),
) { ) {
itemsIndexed(tunnels.toList()) { index, tunnel -> itemsIndexed(tunnels.toList()) { index, tunnel ->
+1 -1
View File
@@ -13,7 +13,7 @@ buildscript {
} }
plugins { plugins {
id("com.android.application") version "8.2.0-beta01" apply false id("com.android.application") version "8.2.0-beta03" 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