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 @@ -89,6 +89,7 @@ class AndroidApplication : Application() {
model = "${Build.MANUFACTURER} ${Build.MODEL}",
requestNotificationsPermission = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU,
supportsInAppLanguage = Build.VERSION.SDK_INT >= 33,
canPullToRefresh = true,
sentryDsn =
"https://7a49ffedcb48b9b69705d1ac2c032c69@o155150.ingest.sentry.io/4508325642764288",
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,7 @@
<string name="LoadingScreen_Runv2_Failure">Error</string>
<string name="LoadingScreen_Runv2_Canceled">Link installation cancelled</string>
<string name="DescriptorUpdate_Updates">UPDATES</string>
<string name="DescriptorUpdate_CheckUpdates">Check for Updates</string>
<string name="AddDescriptor_AutoRunDisabled">Auto-run is disabled. Please enable it to run tests automatically.</string>
<string name="Descriptor_LastTestResult">Last Test Result</string>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ class Dependencies(
private val readAssetFile: (String) -> String,
private val databaseDriverFactory: () -> SqlDriver,
private val networkTypeFinder: NetworkTypeFinder,
@VisibleForTesting
@get:VisibleForTesting
val buildDataStore: () -> DataStore<Preferences>,
private val getBatteryState: () -> BatteryState,
val startSingleRunInner: (RunSpecification) -> Unit,
Expand Down Expand Up @@ -541,6 +541,7 @@ class Dependencies(
observeDescriptorUpdateState = descriptorUpdateStateManager::observe,
getAutoRunSettings = getAutoRunSettings::invoke,
batteryOptimization = batteryOptimization,
canPullToRefresh = platformInfo.canPullToRefresh,
)

fun descriptorViewModel(
Expand All @@ -567,6 +568,7 @@ class Dependencies(
observeDescriptorsUpdateState = descriptorUpdateStateManager::observe,
dismissDescriptorReviewNotice = dismissDescriptorReviewNotice::invoke,
undoRejectedDescriptorUpdate = undoRejectedDescriptorUpdate::invoke,
canPullToRefresh = platformInfo.canPullToRefresh,
)

fun logViewModel(onBack: () -> Unit) =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
import org.ooni.engine.Engine
import org.ooni.engine.models.Result
import org.ooni.probe.data.models.DescriptorUpdateOperationState
import org.ooni.probe.data.models.DescriptorsUpdateState
import org.ooni.probe.data.models.InstalledTestDescriptorModel
import org.ooni.probe.data.models.DescriptorUpdateOperationState

class FetchDescriptorsUpdates(
private val fetchDescriptor: suspend (descriptorId: String) -> Result<InstalledTestDescriptorModel?, Engine.MkException>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ data class PlatformInfo(
val knownBatteryState: Boolean = true,
val knownNetworkType: Boolean = true,
val supportsInAppLanguage: Boolean = false,
val canPullToRefresh: Boolean = false,
val sentryDsn: String,
) {
val version get() = "$buildName ($buildNumber)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
Expand All @@ -20,6 +21,7 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.pulltorefresh.PullToRefreshDefaults
import androidx.compose.material3.pulltorefresh.pullToRefresh
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
Expand All @@ -31,6 +33,7 @@ import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.unit.dp
import ooniprobe.composeapp.generated.resources.DescriptorUpdate_CheckUpdates
import ooniprobe.composeapp.generated.resources.Modal_DisableVPN_Title
import ooniprobe.composeapp.generated.resources.Res
import ooniprobe.composeapp.generated.resources.app_name
Expand All @@ -40,6 +43,7 @@ import ooniprobe.composeapp.generated.resources.logo_probe
import org.jetbrains.compose.resources.painterResource
import org.jetbrains.compose.resources.stringResource
import org.jetbrains.compose.ui.tooling.preview.Preview
import org.ooni.probe.data.models.DescriptorType
import org.ooni.probe.data.models.DescriptorUpdateOperationState
import org.ooni.probe.ui.shared.IgnoreBatteryOptimizationDialog
import org.ooni.probe.ui.shared.TestRunErrorMessages
Expand All @@ -60,7 +64,7 @@ fun DashboardScreen(
isRefreshing = state.isRefreshing,
onRefresh = { onEvent(DashboardViewModel.Event.FetchUpdatedDescriptors) },
state = pullRefreshState,
enabled = state.isRefreshEnabled,
enabled = state.isRefreshEnabled && state.canPullToRefresh,
)
.background(MaterialTheme.colorScheme.background),
) {
Expand Down Expand Up @@ -112,7 +116,8 @@ fun DashboardScreen(
Box {
val lazyListState = rememberLazyListState()
LazyColumn(
modifier = Modifier.padding(top = if (isHeightCompact()) 8.dp else 24.dp)
modifier = Modifier
.padding(top = if (isHeightCompact()) 8.dp else 24.dp)
.testTag("Dashboard-List"),
contentPadding = PaddingValues(bottom = 16.dp),
state = lazyListState,
Expand All @@ -121,7 +126,21 @@ fun DashboardScreen(
state.descriptors.forEach { (type, items) ->
if (allSectionsHaveValues && items.isNotEmpty()) {
item(type) {
TestDescriptorSection(type)
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
.padding(top = 16.dp, bottom = 4.dp),
) {
TestDescriptorSection(type, modifier = Modifier.weight(1f))
if (type == DescriptorType.Installed && !state.canPullToRefresh) {
CheckUpdatesButton(
enabled = !state.isRefreshing,
onEvent = onEvent,
)
}
}
}
}
items(items, key = { it.key }) { descriptor ->
Expand All @@ -131,7 +150,9 @@ fun DashboardScreen(
onEvent(DashboardViewModel.Event.DescriptorClicked(descriptor))
},
onUpdateClick = {
onEvent(DashboardViewModel.Event.UpdateDescriptorClicked(descriptor))
onEvent(
DashboardViewModel.Event.UpdateDescriptorClicked(descriptor),
)
},
)
}
Expand Down Expand Up @@ -195,6 +216,27 @@ private fun VpnWarning() {
}
}

@Composable
private fun CheckUpdatesButton(
enabled: Boolean,
onEvent: (DashboardViewModel.Event) -> Unit,
) {
TextButton(
onClick = { onEvent(DashboardViewModel.Event.FetchUpdatedDescriptors) },
enabled = enabled,
contentPadding = PaddingValues(
horizontal = 8.dp,
vertical = 4.dp,
),
modifier = Modifier.defaultMinSize(minHeight = 32.dp),
) {
Text(
stringResource(Res.string.DescriptorUpdate_CheckUpdates),
style = MaterialTheme.typography.labelMedium,
)
}
}

@Preview
@Composable
fun DashboardScreenPreview() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,11 @@ class DashboardViewModel(
dismissDescriptorsUpdateNotice: () -> Unit,
getAutoRunSettings: () -> Flow<AutoRunParameters>,
batteryOptimization: BatteryOptimization,
canPullToRefresh: Boolean,
) : ViewModel() {
private val events = MutableSharedFlow<Event>(extraBufferCapacity = 1)

private val _state = MutableStateFlow(State())
private val _state = MutableStateFlow(State(canPullToRefresh = canPullToRefresh))
val state = _state.asStateFlow()

init {
Expand Down Expand Up @@ -190,6 +191,7 @@ class DashboardViewModel(
val availableUpdates: List<InstalledTestDescriptorModel> = emptyList(),
val descriptorsUpdateOperationState: DescriptorUpdateOperationState = DescriptorUpdateOperationState.Idle,
val showIgnoreBatteryOptimizationNotice: Boolean = false,
val canPullToRefresh: Boolean = true,
) {
val isRefreshing: Boolean
get() = descriptorsUpdateOperationState == DescriptorUpdateOperationState.FetchingUpdates
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
package org.ooni.probe.ui.dashboard

import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import ooniprobe.composeapp.generated.resources.Dashboard_RunV2_Ooni_Title
import ooniprobe.composeapp.generated.resources.Dashboard_RunV2_Title
import ooniprobe.composeapp.generated.resources.Res
import org.jetbrains.compose.resources.stringResource
import org.ooni.probe.data.models.DescriptorType

@Composable
fun TestDescriptorSection(type: DescriptorType) {
fun TestDescriptorSection(
type: DescriptorType,
modifier: Modifier = Modifier,
) {
Text(
stringResource(
when (type) {
Expand All @@ -23,10 +23,6 @@ fun TestDescriptorSection(type: DescriptorType) {
},
).uppercase(),
style = MaterialTheme.typography.labelLarge,
modifier =
Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
.padding(top = 16.dp, bottom = 4.dp),
modifier = modifier,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ import ooniprobe.composeapp.generated.resources.Common_Enable
import ooniprobe.composeapp.generated.resources.Dashboard_Overview_ChooseWebsites
import ooniprobe.composeapp.generated.resources.Dashboard_Overview_Estimated
import ooniprobe.composeapp.generated.resources.Dashboard_Runv2_Overview_ReviewUpdates
import ooniprobe.composeapp.generated.resources.OONIRun_Run
import ooniprobe.composeapp.generated.resources.Descriptor_LastTestResult
import ooniprobe.composeapp.generated.resources.OONIRun_Run
import ooniprobe.composeapp.generated.resources.Res
import ooniprobe.composeapp.generated.resources.ic_timer
import ooniprobe.composeapp.generated.resources.ooni_empty_state
Expand Down Expand Up @@ -85,7 +85,7 @@ fun DescriptorScreen(
isRefreshing = state.isRefreshing,
onRefresh = { onEvent(DescriptorViewModel.Event.FetchUpdatedDescriptor) },
state = pullRefreshState,
enabled = state.descriptor?.source is Descriptor.Source.Installed,
enabled = state.isRefreshEnabled && state.canPullToRefresh,
)
.background(MaterialTheme.colorScheme.background),
) {
Expand Down Expand Up @@ -118,7 +118,7 @@ fun DescriptorScreen(
.fillMaxSize()
.verticalScroll(rememberScrollState())
.padding(WindowInsets.navigationBars.asPaddingValues())
.padding(bottom = 32.dp),
.padding(bottom = 64.dp),
) {
val descriptor = state.descriptor ?: return

Expand Down Expand Up @@ -233,6 +233,7 @@ fun DescriptorScreen(
if (descriptor.source is Descriptor.Source.Installed) {
InstalledDescriptorActionsView(
descriptor = descriptor.source.value,
showCheckUpdatesButton = !state.canPullToRefresh,
onEvent = onEvent,
modifier = Modifier.padding(horizontal = 16.dp),
)
Expand Down Expand Up @@ -304,50 +305,50 @@ private fun DescriptorDetails(
}
}

Row(
modifier = Modifier
.align(Alignment.CenterHorizontally)
.padding(top = 8.dp),
) {
if (descriptor.name == "websites") {
if (descriptor.isExpired) {
ExpiredChip()
} else {
Row(
modifier = Modifier
.align(Alignment.CenterHorizontally)
.padding(top = 8.dp),
) {
if (descriptor.name == "websites") {
OutlinedButton(
onClick = { onEvent(DescriptorViewModel.Event.ChooseWebsitesClicked) },
border = ButtonDefaults
.outlinedButtonBorder(enabled = true)
.copy(brush = SolidColor(onDescriptorColor)),
colors = ButtonDefaults.outlinedButtonColors(contentColor = onDescriptorColor),
modifier = Modifier
.padding(end = 8.dp)
.testTag("Choose-Websites"),
) {
Text(stringResource(Res.string.Dashboard_Overview_ChooseWebsites))
}
}

OutlinedButton(
onClick = { onEvent(DescriptorViewModel.Event.ChooseWebsitesClicked) },
onClick = { onEvent(DescriptorViewModel.Event.RunClicked) },
border = ButtonDefaults
.outlinedButtonBorder(enabled = true)
.copy(brush = SolidColor(onDescriptorColor)),
colors = ButtonDefaults.outlinedButtonColors(contentColor = onDescriptorColor),
modifier = Modifier
.padding(end = 8.dp)
.testTag("Choose-Websites"),
.copy(brush = SolidColor(descriptorColor)),
colors = ButtonDefaults.outlinedButtonColors(
contentColor = descriptorColor,
containerColor = onDescriptorColor,
),
) {
Text(stringResource(Res.string.Dashboard_Overview_ChooseWebsites))
Text(
stringResource(Res.string.OONIRun_Run),
style = MaterialTheme.typography.titleMedium,
)
Icon(
painterResource(Res.drawable.ic_timer),
contentDescription = null,
modifier = Modifier.padding(start = 8.dp),
)
}
}

OutlinedButton(
onClick = { onEvent(DescriptorViewModel.Event.RunClicked) },
border = ButtonDefaults
.outlinedButtonBorder(enabled = true)
.copy(brush = SolidColor(descriptorColor)),
colors = ButtonDefaults.outlinedButtonColors(
contentColor = descriptorColor,
containerColor = onDescriptorColor,
),
) {
Text(
stringResource(Res.string.OONIRun_Run),
style = MaterialTheme.typography.titleMedium,
)
Icon(
painterResource(Res.drawable.ic_timer),
contentDescription = null,
modifier = Modifier.padding(start = 8.dp),
)
}
}

if (descriptor.isExpired) {
ExpiredChip()
}

if (descriptor.updateStatus is UpdateStatus.Updatable) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,11 @@ class DescriptorViewModel(
observeDescriptorsUpdateState: () -> Flow<DescriptorsUpdateState>,
dismissDescriptorReviewNotice: () -> Unit,
undoRejectedDescriptorUpdate: suspend (InstalledTestDescriptorModel.Id) -> Unit,
canPullToRefresh: Boolean,
) : ViewModel() {
private val events = MutableSharedFlow<Event>(extraBufferCapacity = 1)

private val _state = MutableStateFlow(State())
private val _state = MutableStateFlow(State(canPullToRefresh = canPullToRefresh))
val state = _state.asStateFlow()

init {
Expand Down Expand Up @@ -251,6 +252,7 @@ class DescriptorViewModel(
val lastResult: ResultListItem? = null,
val updateOperationState: DescriptorUpdateOperationState = DescriptorUpdateOperationState.Idle,
val isAutoRunEnabled: Boolean = true,
val canPullToRefresh: Boolean = true,
) {
val isRefreshing: Boolean
get() = updateOperationState == DescriptorUpdateOperationState.FetchingUpdates
Expand All @@ -260,6 +262,7 @@ class DescriptorViewModel(
tests.size -> ToggleableState.On
else -> ToggleableState.Indeterminate
}
val isRefreshEnabled get() = descriptor?.source is Descriptor.Source.Installed
}

sealed interface Event {
Expand Down
Loading
Loading