Skip to content

Commit e50853f

Browse files
committed
Merge remote-tracking branch 'origin/master' into feat/auto-restart-v2
2 parents 84d87b4 + 0a3acf0 commit e50853f

104 files changed

Lines changed: 2317 additions & 627 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/build-aab.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ jobs:
122122
echo "path=$AAB_PATH" >> $GITHUB_OUTPUT
123123
124124
- name: Upload AAB Artifact
125-
uses: actions/upload-artifact@v6
125+
uses: actions/upload-artifact@v7
126126
with:
127127
name: google-play-aab
128128
path: ${{ steps.aab-path.outputs.path }}

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ jobs:
115115
id: apk-path
116116
run: echo "path=$(find . -regex '^.*/build/outputs/apk/${{ inputs.flavor }}/${{ inputs.build_type }}/.*\.apk$' -type f | head -1 | tail -c+2)" >> $GITHUB_OUTPUT
117117
- name: Upload All APK Artifacts
118-
uses: actions/upload-artifact@v6
118+
uses: actions/upload-artifact@v7
119119
with:
120120
name: android_artifacts_${{ inputs.flavor }}
121121
path: >-

.github/workflows/nightly.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ jobs:
7171
run: mkdir ${{ github.workspace }}/temp
7272

7373
- name: Download artifacts
74-
uses: actions/download-artifact@v7
74+
uses: actions/download-artifact@v8
7575
with:
7676
pattern: android_artifacts_*
7777
path: ${{ github.workspace }}/temp

.github/workflows/publish.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ jobs:
115115
run: mkdir ${{ github.workspace }}/temp
116116

117117
- name: Download artifacts
118-
uses: actions/download-artifact@v7
118+
uses: actions/download-artifact@v8
119119
with:
120120
pattern: android_artifacts_*
121121
path: ${{ github.workspace }}/temp
@@ -240,4 +240,4 @@ jobs:
240240
- name: Distribute app to Prod track 🚀
241241
run: |
242242
track=${{ github.event_name == 'push' && 'production' || inputs.track }}
243-
(cd ${{ github.workspace }} && bundle install && bundle exec fastlane $track)
243+
(cd ${{ github.workspace }} && bundle install && bundle exec fastlane $track --verbose)

