mirror of
https://github.com/wgtunnel/android.git
synced 2026-07-03 14:07:49 +02:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e15a7c5627 |
@@ -167,16 +167,6 @@
|
|||||||
android:stopWithTask="false"
|
android:stopWithTask="false"
|
||||||
tools:node="merge" />
|
tools:node="merge" />
|
||||||
|
|
||||||
<service
|
|
||||||
android:name=".service.foreground.TunnelBackgroundService"
|
|
||||||
android:exported="false"
|
|
||||||
android:foregroundServiceType="systemExempted"
|
|
||||||
android:permission="android.permission.BIND_VPN_SERVICE">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.net.VpnService" />
|
|
||||||
</intent-filter>
|
|
||||||
</service>
|
|
||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".receiver.BootReceiver"
|
android:name=".receiver.BootReceiver"
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
@@ -194,13 +184,6 @@
|
|||||||
android:name=".receiver.BackgroundActionReceiver"
|
android:name=".receiver.BackgroundActionReceiver"
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
android:exported="false"/>
|
android:exported="false"/>
|
||||||
<receiver
|
|
||||||
android:name=".receiver.AppUpdateReceiver"
|
|
||||||
android:exported="false">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
|
|
||||||
</intent-filter>
|
|
||||||
</receiver>
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".receiver.KernelReceiver"
|
android:name=".receiver.KernelReceiver"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
|
|||||||
@@ -3,15 +3,10 @@ package com.zaneschepke.wireguardautotunnel
|
|||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.os.StrictMode
|
import android.os.StrictMode
|
||||||
import android.os.StrictMode.ThreadPolicy
|
import android.os.StrictMode.ThreadPolicy
|
||||||
import com.zaneschepke.logcatter.LocalLogCollector
|
|
||||||
import com.zaneschepke.wireguardautotunnel.module.ApplicationScope
|
import com.zaneschepke.wireguardautotunnel.module.ApplicationScope
|
||||||
import com.zaneschepke.wireguardautotunnel.module.IoDispatcher
|
|
||||||
import com.zaneschepke.wireguardautotunnel.util.ReleaseTree
|
import com.zaneschepke.wireguardautotunnel.util.ReleaseTree
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.isRunningOnTv
|
|
||||||
import dagger.hilt.android.HiltAndroidApp
|
import dagger.hilt.android.HiltAndroidApp
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@@ -22,13 +17,6 @@ class WireGuardAutoTunnel : Application() {
|
|||||||
@ApplicationScope
|
@ApplicationScope
|
||||||
lateinit var applicationScope: CoroutineScope
|
lateinit var applicationScope: CoroutineScope
|
||||||
|
|
||||||
@Inject
|
|
||||||
lateinit var localLogCollector: LocalLogCollector
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
@IoDispatcher
|
|
||||||
lateinit var ioDispatcher: CoroutineDispatcher
|
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
instance = this
|
instance = this
|
||||||
@@ -45,11 +33,6 @@ class WireGuardAutoTunnel : Application() {
|
|||||||
} else {
|
} else {
|
||||||
Timber.plant(ReleaseTree())
|
Timber.plant(ReleaseTree())
|
||||||
}
|
}
|
||||||
if (!isRunningOnTv()) {
|
|
||||||
applicationScope.launch(ioDispatcher) {
|
|
||||||
localLogCollector.start()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.receiver
|
|
||||||
|
|
||||||
import android.content.BroadcastReceiver
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import com.zaneschepke.wireguardautotunnel.data.repository.AppDataRepository
|
|
||||||
import com.zaneschepke.wireguardautotunnel.module.ApplicationScope
|
|
||||||
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager
|
|
||||||
import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelService
|
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.startTunnelBackground
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import timber.log.Timber
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@AndroidEntryPoint
|
|
||||||
class AppUpdateReceiver : BroadcastReceiver() {
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
@ApplicationScope
|
|
||||||
lateinit var applicationScope: CoroutineScope
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
lateinit var appDataRepository: AppDataRepository
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
lateinit var serviceManager: ServiceManager
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
lateinit var tunnelService: TunnelService
|
|
||||||
|
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
|
||||||
if (intent.action != Intent.ACTION_MY_PACKAGE_REPLACED) return
|
|
||||||
applicationScope.launch {
|
|
||||||
val settings = appDataRepository.settings.getSettings()
|
|
||||||
if (settings.isAutoTunnelEnabled) {
|
|
||||||
Timber.i("Restarting services after upgrade")
|
|
||||||
serviceManager.startWatcherServiceForeground(context)
|
|
||||||
}
|
|
||||||
if (!settings.isAutoTunnelEnabled || settings.isAutoTunnelPaused) {
|
|
||||||
val tunnels = appDataRepository.tunnels.getAll().filter { it.isActive }
|
|
||||||
if (tunnels.isNotEmpty()) context.startTunnelBackground(tunnels.first().id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-8
@@ -5,12 +5,10 @@ import android.content.Context
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import com.zaneschepke.wireguardautotunnel.data.repository.TunnelConfigRepository
|
import com.zaneschepke.wireguardautotunnel.data.repository.TunnelConfigRepository
|
||||||
import com.zaneschepke.wireguardautotunnel.module.ApplicationScope
|
import com.zaneschepke.wireguardautotunnel.module.ApplicationScope
|
||||||
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager
|
|
||||||
import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelService
|
import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelService
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import timber.log.Timber
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Provider
|
import javax.inject.Provider
|
||||||
|
|
||||||
@@ -27,19 +25,14 @@ class BackgroundActionReceiver : BroadcastReceiver() {
|
|||||||
@Inject
|
@Inject
|
||||||
lateinit var tunnelConfigRepository: TunnelConfigRepository
|
lateinit var tunnelConfigRepository: TunnelConfigRepository
|
||||||
|
|
||||||
@Inject
|
|
||||||
lateinit var serviceManager: ServiceManager
|
|
||||||
|
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
val id = intent.getIntExtra(TUNNEL_ID_EXTRA_KEY, 0)
|
val id = intent.getIntExtra(TUNNEL_ID_EXTRA_KEY, 0)
|
||||||
if (id == 0) return
|
if (id == 0) return
|
||||||
when (intent.action) {
|
when (intent.action) {
|
||||||
ACTION_CONNECT -> {
|
ACTION_CONNECT -> {
|
||||||
Timber.d("Connect actions")
|
|
||||||
applicationScope.launch {
|
applicationScope.launch {
|
||||||
val tunnel = tunnelConfigRepository.getById(id)
|
val tunnel = tunnelConfigRepository.getById(id)
|
||||||
tunnel?.let {
|
tunnel?.let {
|
||||||
serviceManager.startTunnelBackgroundService(context)
|
|
||||||
tunnelService.get().startTunnel(it)
|
tunnelService.get().startTunnel(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -48,7 +41,6 @@ class BackgroundActionReceiver : BroadcastReceiver() {
|
|||||||
applicationScope.launch {
|
applicationScope.launch {
|
||||||
val tunnel = tunnelConfigRepository.getById(id)
|
val tunnel = tunnelConfigRepository.getById(id)
|
||||||
tunnel?.let {
|
tunnel?.let {
|
||||||
serviceManager.stopTunnelBackgroundService(context)
|
|
||||||
tunnelService.get().stopTunnel(it)
|
tunnelService.get().stopTunnel(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import com.zaneschepke.wireguardautotunnel.data.repository.AppDataRepository
|
|||||||
import com.zaneschepke.wireguardautotunnel.module.ApplicationScope
|
import com.zaneschepke.wireguardautotunnel.module.ApplicationScope
|
||||||
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager
|
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager
|
||||||
import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelService
|
import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelService
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.startTunnelBackground
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -36,7 +35,7 @@ class BootReceiver : BroadcastReceiver() {
|
|||||||
val settings = appDataRepository.settings.getSettings()
|
val settings = appDataRepository.settings.getSettings()
|
||||||
if (settings.isRestoreOnBootEnabled) {
|
if (settings.isRestoreOnBootEnabled) {
|
||||||
appDataRepository.getStartTunnelConfig()?.let {
|
appDataRepository.getStartTunnelConfig()?.let {
|
||||||
context.startTunnelBackground(it.id)
|
tunnelService.get().startTunnel(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (settings.isAutoTunnelEnabled) {
|
if (settings.isAutoTunnelEnabled) {
|
||||||
|
|||||||
-16
@@ -50,20 +50,4 @@ class ServiceManager {
|
|||||||
AutoTunnelService::class.java,
|
AutoTunnelService::class.java,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startTunnelBackgroundService(context: Context) {
|
|
||||||
actionOnService(
|
|
||||||
Action.START_FOREGROUND,
|
|
||||||
context,
|
|
||||||
TunnelBackgroundService::class.java,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun stopTunnelBackgroundService(context: Context) {
|
|
||||||
actionOnService(
|
|
||||||
Action.STOP,
|
|
||||||
context,
|
|
||||||
TunnelBackgroundService::class.java,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
-41
@@ -1,41 +0,0 @@
|
|||||||
package com.zaneschepke.wireguardautotunnel.service.foreground
|
|
||||||
|
|
||||||
import android.app.Notification
|
|
||||||
import android.os.Bundle
|
|
||||||
import com.zaneschepke.wireguardautotunnel.R
|
|
||||||
import com.zaneschepke.wireguardautotunnel.service.notification.NotificationService
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@AndroidEntryPoint
|
|
||||||
class TunnelBackgroundService : ForegroundService() {
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
lateinit var notificationService: NotificationService
|
|
||||||
|
|
||||||
private val foregroundId = 123
|
|
||||||
|
|
||||||
override fun onCreate() {
|
|
||||||
super.onCreate()
|
|
||||||
startForeground(foregroundId, createNotification())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun startService(extras: Bundle?) {
|
|
||||||
super.startService(extras)
|
|
||||||
startForeground(foregroundId, createNotification())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun stopService() {
|
|
||||||
super.stopService()
|
|
||||||
stopForeground(STOP_FOREGROUND_REMOVE)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createNotification(): Notification {
|
|
||||||
return notificationService.createNotification(
|
|
||||||
getString(R.string.vpn_channel_id),
|
|
||||||
getString(R.string.vpn_channel_name),
|
|
||||||
getString(R.string.tunnel_start_text),
|
|
||||||
description = "",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+2
-4
@@ -7,8 +7,6 @@ import com.zaneschepke.wireguardautotunnel.module.ApplicationScope
|
|||||||
import com.zaneschepke.wireguardautotunnel.service.foreground.Action
|
import com.zaneschepke.wireguardautotunnel.service.foreground.Action
|
||||||
import com.zaneschepke.wireguardautotunnel.service.foreground.AutoTunnelService
|
import com.zaneschepke.wireguardautotunnel.service.foreground.AutoTunnelService
|
||||||
import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelService
|
import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelService
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.startTunnelBackground
|
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.stopTunnelBackground
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -44,8 +42,8 @@ class ShortcutsActivity : ComponentActivity() {
|
|||||||
Timber.d("Shortcut action on name: ${tunnelConfig?.name}")
|
Timber.d("Shortcut action on name: ${tunnelConfig?.name}")
|
||||||
tunnelConfig?.let {
|
tunnelConfig?.let {
|
||||||
when (intent.action) {
|
when (intent.action) {
|
||||||
Action.START.name -> this@ShortcutsActivity.startTunnelBackground(it.id)
|
Action.START.name -> tunnelService.get().startTunnel(it)
|
||||||
Action.STOP.name -> this@ShortcutsActivity.stopTunnelBackground(it.id)
|
Action.STOP.name -> tunnelService.get().stopTunnel(it)
|
||||||
else -> Unit
|
else -> Unit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
-1
@@ -68,7 +68,6 @@ class TunnelControlTile : TileService(), LifecycleOwner {
|
|||||||
override fun onClick() {
|
override fun onClick() {
|
||||||
super.onClick()
|
super.onClick()
|
||||||
unlockAndRun {
|
unlockAndRun {
|
||||||
Timber.d("Click")
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
val context = this@TunnelControlTile
|
val context = this@TunnelControlTile
|
||||||
val lastActive = appDataRepository.getStartTunnelConfig()
|
val lastActive = appDataRepository.getStartTunnelConfig()
|
||||||
|
|||||||
@@ -14,7 +14,4 @@ interface TunnelService : Tunnel, org.amnezia.awg.backend.Tunnel {
|
|||||||
suspend fun runningTunnelNames(): Set<String>
|
suspend fun runningTunnelNames(): Set<String>
|
||||||
|
|
||||||
suspend fun getState(): TunnelState
|
suspend fun getState(): TunnelState
|
||||||
|
|
||||||
fun cancelStatsJob()
|
|
||||||
fun startStatsJob()
|
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-22
@@ -14,6 +14,7 @@ import com.zaneschepke.wireguardautotunnel.service.tunnel.statistics.TunnelStati
|
|||||||
import com.zaneschepke.wireguardautotunnel.service.tunnel.statistics.WireGuardStatistics
|
import com.zaneschepke.wireguardautotunnel.service.tunnel.statistics.WireGuardStatistics
|
||||||
import com.zaneschepke.wireguardautotunnel.util.Constants
|
import com.zaneschepke.wireguardautotunnel.util.Constants
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.requestTunnelTileServiceStateUpdate
|
import com.zaneschepke.wireguardautotunnel.util.extensions.requestTunnelTileServiceStateUpdate
|
||||||
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
@@ -55,17 +56,8 @@ constructor(
|
|||||||
return runCatching {
|
return runCatching {
|
||||||
when (val backend = backend()) {
|
when (val backend = backend()) {
|
||||||
is Backend -> backend.setState(this, tunnelState.toWgState(), TunnelConfig.configFromWgQuick(tunnelConfig.wgQuick)).let { TunnelState.from(it) }
|
is Backend -> backend.setState(this, tunnelState.toWgState(), TunnelConfig.configFromWgQuick(tunnelConfig.wgQuick)).let { TunnelState.from(it) }
|
||||||
is org.amnezia.awg.backend.Backend -> {
|
is org.amnezia.awg.backend.Backend -> backend.setState(this, tunnelState.toAmState(), TunnelConfig.configFromAmQuick(tunnelConfig.amQuick)).let {
|
||||||
val config = if (tunnelConfig.amQuick.isBlank()) {
|
TunnelState.from(it)
|
||||||
TunnelConfig.configFromAmQuick(
|
|
||||||
tunnelConfig.wgQuick,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
TunnelConfig.configFromAmQuick(tunnelConfig.amQuick)
|
|
||||||
}
|
|
||||||
backend.setState(this, tunnelState.toAmState(), config).let {
|
|
||||||
TunnelState.from(it)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else -> throw NotImplementedError()
|
else -> throw NotImplementedError()
|
||||||
}
|
}
|
||||||
@@ -152,14 +144,6 @@ constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun cancelStatsJob() {
|
|
||||||
statsJob?.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun startStatsJob() {
|
|
||||||
statsJob = startTunnelStatisticsJob()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getName(): String {
|
override fun getName(): String {
|
||||||
return _vpnState.value.tunnelConfig?.name ?: ""
|
return _vpnState.value.tunnelConfig?.name ?: ""
|
||||||
}
|
}
|
||||||
@@ -171,9 +155,15 @@ constructor(
|
|||||||
private fun handleStateChange(state: TunnelState) {
|
private fun handleStateChange(state: TunnelState) {
|
||||||
emitTunnelState(state)
|
emitTunnelState(state)
|
||||||
WireGuardAutoTunnel.instance.requestTunnelTileServiceStateUpdate()
|
WireGuardAutoTunnel.instance.requestTunnelTileServiceStateUpdate()
|
||||||
when (state) {
|
if (state == TunnelState.UP) {
|
||||||
TunnelState.UP -> startStatsJob()
|
statsJob = startTunnelStatisticsJob()
|
||||||
else -> cancelStatsJob()
|
}
|
||||||
|
if (state == TunnelState.DOWN) {
|
||||||
|
try {
|
||||||
|
statsJob?.cancel()
|
||||||
|
} catch (e: CancellationException) {
|
||||||
|
Timber.i("Stats job cancelled")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,9 +5,6 @@ import androidx.activity.SystemBarStyle
|
|||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.compose.animation.core.tween
|
|
||||||
import androidx.compose.animation.fadeIn
|
|
||||||
import androidx.compose.animation.fadeOut
|
|
||||||
import androidx.compose.foundation.focusable
|
import androidx.compose.foundation.focusable
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
@@ -18,7 +15,6 @@ import androidx.compose.material3.SnackbarDuration
|
|||||||
import androidx.compose.material3.SnackbarHost
|
import androidx.compose.material3.SnackbarHost
|
||||||
import androidx.compose.material3.SnackbarHostState
|
import androidx.compose.material3.SnackbarHostState
|
||||||
import androidx.compose.material3.SnackbarResult
|
import androidx.compose.material3.SnackbarResult
|
||||||
import androidx.compose.material3.Surface
|
|
||||||
import androidx.compose.material3.surfaceColorAtElevation
|
import androidx.compose.material3.surfaceColorAtElevation
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@@ -39,7 +35,8 @@ import androidx.navigation.compose.currentBackStackEntryAsState
|
|||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import androidx.navigation.navArgument
|
import androidx.navigation.navArgument
|
||||||
import com.zaneschepke.wireguardautotunnel.data.repository.AppStateRepository
|
import com.zaneschepke.wireguardautotunnel.data.repository.AppStateRepository
|
||||||
import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelService
|
import com.zaneschepke.wireguardautotunnel.data.repository.SettingsRepository
|
||||||
|
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.navigation.BottomNavBar
|
import com.zaneschepke.wireguardautotunnel.ui.common.navigation.BottomNavBar
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.common.prompt.CustomSnackBar
|
import com.zaneschepke.wireguardautotunnel.ui.common.prompt.CustomSnackBar
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.screens.config.ConfigScreen
|
import com.zaneschepke.wireguardautotunnel.ui.screens.config.ConfigScreen
|
||||||
@@ -51,7 +48,6 @@ import com.zaneschepke.wireguardautotunnel.ui.screens.settings.SettingsScreen
|
|||||||
import com.zaneschepke.wireguardautotunnel.ui.screens.support.SupportScreen
|
import com.zaneschepke.wireguardautotunnel.ui.screens.support.SupportScreen
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.screens.support.logs.LogsScreen
|
import com.zaneschepke.wireguardautotunnel.ui.screens.support.logs.LogsScreen
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.theme.WireguardAutoTunnelTheme
|
import com.zaneschepke.wireguardautotunnel.ui.theme.WireguardAutoTunnelTheme
|
||||||
import com.zaneschepke.wireguardautotunnel.util.Constants
|
|
||||||
import com.zaneschepke.wireguardautotunnel.util.StringValue
|
import com.zaneschepke.wireguardautotunnel.util.StringValue
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@@ -64,7 +60,10 @@ class MainActivity : AppCompatActivity() {
|
|||||||
lateinit var appStateRepository: AppStateRepository
|
lateinit var appStateRepository: AppStateRepository
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var tunnelService: TunnelService
|
lateinit var settingsRepository: SettingsRepository
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var serviceManager: ServiceManager
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@@ -73,6 +72,13 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
enableEdgeToEdge(navigationBarStyle = SystemBarStyle.dark(Color.Transparent.toArgb()))
|
enableEdgeToEdge(navigationBarStyle = SystemBarStyle.dark(Color.Transparent.toArgb()))
|
||||||
|
|
||||||
|
lifecycleScope.launch {
|
||||||
|
val settings = settingsRepository.getSettings()
|
||||||
|
if (settings.isAutoTunnelEnabled) {
|
||||||
|
serviceManager.startWatcherService(application.applicationContext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
val appViewModel = hiltViewModel<AppViewModel>()
|
val appViewModel = hiltViewModel<AppViewModel>()
|
||||||
val appUiState by appViewModel.appUiState.collectAsStateWithLifecycle()
|
val appUiState by appViewModel.appUiState.collectAsStateWithLifecycle()
|
||||||
@@ -121,7 +127,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
containerColor = MaterialTheme.colorScheme.background,
|
// TODO refactor
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.focusable()
|
.focusable()
|
||||||
@@ -142,97 +148,92 @@ class MainActivity : AppCompatActivity() {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
) { padding ->
|
) { padding ->
|
||||||
Surface(modifier = Modifier.fillMaxSize().padding(padding)) {
|
NavHost(
|
||||||
NavHost(
|
navController,
|
||||||
navController,
|
startDestination = (if (isPinLockEnabled == true) Screen.Lock.route else Screen.Main.route),
|
||||||
enterTransition = { fadeIn(tween(Constants.TRANSITION_ANIMATION_TIME)) },
|
modifier =
|
||||||
exitTransition = { fadeOut(tween(Constants.TRANSITION_ANIMATION_TIME)) },
|
Modifier
|
||||||
startDestination = (if (isPinLockEnabled == true) Screen.Lock.route else Screen.Main.route),
|
.padding(padding)
|
||||||
|
.fillMaxSize(),
|
||||||
|
) {
|
||||||
|
composable(
|
||||||
|
Screen.Main.route,
|
||||||
) {
|
) {
|
||||||
composable(
|
MainScreen(
|
||||||
Screen.Main.route,
|
focusRequester = focusRequester,
|
||||||
) {
|
appViewModel = appViewModel,
|
||||||
MainScreen(
|
navController = navController,
|
||||||
focusRequester = focusRequester,
|
)
|
||||||
appViewModel = appViewModel,
|
}
|
||||||
|
composable(
|
||||||
|
Screen.Settings.route,
|
||||||
|
) {
|
||||||
|
SettingsScreen(
|
||||||
|
appViewModel = appViewModel,
|
||||||
|
navController = navController,
|
||||||
|
focusRequester = focusRequester,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
composable(
|
||||||
|
Screen.Support.route,
|
||||||
|
) {
|
||||||
|
SupportScreen(
|
||||||
|
focusRequester = focusRequester,
|
||||||
|
navController = navController,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
composable(Screen.Support.Logs.route) {
|
||||||
|
LogsScreen()
|
||||||
|
}
|
||||||
|
composable(
|
||||||
|
"${Screen.Config.route}/{id}?configType={configType}",
|
||||||
|
arguments =
|
||||||
|
listOf(
|
||||||
|
navArgument("id") {
|
||||||
|
type = NavType.StringType
|
||||||
|
defaultValue = "0"
|
||||||
|
},
|
||||||
|
navArgument("configType") {
|
||||||
|
type = NavType.StringType
|
||||||
|
defaultValue = ConfigType.WIREGUARD.name
|
||||||
|
},
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
val id = it.arguments?.getString("id")
|
||||||
|
val configType =
|
||||||
|
ConfigType.valueOf(
|
||||||
|
it.arguments?.getString("configType") ?: ConfigType.WIREGUARD.name,
|
||||||
|
)
|
||||||
|
if (!id.isNullOrBlank()) {
|
||||||
|
ConfigScreen(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
|
tunnelId = id,
|
||||||
|
appViewModel = appViewModel,
|
||||||
|
focusRequester = focusRequester,
|
||||||
|
configType = configType,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
composable(
|
}
|
||||||
Screen.Settings.route,
|
composable("${Screen.Option.route}/{id}") {
|
||||||
) {
|
val id = it.arguments?.getString("id")
|
||||||
SettingsScreen(
|
if (!id.isNullOrBlank()) {
|
||||||
appViewModel = appViewModel,
|
OptionsScreen(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
|
tunnelId = id,
|
||||||
|
appViewModel = appViewModel,
|
||||||
focusRequester = focusRequester,
|
focusRequester = focusRequester,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
composable(
|
}
|
||||||
Screen.Support.route,
|
composable(Screen.Lock.route) {
|
||||||
) {
|
PinLockScreen(
|
||||||
SupportScreen(
|
navController = navController,
|
||||||
focusRequester = focusRequester,
|
appViewModel = appViewModel,
|
||||||
navController = navController,
|
)
|
||||||
)
|
|
||||||
}
|
|
||||||
composable(Screen.Support.Logs.route) {
|
|
||||||
LogsScreen()
|
|
||||||
}
|
|
||||||
composable(
|
|
||||||
"${Screen.Config.route}/{id}?configType={configType}",
|
|
||||||
arguments =
|
|
||||||
listOf(
|
|
||||||
navArgument("id") {
|
|
||||||
type = NavType.StringType
|
|
||||||
defaultValue = "0"
|
|
||||||
},
|
|
||||||
navArgument("configType") {
|
|
||||||
type = NavType.StringType
|
|
||||||
defaultValue = ConfigType.WIREGUARD.name
|
|
||||||
},
|
|
||||||
),
|
|
||||||
) {
|
|
||||||
val id = it.arguments?.getString("id")
|
|
||||||
val configType =
|
|
||||||
ConfigType.valueOf(
|
|
||||||
it.arguments?.getString("configType") ?: ConfigType.WIREGUARD.name,
|
|
||||||
)
|
|
||||||
if (!id.isNullOrBlank()) {
|
|
||||||
ConfigScreen(
|
|
||||||
navController = navController,
|
|
||||||
tunnelId = id,
|
|
||||||
appViewModel = appViewModel,
|
|
||||||
focusRequester = focusRequester,
|
|
||||||
configType = configType,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
composable("${Screen.Option.route}/{id}") {
|
|
||||||
val id = it.arguments?.getString("id")
|
|
||||||
if (!id.isNullOrBlank()) {
|
|
||||||
OptionsScreen(
|
|
||||||
navController = navController,
|
|
||||||
tunnelId = id,
|
|
||||||
appViewModel = appViewModel,
|
|
||||||
focusRequester = focusRequester,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
composable(Screen.Lock.route) {
|
|
||||||
PinLockScreen(
|
|
||||||
navController = navController,
|
|
||||||
appViewModel = appViewModel,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
super.onDestroy()
|
|
||||||
tunnelService.cancelStatsJob()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,16 +9,21 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
|||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
|
import com.zaneschepke.logcatter.LocalLogCollector
|
||||||
import com.zaneschepke.wireguardautotunnel.WireGuardAutoTunnel
|
import com.zaneschepke.wireguardautotunnel.WireGuardAutoTunnel
|
||||||
import com.zaneschepke.wireguardautotunnel.data.repository.AppDataRepository
|
import com.zaneschepke.wireguardautotunnel.data.repository.AppDataRepository
|
||||||
import com.zaneschepke.wireguardautotunnel.data.repository.AppStateRepository
|
import com.zaneschepke.wireguardautotunnel.data.repository.AppStateRepository
|
||||||
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager
|
import com.zaneschepke.wireguardautotunnel.module.ApplicationScope
|
||||||
import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelService
|
import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelService
|
||||||
import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelState
|
import com.zaneschepke.wireguardautotunnel.util.Constants
|
||||||
|
import com.zaneschepke.wireguardautotunnel.util.extensions.isRunningOnTv
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.requestAutoTunnelTileServiceUpdate
|
import com.zaneschepke.wireguardautotunnel.util.extensions.requestAutoTunnelTileServiceUpdate
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.requestTunnelTileServiceStateUpdate
|
import com.zaneschepke.wireguardautotunnel.util.extensions.requestTunnelTileServiceStateUpdate
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import timber.log.Timber
|
||||||
import xyz.teamgravity.pin_lock_compose.PinManager
|
import xyz.teamgravity.pin_lock_compose.PinManager
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Provider
|
import javax.inject.Provider
|
||||||
@@ -36,7 +41,11 @@ class SplashActivity : ComponentActivity() {
|
|||||||
lateinit var tunnelService: Provider<TunnelService>
|
lateinit var tunnelService: Provider<TunnelService>
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var serviceManager: ServiceManager
|
lateinit var localLogCollector: LocalLogCollector
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
@ApplicationScope
|
||||||
|
lateinit var applicationScope: CoroutineScope
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
@@ -45,17 +54,29 @@ class SplashActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
applicationScope.launch {
|
||||||
|
if (!this@SplashActivity.isRunningOnTv()) localLogCollector.start()
|
||||||
|
}
|
||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||||
val pinLockEnabled = appStateRepository.isPinLockEnabled()
|
val pinLockEnabled = appStateRepository.isPinLockEnabled()
|
||||||
if (pinLockEnabled) {
|
if (pinLockEnabled) {
|
||||||
PinManager.initialize(WireGuardAutoTunnel.instance)
|
PinManager.initialize(WireGuardAutoTunnel.instance)
|
||||||
}
|
}
|
||||||
|
// TODO eventually make this support multi-tunnel
|
||||||
|
Timber.d("Check for active tunnels")
|
||||||
val settings = appDataRepository.settings.getSettings()
|
val settings = appDataRepository.settings.getSettings()
|
||||||
if (settings.isAutoTunnelEnabled) serviceManager.startWatcherService(application.applicationContext)
|
if (settings.isKernelEnabled) {
|
||||||
if (tunnelService.get().getState() == TunnelState.UP) tunnelService.get().startStatsJob()
|
// delay in case state change is underway while app is opened
|
||||||
val tunnels = appDataRepository.tunnels.getActive()
|
delay(Constants.FOCUS_REQUEST_DELAY)
|
||||||
if (tunnels.isNotEmpty() && tunnelService.get().getState() == TunnelState.DOWN) tunnelService.get().startTunnel(tunnels.first())
|
val activeTunnels = appDataRepository.tunnels.getActive()
|
||||||
|
Timber.d("Kernel mode enabled, seeing if we need to start a tunnel")
|
||||||
|
activeTunnels.firstOrNull()?.let {
|
||||||
|
Timber.d("Trying to start active kernel tunnel: ${it.name}")
|
||||||
|
tunnelService.get().startTunnel(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
requestTunnelTileServiceStateUpdate()
|
requestTunnelTileServiceStateUpdate()
|
||||||
requestAutoTunnelTileServiceUpdate()
|
requestAutoTunnelTileServiceUpdate()
|
||||||
|
|
||||||
|
|||||||
+15
-7
@@ -10,29 +10,37 @@ import androidx.compose.runtime.getValue
|
|||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
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.graphics.Color
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import androidx.navigation.NavGraph.Companion.findStartDestination
|
import androidx.navigation.NavGraph.Companion.findStartDestination
|
||||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||||
|
import com.zaneschepke.wireguardautotunnel.ui.Screen
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BottomNavBar(navController: NavController, bottomNavItems: List<BottomNavItem>) {
|
fun BottomNavBar(navController: NavController, bottomNavItems: List<BottomNavItem>) {
|
||||||
|
val backStackEntry = navController.currentBackStackEntryAsState()
|
||||||
|
|
||||||
var showBottomBar by rememberSaveable { mutableStateOf(true) }
|
var showBottomBar by rememberSaveable { mutableStateOf(true) }
|
||||||
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
||||||
|
|
||||||
showBottomBar = bottomNavItems.firstOrNull { navBackStackEntry?.destination?.route?.contains(it.route) == true } != null
|
// TODO find a better way to hide nav bar
|
||||||
|
showBottomBar =
|
||||||
|
when (navBackStackEntry?.destination?.route) {
|
||||||
|
Screen.Lock.route -> false
|
||||||
|
else -> true
|
||||||
|
}
|
||||||
|
|
||||||
if (showBottomBar) {
|
NavigationBar(
|
||||||
NavigationBar(
|
containerColor = if (!showBottomBar) Color.Transparent else MaterialTheme.colorScheme.background,
|
||||||
containerColor = MaterialTheme.colorScheme.surface,
|
) {
|
||||||
) {
|
if (showBottomBar) {
|
||||||
bottomNavItems.forEach { item ->
|
bottomNavItems.forEach { item ->
|
||||||
val selected = navBackStackEntry?.destination?.route?.contains(item.route) == true
|
val selected = item.route == backStackEntry.value?.destination?.route
|
||||||
|
|
||||||
NavigationBarItem(
|
NavigationBarItem(
|
||||||
selected = selected,
|
selected = selected,
|
||||||
onClick = {
|
onClick = {
|
||||||
if (navBackStackEntry?.destination?.route == item.route) return@NavigationBarItem
|
|
||||||
navController.navigate(item.route) {
|
navController.navigate(item.route) {
|
||||||
// Pop up to the start destination of the graph to
|
// Pop up to the start destination of the graph to
|
||||||
// avoid building up a large stack of destinations
|
// avoid building up a large stack of destinations
|
||||||
|
|||||||
+1
-6
@@ -110,12 +110,7 @@ fun ConfigScreen(
|
|||||||
LaunchedEffect(uiState.loading) {
|
LaunchedEffect(uiState.loading) {
|
||||||
if (!uiState.loading && context.isRunningOnTv()) {
|
if (!uiState.loading && context.isRunningOnTv()) {
|
||||||
delay(Constants.FOCUS_REQUEST_DELAY)
|
delay(Constants.FOCUS_REQUEST_DELAY)
|
||||||
kotlin.runCatching {
|
focusRequester.requestFocus()
|
||||||
focusRequester.requestFocus()
|
|
||||||
}.onFailure {
|
|
||||||
delay(Constants.FOCUS_REQUEST_DELAY)
|
|
||||||
focusRequester.requestFocus()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -296,7 +296,7 @@ constructor(
|
|||||||
val wgQuick = buildConfig().toWgQuickString(true)
|
val wgQuick = buildConfig().toWgQuickString(true)
|
||||||
val amQuick =
|
val amQuick =
|
||||||
if (configType == ConfigType.AMNEZIA) {
|
if (configType == ConfigType.AMNEZIA) {
|
||||||
buildAmConfig().toAwgQuickString(true)
|
buildAmConfig().toAwgQuickString()
|
||||||
} else {
|
} else {
|
||||||
TunnelConfig.AM_QUICK_DEFAULT
|
TunnelConfig.AM_QUICK_DEFAULT
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-8
@@ -4,6 +4,9 @@ import android.annotation.SuppressLint
|
|||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.appcompat.app.AppCompatActivity.RESULT_OK
|
import androidx.appcompat.app.AppCompatActivity.RESULT_OK
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.fadeIn
|
||||||
|
import androidx.compose.animation.fadeOut
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.focusable
|
import androidx.compose.foundation.focusable
|
||||||
import androidx.compose.foundation.gestures.ScrollableDefaults
|
import androidx.compose.foundation.gestures.ScrollableDefaults
|
||||||
@@ -144,12 +147,7 @@ fun MainScreen(
|
|||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
if (context.isRunningOnTv()) {
|
if (context.isRunningOnTv()) {
|
||||||
delay(Constants.FOCUS_REQUEST_DELAY)
|
delay(Constants.FOCUS_REQUEST_DELAY)
|
||||||
kotlin.runCatching {
|
focusRequester.requestFocus()
|
||||||
focusRequester.requestFocus()
|
|
||||||
}.onFailure {
|
|
||||||
delay(Constants.FOCUS_REQUEST_DELAY)
|
|
||||||
focusRequester.requestFocus()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,8 +265,12 @@ fun MainScreen(
|
|||||||
reverseLayout = false,
|
reverseLayout = false,
|
||||||
flingBehavior = ScrollableDefaults.flingBehavior(),
|
flingBehavior = ScrollableDefaults.flingBehavior(),
|
||||||
) {
|
) {
|
||||||
if (uiState.tunnels.isEmpty()) {
|
item {
|
||||||
item {
|
AnimatedVisibility(
|
||||||
|
uiState.tunnels.isEmpty(),
|
||||||
|
exit = fadeOut(),
|
||||||
|
enter = fadeIn(),
|
||||||
|
) {
|
||||||
GettingStartedLabel(onClick = { context.openWebUrl(it) })
|
GettingStartedLabel(onClick = { context.openWebUrl(it) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -179,7 +179,7 @@ constructor(
|
|||||||
when (type) {
|
when (type) {
|
||||||
ConfigType.AMNEZIA -> {
|
ConfigType.AMNEZIA -> {
|
||||||
val config = org.amnezia.awg.config.Config.parse(it)
|
val config = org.amnezia.awg.config.Config.parse(it)
|
||||||
amQuick = config.toAwgQuickString(true)
|
amQuick = config.toAwgQuickString()
|
||||||
config.toWgQuickString()
|
config.toWgQuickString()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,7 +252,7 @@ constructor(
|
|||||||
org.amnezia.awg.config.Config.parse(
|
org.amnezia.awg.config.Config.parse(
|
||||||
zip,
|
zip,
|
||||||
)
|
)
|
||||||
amQuick = config.toAwgQuickString(true)
|
amQuick = config.toAwgQuickString()
|
||||||
config.toWgQuickString()
|
config.toWgQuickString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-6
@@ -88,12 +88,7 @@ fun OptionsScreen(
|
|||||||
optionsViewModel.init(tunnelId)
|
optionsViewModel.init(tunnelId)
|
||||||
if (context.isRunningOnTv()) {
|
if (context.isRunningOnTv()) {
|
||||||
delay(Constants.FOCUS_REQUEST_DELAY)
|
delay(Constants.FOCUS_REQUEST_DELAY)
|
||||||
kotlin.runCatching {
|
focusRequester.requestFocus()
|
||||||
focusRequester.requestFocus()
|
|
||||||
}.onFailure {
|
|
||||||
delay(Constants.FOCUS_REQUEST_DELAY)
|
|
||||||
focusRequester.requestFocus()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+46
-10
@@ -12,6 +12,7 @@ import androidx.activity.compose.rememberLauncherForActivityResult
|
|||||||
import androidx.activity.result.ActivityResult
|
import androidx.activity.result.ActivityResult
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.appcompat.app.AppCompatActivity.RESULT_OK
|
import androidx.appcompat.app.AppCompatActivity.RESULT_OK
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
@@ -66,6 +67,7 @@ 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.WireGuardAutoTunnel
|
||||||
|
import com.zaneschepke.wireguardautotunnel.data.domain.TunnelConfig
|
||||||
import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelState
|
import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelState
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.AppViewModel
|
import com.zaneschepke.wireguardautotunnel.ui.AppViewModel
|
||||||
import com.zaneschepke.wireguardautotunnel.ui.Screen
|
import com.zaneschepke.wireguardautotunnel.ui.Screen
|
||||||
@@ -82,7 +84,9 @@ import com.zaneschepke.wireguardautotunnel.util.extensions.isRunningOnTv
|
|||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.launchAppSettings
|
import com.zaneschepke.wireguardautotunnel.util.extensions.launchAppSettings
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.showToast
|
import com.zaneschepke.wireguardautotunnel.util.extensions.showToast
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import timber.log.Timber
|
||||||
import xyz.teamgravity.pin_lock_compose.PinManager
|
import xyz.teamgravity.pin_lock_compose.PinManager
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
@OptIn(
|
@OptIn(
|
||||||
ExperimentalPermissionsApi::class,
|
ExperimentalPermissionsApi::class,
|
||||||
@@ -151,6 +155,43 @@ fun SettingsScreen(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fun exportAllConfigs() {
|
||||||
|
try {
|
||||||
|
val wgFiles =
|
||||||
|
uiState.tunnels.map { config ->
|
||||||
|
val file = File(context.cacheDir, "${config.name}-wg.conf")
|
||||||
|
file.outputStream().use {
|
||||||
|
it.write(config.wgQuick.toByteArray())
|
||||||
|
}
|
||||||
|
file
|
||||||
|
}
|
||||||
|
val amFiles =
|
||||||
|
uiState.tunnels.mapNotNull { config ->
|
||||||
|
if (config.amQuick != TunnelConfig.AM_QUICK_DEFAULT) {
|
||||||
|
val file = File(context.cacheDir, "${config.name}-am.conf")
|
||||||
|
file.outputStream().use {
|
||||||
|
it.write(config.amQuick.toByteArray())
|
||||||
|
}
|
||||||
|
file
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scope.launch {
|
||||||
|
viewModel.onExportTunnels(wgFiles + amFiles).onFailure {
|
||||||
|
appViewModel.showSnackbarMessage(it.getMessage(context))
|
||||||
|
}.onSuccess {
|
||||||
|
didExportFiles = true
|
||||||
|
appViewModel.showSnackbarMessage(
|
||||||
|
context.getString(R.string.exported_configs_message),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.e(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun isBatteryOptimizationsDisabled(): Boolean {
|
fun isBatteryOptimizationsDisabled(): Boolean {
|
||||||
val pm = context.getSystemService(POWER_SERVICE) as PowerManager
|
val pm = context.getSystemService(POWER_SERVICE) as PowerManager
|
||||||
return pm.isIgnoringBatteryOptimizations(context.packageName)
|
return pm.isIgnoringBatteryOptimizations(context.packageName)
|
||||||
@@ -228,7 +269,7 @@ fun SettingsScreen(
|
|||||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
|
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
|
||||||
checkFineLocationGranted()
|
checkFineLocationGranted()
|
||||||
}
|
}
|
||||||
if (!uiState.isLocationDisclosureShown) {
|
if(!uiState.isLocationDisclosureShown) {
|
||||||
BackgroundLocationDisclosure(
|
BackgroundLocationDisclosure(
|
||||||
onDismiss = { viewModel.setLocationDisclosureShown() },
|
onDismiss = { viewModel.setLocationDisclosureShown() },
|
||||||
onAttest = {
|
onAttest = {
|
||||||
@@ -241,6 +282,7 @@ fun SettingsScreen(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
BackgroundLocationDialog(
|
BackgroundLocationDialog(
|
||||||
showLocationDialog,
|
showLocationDialog,
|
||||||
onDismiss = { showLocationDialog = false },
|
onDismiss = { showLocationDialog = false },
|
||||||
@@ -259,13 +301,7 @@ fun SettingsScreen(
|
|||||||
AuthorizationPrompt(
|
AuthorizationPrompt(
|
||||||
onSuccess = {
|
onSuccess = {
|
||||||
showAuthPrompt = false
|
showAuthPrompt = false
|
||||||
scope.launch {
|
exportAllConfigs()
|
||||||
viewModel.exportAllConfigs().onSuccess {
|
|
||||||
appViewModel.showSnackbarMessage(context.getString(R.string.exported_configs_message))
|
|
||||||
}.onFailure {
|
|
||||||
appViewModel.showSnackbarMessage(context.getString(R.string.export_configs_failed))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
onError = { _ ->
|
onError = { _ ->
|
||||||
showAuthPrompt = false
|
showAuthPrompt = false
|
||||||
@@ -344,7 +380,7 @@ fun SettingsScreen(
|
|||||||
.focusRequester(focusRequester)
|
.focusRequester(focusRequester)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if (uiState.settings.isTunnelOnWifiEnabled) {
|
AnimatedVisibility(visible = uiState.settings.isTunnelOnWifiEnabled) {
|
||||||
Column {
|
Column {
|
||||||
FlowRow(
|
FlowRow(
|
||||||
modifier =
|
modifier =
|
||||||
@@ -594,7 +630,7 @@ fun SettingsScreen(
|
|||||||
Modifier
|
Modifier
|
||||||
.fillMaxWidth(fillMaxWidth)
|
.fillMaxWidth(fillMaxWidth)
|
||||||
.padding(vertical = 10.dp)
|
.padding(vertical = 10.dp)
|
||||||
.padding(bottom = 10.dp),
|
.padding(bottom = 140.dp),
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
horizontalAlignment = Alignment.Start,
|
horizontalAlignment = Alignment.Start,
|
||||||
|
|||||||
-9
@@ -248,13 +248,4 @@ constructor(
|
|||||||
onSuccess()
|
onSuccess()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun exportAllConfigs(): Result<Unit> {
|
|
||||||
return kotlin.runCatching {
|
|
||||||
val tunnels = appDataRepository.tunnels.getAll()
|
|
||||||
val wgFiles = fileUtils.createWgFiles(tunnels)
|
|
||||||
val amFiles = fileUtils.createAmFiles(tunnels)
|
|
||||||
onExportTunnels(wgFiles + amFiles)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-1
@@ -28,7 +28,12 @@ import com.zaneschepke.wireguardautotunnel.R
|
|||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.isRunningOnTv
|
import com.zaneschepke.wireguardautotunnel.util.extensions.isRunningOnTv
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BackgroundLocationDisclosure(onDismiss: () -> Unit, onAttest: () -> Unit, scrollState: ScrollState, focusRequester: FocusRequester) {
|
fun BackgroundLocationDisclosure(
|
||||||
|
onDismiss: () -> Unit,
|
||||||
|
onAttest: () -> Unit,
|
||||||
|
scrollState: ScrollState,
|
||||||
|
focusRequester: FocusRequester,
|
||||||
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
Column(
|
Column(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
|||||||
@@ -20,10 +20,9 @@ private val DarkColorScheme =
|
|||||||
darkColorScheme(
|
darkColorScheme(
|
||||||
// primary = Purple80,
|
// primary = Purple80,
|
||||||
primary = virdigris,
|
primary = virdigris,
|
||||||
secondary = PurpleGrey40,
|
secondary = virdigris,
|
||||||
// secondary = PurpleGrey80,
|
// secondary = PurpleGrey80,
|
||||||
tertiary = Pink40,
|
tertiary = virdigris,
|
||||||
surfaceTint = Pink80,
|
|
||||||
// tertiary = Pink80
|
// tertiary = Pink80
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -32,7 +31,6 @@ private val LightColorScheme =
|
|||||||
primary = Purple40,
|
primary = Purple40,
|
||||||
secondary = PurpleGrey40,
|
secondary = PurpleGrey40,
|
||||||
tertiary = Pink40,
|
tertiary = Pink40,
|
||||||
surfaceTint = Pink80,
|
|
||||||
/* Other default colors to override
|
/* Other default colors to override
|
||||||
background = Color(0xFFFFFBFE),
|
background = Color(0xFFFFFBFE),
|
||||||
surface = Color(0xFFFFFBFE),
|
surface = Color(0xFFFFFBFE),
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ object Constants {
|
|||||||
const val MANUAL_TUNNEL_CONFIG_ID = "0"
|
const val MANUAL_TUNNEL_CONFIG_ID = "0"
|
||||||
const val BATTERY_SAVER_WATCHER_WAKE_LOCK_TIMEOUT = 10 * 60 * 1_000L // 10 minutes
|
const val BATTERY_SAVER_WATCHER_WAKE_LOCK_TIMEOUT = 10 * 60 * 1_000L // 10 minutes
|
||||||
const val VPN_STATISTIC_CHECK_INTERVAL = 1_000L
|
const val VPN_STATISTIC_CHECK_INTERVAL = 1_000L
|
||||||
const val WATCHER_COLLECTION_DELAY = 3_000L
|
const val WATCHER_COLLECTION_DELAY = 1_000L
|
||||||
const val CONF_FILE_EXTENSION = ".conf"
|
const val CONF_FILE_EXTENSION = ".conf"
|
||||||
const val ZIP_FILE_EXTENSION = ".zip"
|
const val ZIP_FILE_EXTENSION = ".zip"
|
||||||
const val URI_CONTENT_SCHEME = "content"
|
const val URI_CONTENT_SCHEME = "content"
|
||||||
@@ -24,8 +24,6 @@ object Constants {
|
|||||||
const val SUBSCRIPTION_TIMEOUT = 5_000L
|
const val SUBSCRIPTION_TIMEOUT = 5_000L
|
||||||
const val FOCUS_REQUEST_DELAY = 500L
|
const val FOCUS_REQUEST_DELAY = 500L
|
||||||
|
|
||||||
const val TRANSITION_ANIMATION_TIME = 200
|
|
||||||
|
|
||||||
const val DEFAULT_PING_IP = "1.1.1.1"
|
const val DEFAULT_PING_IP = "1.1.1.1"
|
||||||
const val PING_TIMEOUT = 5_000L
|
const val PING_TIMEOUT = 5_000L
|
||||||
const val VPN_RESTART_DELAY = 1_000L
|
const val VPN_RESTART_DELAY = 1_000L
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ import android.os.Build
|
|||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
import android.provider.MediaStore.MediaColumns
|
import android.provider.MediaStore.MediaColumns
|
||||||
import com.zaneschepke.wireguardautotunnel.data.domain.TunnelConfig
|
|
||||||
import com.zaneschepke.wireguardautotunnel.util.extensions.TunnelConfigs
|
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
@@ -41,26 +39,6 @@ class FileUtils(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createWgFiles(tunnels: TunnelConfigs): List<File> {
|
|
||||||
return tunnels.map { config ->
|
|
||||||
val file = File(context.cacheDir, "${config.name}-wg.conf")
|
|
||||||
file.outputStream().use {
|
|
||||||
it.write(config.wgQuick.toByteArray())
|
|
||||||
}
|
|
||||||
file
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun createAmFiles(tunnels: TunnelConfigs): List<File> {
|
|
||||||
return tunnels.filter { it.amQuick != TunnelConfig.AM_QUICK_DEFAULT }.map { config ->
|
|
||||||
val file = File(context.cacheDir, "${config.name}-am.conf")
|
|
||||||
file.outputStream().use {
|
|
||||||
it.write(config.amQuick.toByteArray())
|
|
||||||
}
|
|
||||||
file
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun saveByteArrayToDownloads(content: ByteArray, fileName: String): Result<Unit> {
|
suspend fun saveByteArrayToDownloads(content: ByteArray, fileName: String): Result<Unit> {
|
||||||
return withContext(ioDispatcher) {
|
return withContext(ioDispatcher) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
+1
-1
@@ -29,7 +29,7 @@ fun TunnelStatistics.PeerStats.handshakeStatus(): HandshakeStatus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun Config.toWgQuickString(): String {
|
fun Config.toWgQuickString(): String {
|
||||||
val amQuick = toAwgQuickString(true)
|
val amQuick = toAwgQuickString()
|
||||||
val lines = amQuick.lines().toMutableList()
|
val lines = amQuick.lines().toMutableList()
|
||||||
val linesIterator = lines.iterator()
|
val linesIterator = lines.iterator()
|
||||||
while (linesIterator.hasNext()) {
|
while (linesIterator.hasNext()) {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
object Constants {
|
object Constants {
|
||||||
const val VERSION_NAME = "3.5.1"
|
const val VERSION_NAME = "3.5.0"
|
||||||
const val JVM_TARGET = "17"
|
const val JVM_TARGET = "17"
|
||||||
const val VERSION_CODE = 35100
|
const val VERSION_CODE = 35001
|
||||||
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"
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
What's new:
|
|
||||||
- Fixes for tunnels not launching from background
|
|
||||||
- Add support for restart services after update
|
|
||||||
- UI animation speed improvements
|
|
||||||
- Other optimizations
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
[versions]
|
[versions]
|
||||||
accompanist = "0.34.0"
|
accompanist = "0.34.0"
|
||||||
activityCompose = "1.9.1"
|
activityCompose = "1.9.1"
|
||||||
amneziawgAndroid = "1.2.2"
|
amneziawgAndroid = "1.2.1"
|
||||||
androidx-junit = "1.2.1"
|
androidx-junit = "1.2.1"
|
||||||
appcompat = "1.7.0"
|
appcompat = "1.7.0"
|
||||||
biometricKtx = "1.2.0-alpha05"
|
biometricKtx = "1.2.0-alpha05"
|
||||||
@@ -16,13 +16,13 @@ junit = "4.13.2"
|
|||||||
kotlinx-serialization-json = "1.7.1"
|
kotlinx-serialization-json = "1.7.1"
|
||||||
lifecycle-runtime-compose = "2.8.4"
|
lifecycle-runtime-compose = "2.8.4"
|
||||||
material3 = "1.2.1"
|
material3 = "1.2.1"
|
||||||
multifabVersion = "1.1.1"
|
multifabVersion = "1.1.0"
|
||||||
navigationCompose = "2.7.7"
|
navigationCompose = "2.7.7"
|
||||||
pinLockCompose = "1.0.3"
|
pinLockCompose = "1.0.3"
|
||||||
roomVersion = "2.6.1"
|
roomVersion = "2.6.1"
|
||||||
timber = "5.0.1"
|
timber = "5.0.1"
|
||||||
tunnel = "1.2.1"
|
tunnel = "1.2.1"
|
||||||
androidGradlePlugin = "8.6.0-rc01"
|
androidGradlePlugin = "8.7.0-alpha06"
|
||||||
kotlin = "2.0.10"
|
kotlin = "2.0.10"
|
||||||
ksp = "2.0.10-1.0.24"
|
ksp = "2.0.10-1.0.24"
|
||||||
composeBom = "2024.06.00"
|
composeBom = "2024.06.00"
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import kotlinx.coroutines.flow.Flow
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
interface LocalLogCollector {
|
interface LocalLogCollector {
|
||||||
suspend fun start(onLogMessage: ((message: LogMessage) -> Unit)? = null)
|
fun start(onLogMessage: ((message: LogMessage) -> Unit)? = null)
|
||||||
|
|
||||||
fun stop()
|
fun stop()
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ object LogcatUtil {
|
|||||||
internal object Logcat : LocalLogCollector {
|
internal object Logcat : LocalLogCollector {
|
||||||
private var logcatReader: LogcatReader? = null
|
private var logcatReader: LogcatReader? = null
|
||||||
|
|
||||||
override suspend fun start(onLogMessage: ((message: LogMessage) -> Unit)?) {
|
override fun start(onLogMessage: ((message: LogMessage) -> Unit)?) {
|
||||||
logcatReader ?: run {
|
logcatReader ?: run {
|
||||||
logcatReader =
|
logcatReader =
|
||||||
LogcatReader(
|
LogcatReader(
|
||||||
@@ -78,7 +78,9 @@ object LogcatUtil {
|
|||||||
onLogMessage,
|
onLogMessage,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
logcatReader?.run()
|
logcatReader?.let { logReader ->
|
||||||
|
if (!logReader.isAlive) logReader.start()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun stop() {
|
override fun stop() {
|
||||||
@@ -140,7 +142,7 @@ object LogcatUtil {
|
|||||||
pID: String,
|
pID: String,
|
||||||
private val logcatPath: String,
|
private val logcatPath: String,
|
||||||
private val callback: ((input: LogMessage) -> Unit)?,
|
private val callback: ((input: LogMessage) -> Unit)?,
|
||||||
) {
|
) : Thread() {
|
||||||
private var logcatProc: Process? = null
|
private var logcatProc: Process? = null
|
||||||
private var reader: BufferedReader? = null
|
private var reader: BufferedReader? = null
|
||||||
private var mRunning = true
|
private var mRunning = true
|
||||||
@@ -175,7 +177,7 @@ object LogcatUtil {
|
|||||||
}.let { last -> findIpv4AddressRegex.replace(last, "<ipv4-address>") }
|
}.let { last -> findIpv4AddressRegex.replace(last, "<ipv4-address>") }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun run() {
|
override fun run() {
|
||||||
if (outputStream == null) return
|
if (outputStream == null) return
|
||||||
try {
|
try {
|
||||||
clear()
|
clear()
|
||||||
|
|||||||
Reference in New Issue
Block a user