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
56 changes: 39 additions & 17 deletions app/src/main/java/net/megaproxy487/SpeedTestScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package net.megaproxy487
import android.app.Activity
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
Expand Down Expand Up @@ -39,6 +40,7 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.Role
Expand Down Expand Up @@ -171,43 +173,63 @@ internal fun SpeedTestScreen(activity: Activity, onBack: () -> Unit) {

@Composable
private fun SpeedResultsTable(results: List<RouteSpeedResult>) {
BoxWithConstraints(Modifier.fillMaxWidth()) {
// Three columns need more room when the system font is enlarged.
if (maxWidth < 400.dp * LocalDensity.current.fontScale) {
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
results.forEachIndexed { index, result ->
SpeedResultsBlock(listOf(result), firstRoute = index)
}
}
} else {
SpeedResultsBlock(results)
}
}
}

@Composable
private fun SpeedResultsBlock(results: List<RouteSpeedResult>, firstRoute: Int = 0) {
Card(Modifier.fillMaxWidth()) {
Column(Modifier.padding(12.dp), verticalArrangement = Arrangement.spacedBy(10.dp)) {
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
Text("", Modifier.weight(0.8f))
Text(stringResource(R.string.external_underlying_network), Modifier.weight(1f), style = MaterialTheme.typography.bodyMedium, fontWeight = FontWeight.Bold)
Text(stringResource(R.string.external_tunnel_network), Modifier.weight(1f), style = MaterialTheme.typography.bodyMedium, fontWeight = FontWeight.Bold)
if (results.size > 1) Text("", Modifier.weight(0.8f))
results.indices.forEach { index ->
Text(
stringResource(if (firstRoute + index == 0) R.string.external_underlying_network else R.string.external_tunnel_network),
Modifier.weight(1f), style = MaterialTheme.typography.bodySmall, fontWeight = FontWeight.Bold,
)
}
}
SpeedRow(R.string.cloudflare_speed_ip, results) { it.exitIp }
SpeedRow(R.string.cloudflare_speed_country, results) { it.countryCode?.let(::formatCountryCode) }
SpeedRow(R.string.cloudflare_speed_protocol, results) { it.protocol?.replace(" · ", "\n") }
SpeedRow(R.string.cloudflare_speed_latency, results) { it.latencyMs?.let { ms -> stringResource(R.string.latency_milliseconds, ms) } }
SpeedRow(R.string.cloudflare_speed_download, results) { it.downloadMbps?.let { speed -> stringResource(R.string.cloudflare_speed_value, speed) } }
SpeedRow(R.string.cloudflare_speed_upload, results) { it.uploadMbps?.let { speed -> stringResource(R.string.cloudflare_speed_value, speed) } }
SpeedRow(R.string.cloudflare_speed_ip, results, firstRoute) { it.exitIp }
SpeedRow(R.string.cloudflare_speed_country, results, firstRoute) { it.countryCode?.let(::formatCountryCode) }
SpeedRow(R.string.cloudflare_speed_protocol, results, firstRoute) { it.protocol?.replace(" · ", "\n") }
SpeedRow(R.string.cloudflare_speed_latency, results, firstRoute) { it.latencyMs?.let { ms -> stringResource(R.string.latency_milliseconds, ms) } }
SpeedRow(R.string.cloudflare_speed_download, results, firstRoute) { it.downloadMbps?.let { speed -> stringResource(R.string.cloudflare_speed_value, speed) } }
SpeedRow(R.string.cloudflare_speed_upload, results, firstRoute) { it.uploadMbps?.let { speed -> stringResource(R.string.cloudflare_speed_value, speed) } }
}
}
}

@Composable
private fun SpeedRow(label: Int, results: List<RouteSpeedResult>, value: @Composable (CloudflareRouteResult) -> String?) {
private fun SpeedRow(label: Int, results: List<RouteSpeedResult>, firstRoute: Int, value: @Composable (CloudflareRouteResult) -> String?) {
val title = stringResource(label)
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
Text(title, Modifier.weight(0.8f), style = MaterialTheme.typography.bodyMedium)
Text(title, Modifier.weight(0.8f), style = MaterialTheme.typography.bodySmall)
results.forEachIndexed { index, result ->
val route = stringResource(if (index == 0) R.string.external_underlying_network else R.string.external_tunnel_network)
val route = stringResource(if (firstRoute + index == 0) R.string.external_underlying_network else R.string.external_tunnel_network)
val progressDescription = "$title, $route: ${stringResource(R.string.cloudflare_speed_running)}"
Box(Modifier.weight(1f)) {
val text = value(result.values)
when {
text != null -> if (label == R.string.cloudflare_speed_ip) {
SelectionContainer { Text(text) }
} else Text(text)
SelectionContainer { Text(text, style = MaterialTheme.typography.bodyMedium) }
} else Text(text, style = MaterialTheme.typography.bodyMedium)
result.pending -> CircularProgressIndicator(
modifier = Modifier.size(20.dp).semantics { contentDescription = progressDescription }, strokeWidth = 2.dp,
)
result.failed -> Text(stringResource(R.string.cloudflare_speed_failed_short))
result.unavailable -> Text(stringResource(R.string.unavailable))
else -> Text("—")
result.failed -> Text(stringResource(R.string.cloudflare_speed_failed_short), style = MaterialTheme.typography.bodyMedium)
result.unavailable -> Text(stringResource(R.string.unavailable), style = MaterialTheme.typography.bodyMedium)
else -> Text("—", style = MaterialTheme.typography.bodyMedium)
}
}
}
Expand Down
36 changes: 36 additions & 0 deletions app/src/test/java/net/megaproxy487/SpeedResultsLayoutTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package net.megaproxy487

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.requiredWidth
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.test.assertCountEquals
import androidx.compose.ui.test.onAllNodesWithText
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.dp
import org.junit.Test

class SpeedResultsLayoutTest : MainUiTestBase() {
@Test fun narrowWidthAndLargeFontsSplitRoutesIntoSeparateBlocks() {
val width = mutableStateOf(320)
val fontScale = mutableStateOf(1f)
content {
val density = LocalDensity.current.density
CompositionLocalProvider(LocalDensity provides Density(density, fontScale.value)) {
Box(Modifier.requiredWidth(width.value.dp)) { SpeedTestScreen(activity) {} }
}
}
fun assertBlocks(count: Int) {
compose.onAllNodesWithText(text(R.string.cloudflare_speed_download)).assertCountEquals(count)
compose.onAllNodesWithText(text(R.string.external_underlying_network)).assertCountEquals(1)
compose.onAllNodesWithText(text(R.string.external_tunnel_network)).assertCountEquals(1)
}
assertBlocks(2)
compose.runOnIdle { width.value = 600 }
assertBlocks(1)
compose.runOnIdle { fontScale.value = 2f }
assertBlocks(2)
}
}
Loading