fix: stop tunnel regression
Fixes regression where tunnel is stuck in on state after x amount of toggles. Closes #163 Adds obfuscation of potentially sensitive data from logs. Closes #160 Adds hiding of FAB on scroll to allow users to toggle tunnels when they have many tunnel configs. Closes #161
This commit is contained in:
parent
a2b8eb5b0b
commit
5447ec73f7
|
@ -97,7 +97,7 @@ jobs:
|
||||||
|
|
||||||
- name: Get checksum
|
- name: Get checksum
|
||||||
id: checksum
|
id: checksum
|
||||||
run: echo "checksum=$(apksigner verify -print-certs ${{ steps.apk-path.outputs.path }} | grep -Po "(?<=SHA-256 digest:) .*")" | awk '{$1=$1};1' >> $GITHUB_OUTPUT
|
run: echo "checksum=$(apksigner verify -print-certs ${{ steps.apk-path.outputs.path }} | grep -Po "(?<=SHA-256 digest:) .*" | tr -d "[:blank:]")" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Append checksum
|
- name: Append checksum
|
||||||
id: append_checksum
|
id: append_checksum
|
||||||
|
@ -105,8 +105,9 @@ jobs:
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
body: >
|
body: |
|
||||||
<br /> SHA256 fingerprint: <br /> ```${{ steps.checksum.outputs.checksum }}```
|
SHA256 fingerprint:
|
||||||
|
```${{ steps.checksum.outputs.checksum }}```
|
||||||
tag_name: ${{ github.ref_name }}
|
tag_name: ${{ github.ref_name }}
|
||||||
name: ${{ github.ref_name }}
|
name: ${{ github.ref_name }}
|
||||||
draft: false
|
draft: false
|
||||||
|
|
|
@ -103,7 +103,7 @@ jobs:
|
||||||
|
|
||||||
- name: Get checksum
|
- name: Get checksum
|
||||||
id: checksum
|
id: checksum
|
||||||
run: echo "checksum=$(apksigner verify -print-certs ${{ steps.apk-path.outputs.path }} | grep -Po "(?<=SHA-256 digest:) .*")" | awk '{$1=$1};1' >> $GITHUB_OUTPUT
|
run: echo "checksum=$(apksigner verify -print-certs ${{ steps.apk-path.outputs.path }} | grep -Po "(?<=SHA-256 digest:) .*" | tr -d "[:blank:]")" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Append checksum
|
- name: Append checksum
|
||||||
id: append_checksum
|
id: append_checksum
|
||||||
|
@ -111,8 +111,9 @@ jobs:
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
body: >
|
body: |
|
||||||
<br /> SHA256 fingerprint: <br /> ```${{ steps.checksum.outputs.checksum }}```
|
SHA256 fingerprint:
|
||||||
|
```${{ steps.checksum.outputs.checksum }}```
|
||||||
tag_name: ${{ github.ref_name }}
|
tag_name: ${{ github.ref_name }}
|
||||||
name: ${{ github.ref_name }}
|
name: ${{ github.ref_name }}
|
||||||
draft: false
|
draft: false
|
||||||
|
|
|
@ -24,7 +24,7 @@ class NotificationActionReceiver : BroadcastReceiver() {
|
||||||
override fun onReceive(context: Context, intent: Intent?) = goAsync {
|
override fun onReceive(context: Context, intent: Intent?) = goAsync {
|
||||||
try {
|
try {
|
||||||
//TODO fix for manual start changes when enabled
|
//TODO fix for manual start changes when enabled
|
||||||
serviceManager.stopVpnService(context)
|
serviceManager.stopVpnServiceForeground(context)
|
||||||
delay(Constants.TOGGLE_TUNNEL_DELAY)
|
delay(Constants.TOGGLE_TUNNEL_DELAY)
|
||||||
serviceManager.startVpnServiceForeground(context)
|
serviceManager.startVpnServiceForeground(context)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
|
|
@ -3,5 +3,6 @@ package com.zaneschepke.wireguardautotunnel.service.foreground
|
||||||
enum class Action {
|
enum class Action {
|
||||||
START,
|
START,
|
||||||
START_FOREGROUND,
|
START_FOREGROUND,
|
||||||
STOP
|
STOP,
|
||||||
|
STOP_FOREGROUND
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ open class ForegroundService : LifecycleService() {
|
||||||
when (action) {
|
when (action) {
|
||||||
Action.START.name,
|
Action.START.name,
|
||||||
Action.START_FOREGROUND.name -> startService(intent.extras)
|
Action.START_FOREGROUND.name -> startService(intent.extras)
|
||||||
|
Action.STOP.name, Action.STOP_FOREGROUND.name -> stopService()
|
||||||
Constants.ALWAYS_ON_VPN_ACTION -> {
|
Constants.ALWAYS_ON_VPN_ACTION -> {
|
||||||
Timber.i("Always-on VPN starting service")
|
Timber.i("Always-on VPN starting service")
|
||||||
startService(intent.extras)
|
startService(intent.extras)
|
||||||
|
@ -37,16 +37,9 @@ open class ForegroundService : LifecycleService() {
|
||||||
"with a null intent. It has been probably restarted by the system.",
|
"with a null intent. It has been probably restarted by the system.",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// by returning this we make sure the service is restarted if the system kills the service
|
|
||||||
return START_STICKY
|
return START_STICKY
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
super.onDestroy()
|
|
||||||
Timber.d("The service has been destroyed")
|
|
||||||
stopService()
|
|
||||||
}
|
|
||||||
|
|
||||||
protected open fun startService(extras: Bundle?) {
|
protected open fun startService(extras: Bundle?) {
|
||||||
if (isServiceStarted) return
|
if (isServiceStarted) return
|
||||||
Timber.d("Starting ${this.javaClass.simpleName}")
|
Timber.d("Starting ${this.javaClass.simpleName}")
|
||||||
|
@ -55,12 +48,8 @@ open class ForegroundService : LifecycleService() {
|
||||||
|
|
||||||
protected open fun stopService() {
|
protected open fun stopService() {
|
||||||
Timber.d("Stopping ${this.javaClass.simpleName}")
|
Timber.d("Stopping ${this.javaClass.simpleName}")
|
||||||
try {
|
stopForeground(STOP_FOREGROUND_REMOVE)
|
||||||
stopForeground(STOP_FOREGROUND_REMOVE)
|
stopSelf()
|
||||||
stopSelf()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Timber.e(e)
|
|
||||||
}
|
|
||||||
isServiceStarted = false
|
isServiceStarted = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,9 +23,8 @@ class ServiceManager(private val appDataRepository: AppDataRepository) {
|
||||||
intent.component?.javaClass
|
intent.component?.javaClass
|
||||||
try {
|
try {
|
||||||
when (action) {
|
when (action) {
|
||||||
Action.START_FOREGROUND -> context.startForegroundService(intent)
|
Action.START_FOREGROUND, Action.STOP_FOREGROUND -> context.startForegroundService(intent)
|
||||||
Action.START -> context.startService(intent)
|
Action.START, Action.STOP -> context.startService(intent)
|
||||||
Action.STOP -> context.stopService(intent)
|
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e.message)
|
Timber.e(e.message)
|
||||||
|
@ -46,6 +45,16 @@ class ServiceManager(private val appDataRepository: AppDataRepository) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun stopVpnServiceForeground(context: Context, isManualStop: Boolean = false) {
|
||||||
|
if (isManualStop) onManualStop()
|
||||||
|
Timber.i("Stopping vpn service")
|
||||||
|
actionOnService(
|
||||||
|
Action.STOP_FOREGROUND,
|
||||||
|
context,
|
||||||
|
WireGuardTunnelService::class.java,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun stopVpnService(context: Context, isManualStop: Boolean = false) {
|
suspend fun stopVpnService(context: Context, isManualStop: Boolean = false) {
|
||||||
if (isManualStop) onManualStop()
|
if (isManualStop) onManualStop()
|
||||||
Timber.i("Stopping vpn service")
|
Timber.i("Stopping vpn service")
|
||||||
|
|
|
@ -18,6 +18,7 @@ import com.zaneschepke.wireguardautotunnel.service.notification.NotificationServ
|
||||||
import com.zaneschepke.wireguardautotunnel.service.tunnel.VpnService
|
import com.zaneschepke.wireguardautotunnel.service.tunnel.VpnService
|
||||||
import com.zaneschepke.wireguardautotunnel.util.Constants
|
import com.zaneschepke.wireguardautotunnel.util.Constants
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
@ -56,7 +57,7 @@ class WireGuardConnectivityWatcherService : ForegroundService() {
|
||||||
|
|
||||||
private val networkEventsFlow = MutableStateFlow(WatcherState())
|
private val networkEventsFlow = MutableStateFlow(WatcherState())
|
||||||
|
|
||||||
private lateinit var watcherJob: Job
|
private var watcherJob: Job? = null
|
||||||
|
|
||||||
private var wakeLock: PowerManager.WakeLock? = null
|
private var wakeLock: PowerManager.WakeLock? = null
|
||||||
private val tag = this.javaClass.name
|
private val tag = this.javaClass.name
|
||||||
|
@ -74,11 +75,6 @@ class WireGuardConnectivityWatcherService : ForegroundService() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
super.onDestroy()
|
|
||||||
stopService()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun startService(extras: Bundle?) {
|
override fun startService(extras: Bundle?) {
|
||||||
super.startService(extras)
|
super.startService(extras)
|
||||||
try {
|
try {
|
||||||
|
@ -139,8 +135,10 @@ class WireGuardConnectivityWatcherService : ForegroundService() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun cancelWatcherJob() {
|
private fun cancelWatcherJob() {
|
||||||
if (this::watcherJob.isInitialized) {
|
try {
|
||||||
watcherJob.cancel()
|
watcherJob?.cancel()
|
||||||
|
} catch (e : CancellationException) {
|
||||||
|
Timber.i("Watcher job cancelled")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,7 +234,7 @@ class WireGuardConnectivityWatcherService : ForegroundService() {
|
||||||
}
|
}
|
||||||
if (results.contains(false)) {
|
if (results.contains(false)) {
|
||||||
Timber.i("Restarting VPN for ping failure")
|
Timber.i("Restarting VPN for ping failure")
|
||||||
serviceManager.stopVpnService(this)
|
serviceManager.stopVpnServiceForeground(this)
|
||||||
delay(Constants.VPN_RESTART_DELAY)
|
delay(Constants.VPN_RESTART_DELAY)
|
||||||
serviceManager.startVpnServiceForeground(this)
|
serviceManager.startVpnServiceForeground(this)
|
||||||
delay(Constants.PING_COOLDOWN)
|
delay(Constants.PING_COOLDOWN)
|
||||||
|
@ -324,7 +322,9 @@ class WireGuardConnectivityWatcherService : ForegroundService() {
|
||||||
)
|
)
|
||||||
val ssid = wifiService.getNetworkName(it.networkCapabilities)
|
val ssid = wifiService.getNetworkName(it.networkCapabilities)
|
||||||
ssid?.let {
|
ssid?.let {
|
||||||
Timber.i("Detected SSID: $ssid")
|
if(it.contains(Constants.UNREADABLE_SSID)) {
|
||||||
|
Timber.w("SSID unreadable: missing permissions")
|
||||||
|
} else Timber.i("Detected valid SSID")
|
||||||
appDataRepository.appState.setCurrentSsid(ssid)
|
appDataRepository.appState.setCurrentSsid(ssid)
|
||||||
networkEventsFlow.value =
|
networkEventsFlow.value =
|
||||||
networkEventsFlow.value.copy(
|
networkEventsFlow.value.copy(
|
||||||
|
@ -381,7 +381,7 @@ class WireGuardConnectivityWatcherService : ForegroundService() {
|
||||||
|
|
||||||
watcherState.isTunnelOffOnMobileDataConditionMet() -> {
|
watcherState.isTunnelOffOnMobileDataConditionMet() -> {
|
||||||
Timber.i("$autoTunnel - tunnel off on mobile data met, turning vpn off")
|
Timber.i("$autoTunnel - tunnel off on mobile data met, turning vpn off")
|
||||||
serviceManager.stopVpnService(this)
|
serviceManager.stopVpnServiceForeground(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
watcherState.isTunnelNotWifiNamePreferredMet(watcherState.currentNetworkSSID) -> {
|
watcherState.isTunnelNotWifiNamePreferredMet(watcherState.currentNetworkSSID) -> {
|
||||||
|
@ -407,17 +407,17 @@ class WireGuardConnectivityWatcherService : ForegroundService() {
|
||||||
|
|
||||||
watcherState.isTrustedWifiConditionMet() -> {
|
watcherState.isTrustedWifiConditionMet() -> {
|
||||||
Timber.i("$autoTunnel - tunnel off on trusted wifi condition met, turning vpn off")
|
Timber.i("$autoTunnel - tunnel off on trusted wifi condition met, turning vpn off")
|
||||||
serviceManager.stopVpnService(this)
|
serviceManager.stopVpnServiceForeground(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
watcherState.isTunnelOffOnWifiConditionMet() -> {
|
watcherState.isTunnelOffOnWifiConditionMet() -> {
|
||||||
Timber.i("$autoTunnel - tunnel off on wifi condition met, turning vpn off")
|
Timber.i("$autoTunnel - tunnel off on wifi condition met, turning vpn off")
|
||||||
serviceManager.stopVpnService(this)
|
serviceManager.stopVpnServiceForeground(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
watcherState.isTunnelOffOnNoConnectivityMet() -> {
|
watcherState.isTunnelOffOnNoConnectivityMet() -> {
|
||||||
Timber.i("$autoTunnel - tunnel off on no connectivity met, turning vpn off")
|
Timber.i("$autoTunnel - tunnel off on no connectivity met, turning vpn off")
|
||||||
serviceManager.stopVpnService(this)
|
serviceManager.stopVpnServiceForeground(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
|
|
|
@ -16,10 +16,12 @@ import com.zaneschepke.wireguardautotunnel.util.Constants
|
||||||
import com.zaneschepke.wireguardautotunnel.util.handshakeStatus
|
import com.zaneschepke.wireguardautotunnel.util.handshakeStatus
|
||||||
import com.zaneschepke.wireguardautotunnel.util.mapPeerStats
|
import com.zaneschepke.wireguardautotunnel.util.mapPeerStats
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
|
@ -35,7 +37,7 @@ class WireGuardTunnelService : ForegroundService() {
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var notificationService: NotificationService
|
lateinit var notificationService: NotificationService
|
||||||
|
|
||||||
private lateinit var job: Job
|
private var job: Job? = null
|
||||||
|
|
||||||
private var didShowConnected = false
|
private var didShowConnected = false
|
||||||
|
|
||||||
|
@ -49,11 +51,6 @@ class WireGuardTunnelService : ForegroundService() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
super.onDestroy()
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun startService(extras: Bundle?) {
|
override fun startService(extras: Bundle?) {
|
||||||
super.startService(extras)
|
super.startService(extras)
|
||||||
cancelJob()
|
cancelJob()
|
||||||
|
@ -182,8 +179,10 @@ class WireGuardTunnelService : ForegroundService() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun cancelJob() {
|
private fun cancelJob() {
|
||||||
if (this::job.isInitialized) {
|
try {
|
||||||
job.cancel()
|
job?.cancel()
|
||||||
|
} catch (e : CancellationException) {
|
||||||
|
Timber.i("Tunnel job cancelled")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ class ShortcutsActivity : ComponentActivity() {
|
||||||
this@ShortcutsActivity, tunnelConfig?.id, isManualStart = true,
|
this@ShortcutsActivity, tunnelConfig?.id, isManualStart = true,
|
||||||
)
|
)
|
||||||
|
|
||||||
Action.STOP.name -> serviceManager.stopVpnService(
|
Action.STOP.name -> serviceManager.stopVpnServiceForeground(
|
||||||
this@ShortcutsActivity,
|
this@ShortcutsActivity,
|
||||||
isManualStop = true,
|
isManualStop = true,
|
||||||
)
|
)
|
||||||
|
|
|
@ -80,7 +80,7 @@ class TunnelControlTile : TileService() {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
try {
|
try {
|
||||||
if (vpnService.getState() == Tunnel.State.UP) {
|
if (vpnService.getState() == Tunnel.State.UP) {
|
||||||
serviceManager.stopVpnService(
|
serviceManager.stopVpnServiceForeground(
|
||||||
this@TunnelControlTile,
|
this@TunnelControlTile,
|
||||||
isManualStop = true,
|
isManualStop = true,
|
||||||
)
|
)
|
||||||
|
|
|
@ -10,6 +10,7 @@ import com.zaneschepke.wireguardautotunnel.data.repository.AppDataRepository
|
||||||
import com.zaneschepke.wireguardautotunnel.module.Kernel
|
import com.zaneschepke.wireguardautotunnel.module.Kernel
|
||||||
import com.zaneschepke.wireguardautotunnel.module.Userspace
|
import com.zaneschepke.wireguardautotunnel.module.Userspace
|
||||||
import com.zaneschepke.wireguardautotunnel.util.Constants
|
import com.zaneschepke.wireguardautotunnel.util.Constants
|
||||||
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
|
@ -33,7 +34,7 @@ constructor(
|
||||||
|
|
||||||
private val scope = CoroutineScope(Dispatchers.IO)
|
private val scope = CoroutineScope(Dispatchers.IO)
|
||||||
|
|
||||||
private lateinit var statsJob: Job
|
private var statsJob: Job? = null
|
||||||
|
|
||||||
private var backend: Backend = userspaceBackend
|
private var backend: Backend = userspaceBackend
|
||||||
|
|
||||||
|
@ -134,8 +135,10 @@ constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (state == State.DOWN) {
|
if (state == State.DOWN) {
|
||||||
if (this::statsJob.isInitialized) {
|
try {
|
||||||
statsJob.cancel()
|
statsJob?.cancel()
|
||||||
|
} catch (e : CancellationException) {
|
||||||
|
Timber.i("Stats job cancelled")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,12 +118,12 @@ constructor(
|
||||||
fun readLogCatOutput() =
|
fun readLogCatOutput() =
|
||||||
viewModelScope.launch(viewModelScope.coroutineContext + Dispatchers.IO) {
|
viewModelScope.launch(viewModelScope.coroutineContext + Dispatchers.IO) {
|
||||||
launch {
|
launch {
|
||||||
Logcatter.logs {
|
Logcatter.logs(callback = {
|
||||||
logs.add(it)
|
logs.add(it)
|
||||||
if (logs.size > Constants.LOG_BUFFER_SIZE) {
|
if (logs.size > Constants.LOG_BUFFER_SIZE) {
|
||||||
logs.removeRange(0, (logs.size - Constants.LOG_BUFFER_SIZE).toInt())
|
logs.removeRange(0, (logs.size - Constants.LOG_BUFFER_SIZE).toInt())
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -71,8 +71,12 @@ 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
|
||||||
import androidx.compose.ui.focus.onFocusChanged
|
import androidx.compose.ui.focus.onFocusChanged
|
||||||
|
import androidx.compose.ui.geometry.Offset
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||||
|
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
|
||||||
|
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
|
||||||
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import androidx.compose.ui.input.pointer.pointerInput
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||||
|
@ -124,6 +128,25 @@ fun MainScreen(
|
||||||
val sheetState = rememberModalBottomSheetState()
|
val sheetState = rememberModalBottomSheetState()
|
||||||
var showBottomSheet by remember { mutableStateOf(false) }
|
var showBottomSheet by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
// Nested scroll for control FAB
|
||||||
|
val nestedScrollConnection = remember {
|
||||||
|
object : NestedScrollConnection {
|
||||||
|
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
|
||||||
|
// Hide FAB
|
||||||
|
if (available.y < -1) {
|
||||||
|
isVisible.value = false
|
||||||
|
}
|
||||||
|
// Show FAB
|
||||||
|
if (available.y > 1) {
|
||||||
|
isVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return Offset.Zero
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
var showDeleteTunnelAlertDialog by remember { mutableStateOf(false) }
|
var showDeleteTunnelAlertDialog by remember { mutableStateOf(false) }
|
||||||
var selectedTunnel by remember { mutableStateOf<TunnelConfig?>(null) }
|
var selectedTunnel by remember { mutableStateOf<TunnelConfig?>(null) }
|
||||||
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||||
|
@ -377,8 +400,8 @@ fun MainScreen(
|
||||||
verticalArrangement = Arrangement.Top,
|
verticalArrangement = Arrangement.Top,
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxSize()
|
||||||
.overscroll(ScrollableDefaults.overscrollEffect()),
|
.overscroll(ScrollableDefaults.overscrollEffect()).nestedScroll(nestedScrollConnection),
|
||||||
state = rememberLazyListState(0, uiState.tunnels.count()),
|
state = rememberLazyListState(0, uiState.tunnels.count()),
|
||||||
userScrollEnabled = true,
|
userScrollEnabled = true,
|
||||||
reverseLayout = false,
|
reverseLayout = false,
|
||||||
|
|
|
@ -35,4 +35,6 @@ object Constants {
|
||||||
|
|
||||||
const val TUNNEL_EXTRA_KEY = "tunnelId"
|
const val TUNNEL_EXTRA_KEY = "tunnelId"
|
||||||
|
|
||||||
|
const val UNREADABLE_SSID = "<unknown ssid>"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
object Constants {
|
object Constants {
|
||||||
const val VERSION_NAME = "3.4.1"
|
const val VERSION_NAME = "3.4.2"
|
||||||
const val JVM_TARGET = "17"
|
const val JVM_TARGET = "17"
|
||||||
const val VERSION_CODE = 34100
|
const val VERSION_CODE = 34200
|
||||||
const val TARGET_SDK = 34
|
const val TARGET_SDK = 34
|
||||||
const val MIN_SDK = 26
|
const val MIN_SDK = 26
|
||||||
const val APP_ID = "com.zaneschepke.wireguardautotunnel"
|
const val APP_ID = "com.zaneschepke.wireguardautotunnel"
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
What's new:
|
||||||
|
- Fix stop tunnel regression
|
||||||
|
- Add logs obfuscation
|
||||||
|
- Add hide FAB on scroll
|
|
@ -20,7 +20,7 @@ pinLockCompose = "1.0.3"
|
||||||
roomVersion = "2.6.1"
|
roomVersion = "2.6.1"
|
||||||
timber = "5.0.1"
|
timber = "5.0.1"
|
||||||
tunnel = "1.0.20230706"
|
tunnel = "1.0.20230706"
|
||||||
androidGradlePlugin = "8.4.0-rc01"
|
androidGradlePlugin = "8.4.0-rc02"
|
||||||
kotlin = "1.9.23"
|
kotlin = "1.9.23"
|
||||||
ksp = "1.9.23-1.0.19"
|
ksp = "1.9.23-1.0.19"
|
||||||
composeBom = "2024.03.00"
|
composeBom = "2024.03.00"
|
||||||
|
|
|
@ -3,14 +3,29 @@ package com.zaneschepke.logcatter
|
||||||
import com.zaneschepke.logcatter.model.LogMessage
|
import com.zaneschepke.logcatter.model.LogMessage
|
||||||
|
|
||||||
object Logcatter {
|
object Logcatter {
|
||||||
fun logs(callback: (input: LogMessage) -> Unit) {
|
|
||||||
|
private val findKeyRegex = """[A-Za-z0-9+/]{42}[AEIMQUYcgkosw480]=""".toRegex()
|
||||||
|
private val findIpv6AddressRegex = """(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))""".toRegex()
|
||||||
|
private val findIpv4AddressRegex = """((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}""".toRegex()
|
||||||
|
private val findTunnelNameRegex = """(?<=tunnel ).*?(?= UP| DOWN)""".toRegex()
|
||||||
|
|
||||||
|
|
||||||
|
fun logs(callback: (input: LogMessage) -> Unit, obfuscator: (log : String) -> String = { log -> this.obfuscator(log)}){
|
||||||
clear()
|
clear()
|
||||||
Runtime.getRuntime().exec("logcat -v epoch")
|
Runtime.getRuntime().exec("logcat -v epoch")
|
||||||
.inputStream
|
.inputStream
|
||||||
.bufferedReader()
|
.bufferedReader()
|
||||||
.useLines { lines ->
|
.useLines { lines ->
|
||||||
lines.forEach { callback(LogMessage.from(it)) }
|
lines.forEach { callback(LogMessage.from(obfuscator(it))) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun obfuscator(log : String) : String {
|
||||||
|
return findKeyRegex.replace(log, "<crypto-key>").let { first ->
|
||||||
|
findIpv6AddressRegex.replace(first, "<ipv6-address>").let { second ->
|
||||||
|
findTunnelNameRegex.replace(second, "<tunnel>")
|
||||||
}
|
}
|
||||||
|
}.let{ last -> findIpv4AddressRegex.replace(last,"<ipv4-address>") }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clear() {
|
fun clear() {
|
||||||
|
|
Loading…
Reference in New Issue