Skip to content

Commit

Permalink
Merge pull request #114 from ooni/results-summary
Browse files Browse the repository at this point in the history
Results summary
  • Loading branch information
sdsantos authored Sep 17, 2024
2 parents c2f06ad + ee36cb6 commit b606597
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="#FF000000"
android:pathData="M160,880L160,800L800,800L800,880L160,880ZM480,720L200,360L360,360L360,80L600,80L600,360L760,360L480,720ZM480,590L596,440L520,440L520,160L440,160L440,440L364,440L480,590ZM480,440L480,440L480,440L480,440L480,440L480,440L480,440Z"/>
</vector>
10 changes: 10 additions & 0 deletions composeApp/src/commonMain/composeResources/drawable/ic_upload.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="#FF000000"
android:pathData="M160,880L160,800L800,800L800,880L160,880ZM360,720L360,440L200,440L480,80L760,440L600,440L600,720L360,720ZM440,640L520,640L520,360L596,360L480,210L364,360L440,360L440,640ZM480,360L480,360L480,360L480,360L480,360L480,360L480,360Z"/>
</vector>
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,14 @@
<string name="TestResults_Overview_Title">Test Results</string>
<string name="TestResults_Overview_Tab_Label">Test Results</string>
<string name="TestResults_Overview_FilterTests">Filter Tests</string>
<string name="TestResults_Overview_Hero_Tests">Tests</string>
<string name="TestResults_Overview_Hero_Networks">Networks</string>
<string name="TestResults_Overview_Hero_DataUsage">Data Usage</string>
<string name="TestResults_Overview_NoTestsHaveBeenRun">No tests have been run yet. Try running one!</string>
<string name="TestResults_UnknownASN">Unknown</string>
<string name="TestResults_NotAvailable">N/A</string>
<string name="TestResults_Summary_Performance_Hero_Upload">Upload</string>
<string name="TestResults_Summary_Performance_Hero_Download">Download</string>

<string name="Settings_Title">Settings</string>
<string name="Settings_Notifications_Label">Notifications</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.AlertDialog
Expand All @@ -19,6 +22,7 @@ import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.VerticalDivider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
Expand All @@ -42,14 +46,22 @@ import ooniprobe.composeapp.generated.resources.Res
import ooniprobe.composeapp.generated.resources.Snackbar_ResultsSomeNotUploaded_Text
import ooniprobe.composeapp.generated.resources.Snackbar_ResultsSomeNotUploaded_UploadAll
import ooniprobe.composeapp.generated.resources.TestResults_Overview_FilterTests
import ooniprobe.composeapp.generated.resources.TestResults_Overview_Hero_DataUsage
import ooniprobe.composeapp.generated.resources.TestResults_Overview_Hero_Networks
import ooniprobe.composeapp.generated.resources.TestResults_Overview_Hero_Tests
import ooniprobe.composeapp.generated.resources.TestResults_Overview_NoTestsHaveBeenRun
import ooniprobe.composeapp.generated.resources.TestResults_Overview_Title
import ooniprobe.composeapp.generated.resources.TestResults_Summary_Performance_Hero_Download
import ooniprobe.composeapp.generated.resources.TestResults_Summary_Performance_Hero_Upload
import ooniprobe.composeapp.generated.resources.ic_delete_all
import ooniprobe.composeapp.generated.resources.ic_download
import ooniprobe.composeapp.generated.resources.ic_upload
import ooniprobe.composeapp.generated.resources.months
import ooniprobe.composeapp.generated.resources.ooni_empty_state
import org.jetbrains.compose.resources.painterResource
import org.jetbrains.compose.resources.stringArrayResource
import org.jetbrains.compose.resources.stringResource
import org.ooni.probe.ui.shared.formatDataUsage

