diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index de8a147e04e..48647ab029f 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -4,7 +4,7 @@ 23.8 ----- - +- [*] Optimized POS Local Catalog Settings screen [https://github.com/woocommerce/woocommerce-android/pull/14971] 23.7 ----- diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/settings/details/localcatalog/WooPosSettingsLocalCatalogScreen.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/settings/details/localcatalog/WooPosSettingsLocalCatalogScreen.kt index d96dfb6eabd..544a93c23aa 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/settings/details/localcatalog/WooPosSettingsLocalCatalogScreen.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/settings/details/localcatalog/WooPosSettingsLocalCatalogScreen.kt @@ -30,6 +30,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.unit.dp import androidx.constraintlayout.compose.ConstraintLayout import androidx.constraintlayout.compose.Dimension @@ -88,7 +90,8 @@ private fun WooPosSettingsLocalCatalogScreen( allowCellularDataUpdate = state.allowCellularDataUpdate, onToggleCellularData = onToggleCellularData, isLoading = state.catalogStatus is WooPosSettingsLocalCatalogState.CatalogStatus.Loading || - state.catalogStatus is WooPosSettingsLocalCatalogState.CatalogStatus.RefreshingCatalog + state.catalogStatus is WooPosSettingsLocalCatalogState.CatalogStatus.RefreshingCatalog, + hasCellularCapability = state.hasCellularCapability ) ManualUpdateSection( @@ -155,15 +158,19 @@ private fun CatalogStatusSection( private fun CellularDataSection( allowCellularDataUpdate: Boolean, onToggleCellularData: (Boolean) -> Unit, - isLoading: Boolean + isLoading: Boolean, + hasCellularCapability: Boolean ) { + val isEnabled = hasCellularCapability && !isLoading + val contentAlpha = if (hasCellularCapability) 1f else 0.38f + WooPosCard( modifier = Modifier.fillMaxWidth() ) { ConstraintLayout( modifier = Modifier .fillMaxWidth() - .clickable(enabled = !isLoading) { + .clickable(enabled = isEnabled) { onToggleCellularData(!allowCellularDataUpdate) } .padding(WooPosSpacing.Medium.value) @@ -173,7 +180,7 @@ private fun CellularDataSection( WooPosText( text = stringResource(R.string.woopos_settings_local_catalog_cellular_data), style = WooPosTypography.BodyLarge, - color = MaterialTheme.colorScheme.onSurface, + color = MaterialTheme.colorScheme.onSurface.copy(alpha = contentAlpha), fontWeight = FontWeight.Bold, modifier = Modifier.constrainAs(title) { top.linkTo(parent.top) @@ -186,7 +193,7 @@ private fun CellularDataSection( WooPosText( text = stringResource(R.string.woopos_settings_local_catalog_cellular_data_subtitle), style = WooPosTypography.BodyMedium, - color = MaterialTheme.colorScheme.onSurface, + color = MaterialTheme.colorScheme.onSurface.copy(alpha = contentAlpha), modifier = Modifier.constrainAs(subtitle) { top.linkTo(title.bottom, margin = WooPosSpacing.Small.value) start.linkTo(parent.start) @@ -198,7 +205,7 @@ private fun CellularDataSection( Switch( checked = allowCellularDataUpdate, onCheckedChange = null, - enabled = !isLoading, + enabled = isEnabled, colors = SwitchDefaults.colors( checkedThumbColor = Color(0xFFFFFFFF), checkedTrackColor = MaterialTheme.colorScheme.primaryContainer @@ -391,7 +398,29 @@ fun WooPosSettingsLocalCatalogScreenPreview() { lastUpdate = "2 hours ago", lastFullUpdate = "Yesterday at 3:45 PM" ), - allowCellularDataUpdate = true + allowCellularDataUpdate = true, + hasCellularCapability = true + ), + onToggleCellularData = {}, + onRefreshCatalog = {} + ) + } +} + +@WooPosPreview +@Composable +fun WooPosSettingsLocalCatalogScreenNoCellularPreview() { + WooPosTheme { + WooPosSettingsLocalCatalogScreen( + state = WooPosSettingsLocalCatalogState( + catalogStatus = WooPosSettingsLocalCatalogState.CatalogStatus.Available( + productCount = 1250, + variationCount = 3420, + lastUpdate = "2 hours ago", + lastFullUpdate = "Yesterday at 3:45 PM" + ), + allowCellularDataUpdate = false, + hasCellularCapability = false ), onToggleCellularData = {}, onRefreshCatalog = {} @@ -406,7 +435,8 @@ fun WooPosSettingsLocalCatalogScreenLoadingPreview() { WooPosSettingsLocalCatalogScreen( state = WooPosSettingsLocalCatalogState( catalogStatus = WooPosSettingsLocalCatalogState.CatalogStatus.Loading, - allowCellularDataUpdate = true + allowCellularDataUpdate = true, + hasCellularCapability = true ), onToggleCellularData = {}, onRefreshCatalog = {} @@ -414,14 +444,21 @@ fun WooPosSettingsLocalCatalogScreenLoadingPreview() { } } +class CellularCapabilityPreviewParameterProvider : PreviewParameterProvider { + override val values = sequenceOf(true, false) +} + @WooPosPreview @Composable -fun WooPosSettingsLocalCatalogRefreshingPreview() { +fun WooPosSettingsLocalCatalogRefreshingPreview( + @PreviewParameter(CellularCapabilityPreviewParameterProvider::class) hasCellularCapability: Boolean +) { WooPosTheme { WooPosSettingsLocalCatalogScreen( state = WooPosSettingsLocalCatalogState( catalogStatus = WooPosSettingsLocalCatalogState.CatalogStatus.RefreshingCatalog, - allowCellularDataUpdate = true + allowCellularDataUpdate = true, + hasCellularCapability = hasCellularCapability ), onToggleCellularData = {}, onRefreshCatalog = {} diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/settings/details/localcatalog/WooPosSettingsLocalCatalogState.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/settings/details/localcatalog/WooPosSettingsLocalCatalogState.kt index 886d4fa603d..95e4594bbb0 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/settings/details/localcatalog/WooPosSettingsLocalCatalogState.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/settings/details/localcatalog/WooPosSettingsLocalCatalogState.kt @@ -3,6 +3,7 @@ package com.woocommerce.android.ui.woopos.settings.details.localcatalog data class WooPosSettingsLocalCatalogState( val catalogStatus: CatalogStatus = CatalogStatus.Loading, val allowCellularDataUpdate: Boolean = false, + val hasCellularCapability: Boolean = false, ) { sealed class CatalogStatus { data class Available( diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/settings/details/localcatalog/WooPosSettingsLocalCatalogViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/settings/details/localcatalog/WooPosSettingsLocalCatalogViewModel.kt index 4843a4b68d9..7f4a2229757 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/settings/details/localcatalog/WooPosSettingsLocalCatalogViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/settings/details/localcatalog/WooPosSettingsLocalCatalogViewModel.kt @@ -10,6 +10,7 @@ import com.woocommerce.android.ui.woopos.home.WooPosParentToChildrenEventReceive import com.woocommerce.android.ui.woopos.localcatalog.PosLocalCatalogSyncResult import com.woocommerce.android.ui.woopos.localcatalog.WooPosLocalCatalogSyncRepository import com.woocommerce.android.ui.woopos.localcatalog.WooPosLocalCatalogSyncScheduler +import com.woocommerce.android.ui.woopos.util.WooPosCellularCapabilityDetector import com.woocommerce.android.ui.woopos.util.datastore.WooPosPreferencesRepository import com.woocommerce.android.ui.woopos.util.datastore.WooPosSyncTimestampManager import com.woocommerce.android.ui.woopos.util.format.WooPosDateFormatter @@ -31,11 +32,14 @@ class WooPosSettingsLocalCatalogViewModel @Inject constructor( private val syncScheduler: WooPosLocalCatalogSyncScheduler, private val childToParentEventSender: WooPosChildrenToParentEventSender, private val parentToChildEventReceiver: WooPosParentToChildrenEventReceiver, + private val cellularCapabilityDetector: WooPosCellularCapabilityDetector, ) : ViewModel() { private val _state = MutableStateFlow(WooPosSettingsLocalCatalogState()) val state: StateFlow = _state.asStateFlow() init { + checkCellularCapability() + loadCatalogStatus() listenToCellularDataUpdateValue() @@ -43,6 +47,17 @@ class WooPosSettingsLocalCatalogViewModel @Inject constructor( listenToParentEvents() } + private fun checkCellularCapability() { + val hasCellular = cellularCapabilityDetector.hasCellularCapability() + _state.update { it.copy(hasCellularCapability = hasCellular) } + + if (!hasCellular) { + viewModelScope.launch { + preferencesRepository.setAllowCellularDataUpdate(false) + } + } + } + private fun listenToParentEvents() { viewModelScope.launch { parentToChildEventReceiver.events.collect { event -> diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/WooPosCellularCapabilityDetector.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/WooPosCellularCapabilityDetector.kt new file mode 100644 index 00000000000..848955990c6 --- /dev/null +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/util/WooPosCellularCapabilityDetector.kt @@ -0,0 +1,16 @@ +package com.woocommerce.android.ui.woopos.util + +import android.content.Context +import android.content.pm.PackageManager +import dagger.Reusable +import dagger.hilt.android.qualifiers.ApplicationContext +import javax.inject.Inject + +@Reusable +class WooPosCellularCapabilityDetector @Inject constructor( + @ApplicationContext private val context: Context +) { + fun hasCellularCapability(): Boolean { + return context.packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY) + } +} diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/settings/details/localcatalog/WooPosSettingsLocalCatalogViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/settings/details/localcatalog/WooPosSettingsLocalCatalogViewModelTest.kt index 61751d61b38..f13c991a711 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/settings/details/localcatalog/WooPosSettingsLocalCatalogViewModelTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/settings/details/localcatalog/WooPosSettingsLocalCatalogViewModelTest.kt @@ -45,6 +45,8 @@ class WooPosSettingsLocalCatalogViewModelTest { private val syncScheduler: WooPosLocalCatalogSyncScheduler = mock() private val childToParentEventSender: WooPosChildrenToParentEventSender = mock() private val parentToChildEventReceiver: WooPosParentToChildrenEventReceiver = mock() + private val cellularCapabilityDetector: com.woocommerce.android.ui.woopos.util.WooPosCellularCapabilityDetector = + mock() private val mockSite: SiteModel = mock() private val allowCellularDataFlow = MutableStateFlow(false) @@ -57,6 +59,7 @@ class WooPosSettingsLocalCatalogViewModelTest { whenever(selectedSite.get()).thenReturn(mockSite) whenever(preferencesRepository.allowCellularDataUpdate).thenReturn(allowCellularDataFlow) whenever(parentToChildEventReceiver.events).thenReturn(parentEventsFlow) + whenever(cellularCapabilityDetector.hasCellularCapability()).thenReturn(true) whenever(localCatalogSyncRepository.syncLocalCatalogFull(any())) .thenReturn( PosLocalCatalogSyncResult.Success( @@ -295,6 +298,58 @@ class WooPosSettingsLocalCatalogViewModelTest { .isInstanceOf(WooPosSettingsLocalCatalogState.CatalogStatus.Available::class.java) } + @Test + fun `given device has cellular capability, when init, then hasCellularCapability is true`() = runTest { + // GIVEN + whenever(cellularCapabilityDetector.hasCellularCapability()).thenReturn(true) + + // WHEN + sut = createViewModel() + advanceUntilIdle() + + // THEN + assertThat(sut.state.value.hasCellularCapability).isTrue() + } + + @Test + fun `given device has no cellular capability, when init, then hasCellularCapability is false`() = runTest { + // GIVEN + whenever(cellularCapabilityDetector.hasCellularCapability()).thenReturn(false) + + // WHEN + sut = createViewModel() + advanceUntilIdle() + + // THEN + assertThat(sut.state.value.hasCellularCapability).isFalse() + } + + @Test + fun `given device has no cellular capability, when init, then allowCellularDataUpdate is set to false in preferences`() = runTest { + // GIVEN + whenever(cellularCapabilityDetector.hasCellularCapability()).thenReturn(false) + + // WHEN + sut = createViewModel() + advanceUntilIdle() + + // THEN + verify(preferencesRepository).setAllowCellularDataUpdate(false) + } + + @Test + fun `given device has cellular capability, when init, then allowCellularDataUpdate preference is not modified`() = runTest { + // GIVEN + whenever(cellularCapabilityDetector.hasCellularCapability()).thenReturn(true) + + // WHEN + sut = createViewModel() + advanceUntilIdle() + + // THEN + verify(preferencesRepository, times(0)).setAllowCellularDataUpdate(false) + } + private fun createViewModel() = WooPosSettingsLocalCatalogViewModel( syncTimestampManager = syncTimestampManager, localCatalogSyncRepository = localCatalogSyncRepository, @@ -304,5 +359,6 @@ class WooPosSettingsLocalCatalogViewModelTest { syncScheduler = syncScheduler, childToParentEventSender = childToParentEventSender, parentToChildEventReceiver = parentToChildEventReceiver, + cellularCapabilityDetector = cellularCapabilityDetector, ) }