app/src/main/java/com/zaneschepke/wireguardautotunnel/core/notification/NotificationManager.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ interface NotificationManager {
1414
fun createNotification(
1515
channel: NotificationChannels,
1616
title: String = "",
17+
subText: String? = null,
1718
actions: Collection<NotificationCompat.Action> = emptyList(),
1819
description: String = "",
1920
showTimestamp: Boolean = true,
@@ -27,6 +28,7 @@ interface NotificationManager {
2728
fun createNotification(
2829
channel: NotificationChannels,
2930
title: StringValue,
31+
subText: String? = null,
3032
actions: Collection<NotificationCompat.Action> = emptyList(),
3133
description: StringValue,
3234
showTimestamp: Boolean = true,

app/src/main/java/com/zaneschepke/wireguardautotunnel/core/notification/WireGuardNotification.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class WireGuardNotification(override val context: Context) : NotificationManager
2929
override fun createNotification(
3030
channel: NotificationChannels,
3131
title: String,
32+
subText: String?,
3233
actions: Collection<NotificationCompat.Action>,
3334
description: String,
3435
showTimestamp: Boolean,
@@ -44,6 +45,7 @@ class WireGuardNotification(override val context: Context) : NotificationManager
4445
.apply {
4546
actions.forEach { addAction(it) }
4647
setContentTitle(title)
48+
setSubText(subText)
4749
setContentIntent(
4850
PendingIntent.getActivity(
4951
context,
@@ -72,6 +74,7 @@ class WireGuardNotification(override val context: Context) : NotificationManager
7274
override fun createNotification(
7375
channel: NotificationChannels,
7476
title: StringValue,
77+
subText: String?,
7578
actions: Collection<NotificationCompat.Action>,
7679
description: StringValue,
7780
showTimestamp: Boolean,
@@ -84,6 +87,7 @@ class WireGuardNotification(override val context: Context) : NotificationManager
8487
return createNotification(
8588
channel,
8689
title.asString(context),
90+
subText,
8791
actions,
8892
description.asString(context),
8993
showTimestamp,
@@ -109,7 +113,7 @@ class WireGuardNotification(override val context: Context) : NotificationManager
109113
)
110114
return NotificationCompat.Action.Builder(
111115
R.drawable.ic_notification,
112-
notificationAction.title(context).uppercase(),
116+
notificationAction.title(context),
113117
pendingIntent,
114118
)
115119
.build()
@@ -141,6 +145,7 @@ class WireGuardNotification(override val context: Context) : NotificationManager
141145
context.getString(R.string.auto_tunnel_channel_id),
142146
)
143147
}
148+
144149
NotificationChannels.VPN -> {
145150
NotificationCompat.Builder(context, context.getString(R.string.vpn_channel_id))
146151
}
@@ -157,6 +162,7 @@ class WireGuardNotification(override val context: Context) : NotificationManager
157162
)
158163
.apply { description = context.getString(R.string.vpn_channel_description) }
159164
}
165+
160166
NotificationChannels.AUTO_TUNNEL -> {
161167
NotificationChannel(
162168
context.getString(R.string.auto_tunnel_channel_id),

app/src/main/java/com/zaneschepke/wireguardautotunnel/core/service/BaseTunnelForegroundService.kt

Lines changed: 80 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.zaneschepke.wireguardautotunnel.core.service
33
import android.app.Notification
44
import android.content.Intent
55
import android.os.IBinder
6+
import android.text.format.Formatter
67
import androidx.core.app.ServiceCompat
78
import androidx.lifecycle.LifecycleService
89
import androidx.lifecycle.lifecycleScope
@@ -17,6 +18,9 @@ import com.zaneschepke.wireguardautotunnel.domain.repository.GeneralSettingRepos
1718
import com.zaneschepke.wireguardautotunnel.domain.repository.TunnelRepository
1819
import com.zaneschepke.wireguardautotunnel.util.extensions.distinctByKeys
1920
import kotlinx.coroutines.CoroutineDispatcher
21+
import kotlinx.coroutines.Job
22+
import kotlinx.coroutines.delay
23+
import kotlinx.coroutines.isActive
2024
import kotlinx.coroutines.launch
2125
import org.koin.android.ext.android.inject
2226
import org.koin.core.qualifier.named
@@ -25,19 +29,18 @@ import timber.log.Timber
2529
abstract class BaseTunnelForegroundService : LifecycleService(), TunnelService {
2630

2731
private val notificationManager: NotificationManager by inject()
28-
2932
private val serviceManager: ServiceManager by inject()
30-
3133
private val tunnelManager: TunnelManager by inject()
32-
3334
private val ioDispatcher: CoroutineDispatcher by inject(named(Dispatcher.IO))
34-
3535
private val settingsRepository: GeneralSettingRepository by inject()
36-
3736
private val tunnelsRepository: TunnelRepository by inject()
3837

3938
protected abstract val fgsType: Int
4039

40+
private var currentSingleTunnelId: Int? = null
41+
42+
private var statsJob: Job? = null
43+
4144
override fun onBind(intent: Intent): IBinder {
4245
super.onBind(intent)
4346
return LocalBinder(this)
@@ -55,12 +58,14 @@ abstract class BaseTunnelForegroundService : LifecycleService(), TunnelService {
5558

5659
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
5760
super.onStartCommand(intent, flags, startId)
61+
5862
ServiceCompat.startForeground(
5963
this,
6064
NotificationManager.VPN_NOTIFICATION_ID,
6165
onCreateNotification(),
6266
fgsType,
6367
)
68+
6469
if (
6570
intent == null ||
6671
intent.component == null ||
@@ -79,28 +84,68 @@ abstract class BaseTunnelForegroundService : LifecycleService(), TunnelService {
7984
} else {
8085
start()
8186
}
87+
8288
return START_STICKY
8389
}
8490

8591
override fun start() {
8692
lifecycleScope.launch(ioDispatcher) {
8793
tunnelManager.activeTunnels.distinctByKeys().collect { activeTunnels ->
88-
val activeTunConfigs = activeTunnels.keys
94+
val activeTunIds = activeTunnels.keys
8995
val tunnels = tunnelsRepository.getAll()
90-
val activeConfigs = tunnels.filter { activeTunConfigs.contains(it.id) }
96+
val activeConfigs = tunnels.filter { activeTunIds.contains(it.id) }
97+
9198
updateServiceNotification(activeConfigs)
99+
restartStatsUpdaterIfNeeded(activeConfigs)
92100
}
93101
}
94102
}
95103

96-
// TODO Would be cool to have this include kill switch
104+
private fun restartStatsUpdaterIfNeeded(activeConfigs: List<TunnelConfig>) {
105+
val single = activeConfigs.singleOrNull()
106+
107+
if (single == null) {
108+
statsJob?.cancel()
109+
statsJob = null
110+
currentSingleTunnelId = null
111+
return
112+
}
113+
114+
if (currentSingleTunnelId == single.id && statsJob?.isActive == true) return
115+
116+
statsJob?.cancel()
117+
statsJob = null
118+
currentSingleTunnelId = single.id
119+
120+
statsJob =
121+
lifecycleScope.launch(ioDispatcher) {
122+
while (isActive) {
123+
val traffic = readTraffic(single.id)
124+
125+
notificationManager.show(
126+
NotificationManager.VPN_NOTIFICATION_ID,
127+
createTunnelNotification(single, consumedTraffic = traffic),
128+
)
129+
130+
delay(1000)
131+
}
132+
}
133+
}
134+
135+
private fun readTraffic(tunnelId: Int): Pair<Long, Long>? {
136+
val active = tunnelManager.activeTunnels.value[tunnelId] ?: return null
137+
val stats = active.statistics ?: return null
138+
return stats.rx() to stats.tx()
139+
}
140+
97141
private fun updateServiceNotification(activeConfigs: List<TunnelConfig>) {
98142
val notification =
99143
when (activeConfigs.size) {
100144
0 -> onCreateNotification()
101-
1 -> createTunnelNotification(activeConfigs.first())
145+
1 -> createTunnelNotification(activeConfigs.first(), consumedTraffic = null)
102146
else -> createTunnelsNotification()
103147
}
148+
104149
ServiceCompat.startForeground(
105150
this,
106151
NotificationManager.VPN_NOTIFICATION_ID,
@@ -111,21 +156,43 @@ abstract class BaseTunnelForegroundService : LifecycleService(), TunnelService {
111156

112157
override fun stop() {
113158
Timber.d("Stop called")
159+
statsJob?.cancel()
160+
statsJob = null
161+
currentSingleTunnelId = null
162+
114163
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
115164
stopSelf()
116165
}
117166

118167
override fun onDestroy() {
119168
serviceManager.handleTunnelServiceDestroy()
169+
170+
statsJob?.cancel()
171+
statsJob = null
172+
currentSingleTunnelId = null
173+
120174
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
121175
Timber.d("onDestroy")
122176
super.onDestroy()
123177
}
124178

125-
private fun createTunnelNotification(tunnelConfig: TunnelConfig): Notification {
179+
private fun createTunnelNotification(
180+
tunnelConfig: TunnelConfig,
181+
consumedTraffic: Pair<Long, Long>?,
182+
): Notification {
183+
184+
val subText =
185+
consumedTraffic?.let { traffic ->
186+
val formattedRx = "${formatBytes(traffic.first)}"
187+
val formattedTx = "${formatBytes(traffic.second)}"
188+
"$formattedRx $formattedTx"
189+
}
190+
126191
return notificationManager.createNotification(
127192
WireGuardNotification.NotificationChannels.VPN,
128-
title = "${getString(R.string.tunnel_running)} - ${tunnelConfig.name}",
193+
title = tunnelConfig.name,
194+
description = getString(R.string.tunnel_running),
195+
subText = subText,
129196
actions =
130197
listOf(
131198
notificationManager.createNotificationAction(
@@ -160,4 +227,6 @@ abstract class BaseTunnelForegroundService : LifecycleService(), TunnelService {
160227
isGroupSummary = true,
161228
)
162229
}
230+
231+
private fun formatBytes(bytes: Long) = Formatter.formatFileSize(this, bytes)
163232
}

app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/common/ExpandingRowListItem.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,15 @@ import androidx.compose.runtime.Composable
1010
import androidx.compose.ui.Alignment
1111
import androidx.compose.ui.Modifier
1212
import androidx.compose.ui.graphics.Color
13+
import androidx.compose.ui.text.AnnotatedString
1314
import androidx.compose.ui.text.style.TextOverflow
1415
import androidx.compose.ui.unit.dp
1516

1617
@OptIn(ExperimentalFoundationApi::class)
1718
@Composable
1819
fun ExpandingRowListItem(
1920
leading: @Composable () -> Unit,
20-
text: String,
21+
text: AnnotatedString,
2122
trailing: @Composable () -> Unit,
2223
isSelected: Boolean,
2324
expanded: @Composable () -> Unit,

app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/navigation/components/currentBackStackEntryAsNavbarState.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,18 @@ fun currentRouteAsNavbarState(
218218
topTitle = context.getString(R.string.sort),
219219
topTrailing = {
220220
Row {
221+
IconButton(
222+
onClick = {
223+
sharedViewModel.postSideEffect(
224+
LocalSideEffect.SortByLatency
225+
)
226+
}
227+
) {
228+
Icon(
229+
Icons.Rounded.NetworkCheck,
230+
stringResource(R.string.sort_by_latency),
231+
)
232+
}
221233
IconButton(
222234
onClick = {
223235
sharedViewModel.postSideEffect(LocalSideEffect.Sort)

0 commit comments

Comments
 (0)