@Composable
fun ResultsScreen(
Expand Down Expand Up @@ -111,6 +123,8 @@ fun ResultsScreen(
} else if (state.results.isEmpty() && state.filter.isAll) {
EmptyResults()
} else {
Summary(state.summary)

LazyColumn {
state.results.forEach { (date, results) ->
stickyHeader(key = date.toString()) {
Expand Down Expand Up @@ -194,6 +208,84 @@ private fun EmptyResults() {
}
}

@Composable
private fun Summary(summary: ResultsViewModel.Summary?) {
if (summary == null) return

Row(
modifier = Modifier
.fillMaxWidth()
.height(IntrinsicSize.Min) // So VerticalDividers don't expand to the whole screen
.padding(16.dp),
) {
Column(
modifier = Modifier.weight(1f),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
stringResource(Res.string.TestResults_Overview_Hero_Tests),
style = MaterialTheme.typography.labelLarge,
modifier = Modifier.padding(bottom = 12.dp),
)
Text(
summary.resultsCount.toString(),
style = MaterialTheme.typography.headlineMedium,
)
}

VerticalDivider(Modifier.padding(4.dp))

Column(
modifier = Modifier.weight(1f),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
stringResource(Res.string.TestResults_Overview_Hero_Networks),
style = MaterialTheme.typography.labelLarge,
modifier = Modifier.padding(bottom = 12.dp),
)
Text(
summary.networksCount.toString(),
style = MaterialTheme.typography.headlineMedium,
)
}

VerticalDivider(Modifier.padding(4.dp))

Column(
modifier = Modifier.weight(1f),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
stringResource(Res.string.TestResults_Overview_Hero_DataUsage),
style = MaterialTheme.typography.labelLarge,
)
Row(
modifier = Modifier.padding(top = 8.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Icon(
painterResource(Res.drawable.ic_download),
contentDescription = stringResource(Res.string.TestResults_Summary_Performance_Hero_Download),
modifier = Modifier.size(20.dp),
)
Text(summary.dataUsageDown.formatDataUsage())
}
Row(
modifier = Modifier.padding(top = 4.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Icon(
painterResource(Res.drawable.ic_upload),
contentDescription = stringResource(Res.string.TestResults_Summary_Performance_Hero_Upload),
modifier = Modifier.size(20.dp),
)
Text(summary.dataUsageUp.formatDataUsage())
}
}
}
}

@Composable
private fun ResultDateHeader(date: LocalDate) {
val monthNames = stringArrayResource(Res.array.months)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class ResultsViewModel(
_state.update {
it.copy(
results = groupedResults,
summary = results.toSummary(),
isLoading = false,
)
}
Expand Down Expand Up @@ -110,12 +111,28 @@ class ResultsViewModel(
ResultFilter.Type.One(TaskOrigin.AutoRun),
),
val results: Map<LocalDate, List<ResultListItem>> = emptyMap(),
val summary: Summary? = null,
val isLoading: Boolean = true,
) {
val anyMissingUpload
get() = results.any { it.value.any { item -> !item.allMeasurementsUploaded } }
}

data class Summary(
val resultsCount: Int,
val networksCount: Int,
val dataUsageUp: Long,
val dataUsageDown: Long,
)

private fun List<ResultListItem>.toSummary() =
Summary(
resultsCount = size,
networksCount = mapNotNull { it.network }.distinct().size,
dataUsageUp = sumOf { it.result.dataUsageUp },
dataUsageDown = sumOf { it.result.dataUsageDown },
)

sealed interface Event {
data class ResultClick(val result: ResultListItem) : Event

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.ooni.probe.ui.shared

import kotlin.math.abs
import kotlin.math.log10
import kotlin.math.pow

fun Long.formatDataUsage(): String {
if (this <= 0) return "0"
val units = arrayOf("B", "kB", "MB", "GB", "TB", "PB", "EB")
val digitGroups = (log10(this.toDouble()) / log10(1024.0)).toInt()
return (this / 1024.0.pow(digitGroups.toDouble())).format() + " " + units[digitGroups]
}

private fun Double.format(decimalChars: Int = 2): String {
val absoluteValue = abs(this).toInt()
val decimalValue = abs((this - absoluteValue) * 10.0.pow(decimalChars)).toInt()
return if (decimalValue == 0) absoluteValue.toString() else "$absoluteValue.$decimalValue"
}

0 comments on commit b606597

Please sign in to comment.