Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ fun HomeScreen(
val vpnState by VpnManager.state.collectAsState()
val upBps by VpnManager.uploadSpeedBps.collectAsState()
val downBps by VpnManager.downloadSpeedBps.collectAsState()
val uploadTotalBytes by VpnManager.uploadTotalBytes.collectAsState()
val downloadTotalBytes by VpnManager.downloadTotalBytes.collectAsState()
val connectedDurationSeconds by VpnManager.connectedDurationSeconds.collectAsState()
val scanStatus by VpnManager.scanStatus.collectAsState()
val selectedProfile by viewModel.selectedProfile.collectAsState()
val error by VpnManager.errorMessage.collectAsState()
Expand Down Expand Up @@ -228,6 +231,9 @@ fun HomeScreen(
scanProgress = scanProgress,
downBps = downBps,
upBps = upBps,
downloadTotalBytes = downloadTotalBytes,
uploadTotalBytes = uploadTotalBytes,
connectedDurationSeconds = connectedDurationSeconds,
proxyHost = proxyHost,
proxyPort = proxyPort,
socksAuthEnabled = socksAuthEnabled,
Expand Down Expand Up @@ -297,6 +303,9 @@ fun HomeScreen(
scanProgress = scanProgress,
downBps = downBps,
upBps = upBps,
downloadTotalBytes = downloadTotalBytes,
uploadTotalBytes = uploadTotalBytes,
connectedDurationSeconds = connectedDurationSeconds,
proxyHost = proxyHost,
proxyPort = proxyPort,
socksAuthEnabled = socksAuthEnabled,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ fun MdvConnectionTelemetryCard(
scanProgress: Float,
downBps: Long,
upBps: Long,
downloadTotalBytes: Long,
uploadTotalBytes: Long,
connectedDurationSeconds: Long,
proxyHost: String,
proxyPort: Int,
socksAuthEnabled: Boolean,
Expand Down Expand Up @@ -135,6 +138,26 @@ fun MdvConnectionTelemetryCard(
style = MaterialTheme.typography.bodySmall,
color = MdvColor.OnSurfaceVariant
)
if (downloadTotalBytes > 0 || uploadTotalBytes > 0 || connectedDurationSeconds > 0) {
androidx.compose.foundation.layout.Spacer(modifier = Modifier.height(2.dp))
Text(
text = stringResource(
R.string.home_traffic_totals,
formatBytes(downloadTotalBytes),
formatBytes(uploadTotalBytes)
),
style = MaterialTheme.typography.bodySmall,
color = MdvColor.OnSurfaceVariant
)
Text(
text = stringResource(
R.string.home_session_duration,
formatDuration(connectedDurationSeconds)
),
style = MaterialTheme.typography.bodySmall,
color = MdvColor.OnSurfaceVariant
)
}
androidx.compose.foundation.layout.Spacer(modifier = Modifier.height(MdvSpace.S2))
Text(
text = stringResource(R.string.home_socks_address, proxyHost, proxyPort),
Expand Down Expand Up @@ -227,3 +250,27 @@ private fun formatSpeed(bps: Long): String {
else -> "${bps} B/s"
}
}

private fun formatBytes(bytes: Long): String {
val kb = 1024.0
val mb = kb * 1024.0
val gb = mb * 1024.0
return when {
bytes >= gb -> String.format("%.2f GB", bytes / gb)
bytes >= mb -> String.format("%.2f MB", bytes / mb)
bytes >= kb -> String.format("%.1f KB", bytes / kb)
else -> "$bytes B"
}
}

private fun formatDuration(seconds: Long): String {
val safeSeconds = seconds.coerceAtLeast(0L)
val hours = safeSeconds / 3600L
val minutes = (safeSeconds % 3600L) / 60L
val secs = safeSeconds % 60L
return if (hours > 0) {
"%d:%02d:%02d".format(hours, minutes, secs)
} else {
"%02d:%02d".format(minutes, secs)
}
}
19 changes: 17 additions & 2 deletions android/app/src/main/java/com/masterdns/vpn/util/VpnManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ object VpnManager {
val uploadSpeedBps: StateFlow<Long> = _uploadSpeedBps.asStateFlow()
private val _downloadSpeedBps = MutableStateFlow(0L)
val downloadSpeedBps: StateFlow<Long> = _downloadSpeedBps.asStateFlow()
private val _uploadTotalBytes = MutableStateFlow(0L)
val uploadTotalBytes: StateFlow<Long> = _uploadTotalBytes.asStateFlow()
private val _downloadTotalBytes = MutableStateFlow(0L)
val downloadTotalBytes: StateFlow<Long> = _downloadTotalBytes.asStateFlow()
private val _connectedDurationSeconds = MutableStateFlow(0L)
val connectedDurationSeconds: StateFlow<Long> = _connectedDurationSeconds.asStateFlow()

data class ScanStatus(
val scanning: Boolean = false,
Expand Down Expand Up @@ -243,14 +249,23 @@ object VpnManager {
var prevTx = TrafficStats.getUidTxBytes(uid).coerceAtLeast(0L)
var prevRx = TrafficStats.getUidRxBytes(uid).coerceAtLeast(0L)
var prevTime = System.currentTimeMillis()
val startedAt = prevTime
_uploadTotalBytes.value = 0L
_downloadTotalBytes.value = 0L
_connectedDurationSeconds.value = 0L
while (isActive) {
delay(1000L)
val now = System.currentTimeMillis()
val tx = TrafficStats.getUidTxBytes(uid).coerceAtLeast(0L)
val rx = TrafficStats.getUidRxBytes(uid).coerceAtLeast(0L)
val dt = (now - prevTime).coerceAtLeast(1L)
_uploadSpeedBps.value = ((tx - prevTx).coerceAtLeast(0L) * 1000L) / dt
_downloadSpeedBps.value = ((rx - prevRx).coerceAtLeast(0L) * 1000L) / dt
val uploadDelta = (tx - prevTx).coerceAtLeast(0L)
val downloadDelta = (rx - prevRx).coerceAtLeast(0L)
_uploadSpeedBps.value = (uploadDelta * 1000L) / dt
_downloadSpeedBps.value = (downloadDelta * 1000L) / dt
_uploadTotalBytes.value = _uploadTotalBytes.value + uploadDelta
_downloadTotalBytes.value = _downloadTotalBytes.value + downloadDelta
_connectedDurationSeconds.value = ((now - startedAt) / 1000L).coerceAtLeast(0L)
prevTx = tx
prevRx = rx
prevTime = now
Expand Down
2 changes: 2 additions & 0 deletions android/app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@
<string name="home_synced_mtu">Synced MTU: UP %1$d / DOWN %2$d</string>
<string name="home_active_resolvers">Active Resolvers: %1$d</string>
<string name="home_speed_row">Download: %1$s Upload: %2$s</string>
<string name="home_traffic_totals">Total: down %1$s / up %2$s</string>
<string name="home_session_duration">Session: %1$s</string>
<string name="home_socks_address">SOCKS5: %1$s:%2$d</string>
<string name="home_socks_auth_title">SOCKS5 authentication</string>
<string name="home_socks_username">Username: %1$s</string>
Expand Down
Loading