From 8a9ae09f0d3f3a5ef25fafe0cf6d8a1d7305db8b Mon Sep 17 00:00:00 2001 From: andreia Date: Mon, 11 May 2026 16:50:33 +0200 Subject: [PATCH 1/7] add icons to settings options --- .../android/ui/settings/SettingsScreen.kt | 4 ++ .../ui/settings/components/SettingsItem.kt | 48 +++++++++++++++++-- .../settings/components/SettingsSelectItem.kt | 10 +++- .../settings/components/SettingsSwitchItem.kt | 37 ++++++++++++-- app/src/main/res/drawable/ic_cloud_upload.xml | 25 ++++++++++ app/src/main/res/drawable/ic_language.xml | 10 ++++ app/src/main/res/drawable/ic_measurement.xml | 25 ++++++++++ app/src/main/res/drawable/ic_open_in_new.xml | 11 +++++ .../org/groundplatform/ui/theme/Size.kt | 2 + 9 files changed, 162 insertions(+), 10 deletions(-) create mode 100644 app/src/main/res/drawable/ic_cloud_upload.xml create mode 100644 app/src/main/res/drawable/ic_language.xml create mode 100644 app/src/main/res/drawable/ic_measurement.xml create mode 100644 app/src/main/res/drawable/ic_open_in_new.xml diff --git a/app/src/main/java/org/groundplatform/android/ui/settings/SettingsScreen.kt b/app/src/main/java/org/groundplatform/android/ui/settings/SettingsScreen.kt index 2d54eeea3e..ff11086f90 100644 --- a/app/src/main/java/org/groundplatform/android/ui/settings/SettingsScreen.kt +++ b/app/src/main/java/org/groundplatform/android/ui/settings/SettingsScreen.kt @@ -99,6 +99,7 @@ internal fun SettingsScreen( SettingsCategory(stringResource(R.string.general_title)) { // Upload Media SettingsSwitchItem( + trailingIcon = R.drawable.ic_cloud_upload, title = stringResource(R.string.upload_media_title), summary = stringResource(R.string.over_wifi_summary), checked = settings.shouldUploadPhotosOnWifiOnly, @@ -107,6 +108,7 @@ internal fun SettingsScreen( // Language SettingsSelectItem( + trailingIcon = R.drawable.ic_language, title = stringResource(R.string.select_language_title), entriesResId = R.array.language_entries, entryValues = R.array.language_entry_values, @@ -116,6 +118,7 @@ internal fun SettingsScreen( // Measurement Units SettingsSelectItem( + trailingIcon = R.drawable.ic_measurement, title = stringResource(R.string.select_length_title), entriesResId = R.array.length_entries, entryValues = R.array.length_entry_values, @@ -129,6 +132,7 @@ internal fun SettingsScreen( // Help Section SettingsCategory(stringResource(R.string.help_title)) { SettingsItem( + trailingIcon = R.drawable.ic_open_in_new, title = stringResource(R.string.visit_website_title), summary = stringResource(R.string.ground_website), onClick = onVisitWebsiteClick, diff --git a/app/src/main/java/org/groundplatform/android/ui/settings/components/SettingsItem.kt b/app/src/main/java/org/groundplatform/android/ui/settings/components/SettingsItem.kt index 526fdfbe36..7b92a474a0 100644 --- a/app/src/main/java/org/groundplatform/android/ui/settings/components/SettingsItem.kt +++ b/app/src/main/java/org/groundplatform/android/ui/settings/components/SettingsItem.kt @@ -15,38 +15,60 @@ */ package org.groundplatform.android.ui.settings.components +import androidx.annotation.DrawableRes import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource import androidx.compose.ui.semantics.Role import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import org.groundplatform.android.R import org.groundplatform.android.ui.common.ExcludeFromJacocoGeneratedReport import org.groundplatform.ui.theme.AppTheme +import org.groundplatform.ui.theme.sizes /** * A reusable UI component representing a single row in a settings screen. * + * @param modifier The [Modifier] to be applied to the root of this item. + * @param trailingIcon Drawable resource for the icon shown next to the title. * @param title The primary text to be displayed for the setting. * @param summary Optional secondary text to be displayed below the title, providing more detail. * @param onClick The callback to be invoked when the item is clicked. */ @Composable -internal fun SettingsItem(title: String, summary: String? = null, onClick: () -> Unit) { +internal fun SettingsItem( + modifier: Modifier = Modifier, + @DrawableRes trailingIcon: Int, + title: String, + summary: String? = null, + onClick: () -> Unit, +) { Row( modifier = - Modifier.fillMaxWidth().clickable(onClick = onClick, role = Role.Button).padding(16.dp), + modifier.fillMaxWidth().clickable(onClick = onClick, role = Role.Button).padding(16.dp), verticalAlignment = Alignment.CenterVertically, ) { - Column(modifier = Modifier.weight(1f)) { + Icon( + modifier = + Modifier.padding(end = MaterialTheme.sizes.settingsTrailingIconEndPadding) + .size(MaterialTheme.sizes.settingsTrailingIconSize), + painter = painterResource(trailingIcon), + contentDescription = null, + tint = MaterialTheme.colorScheme.onSurfaceVariant, + ) + Column { Text(text = title, style = MaterialTheme.typography.titleMedium) if (summary != null) { Text( @@ -65,8 +87,24 @@ internal fun SettingsItem(title: String, summary: String? = null, onClick: () -> private fun Preview() { AppTheme { Column(verticalArrangement = Arrangement.SpaceEvenly) { - SettingsItem(title = "Name", summary = "Summary", onClick = {}) - SettingsItem(title = "Name", summary = null, onClick = {}) + SettingsItem( + trailingIcon = R.drawable.ic_language, + title = "Name", + summary = "Summary", + onClick = {}, + ) + SettingsItem( + trailingIcon = R.drawable.ic_language, + title = "Name", + summary = null, + onClick = {}, + ) + SettingsItem( + trailingIcon = R.drawable.ic_language, + title = "Language", + summary = "English", + onClick = {}, + ) } } } diff --git a/app/src/main/java/org/groundplatform/android/ui/settings/components/SettingsSelectItem.kt b/app/src/main/java/org/groundplatform/android/ui/settings/components/SettingsSelectItem.kt index c22c5bd0f1..48c2201a4a 100644 --- a/app/src/main/java/org/groundplatform/android/ui/settings/components/SettingsSelectItem.kt +++ b/app/src/main/java/org/groundplatform/android/ui/settings/components/SettingsSelectItem.kt @@ -15,6 +15,8 @@ */ package org.groundplatform.android.ui.settings.components +import androidx.annotation.ArrayRes +import androidx.annotation.DrawableRes import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.widthIn @@ -41,6 +43,7 @@ import org.groundplatform.ui.theme.AppTheme * * When clicked, it displays a dropdown menu with options populated from the provided resource IDs. * + * @param trailingIcon Drawable resource for the icon shown next to the title. * @param title The title of the settings item. * @param entriesResId The resource ID of the string array containing the display labels. * @param entryValues The resource ID of the string array containing the underlying values. @@ -49,9 +52,10 @@ import org.groundplatform.ui.theme.AppTheme */ @Composable internal fun SettingsSelectItem( + @DrawableRes trailingIcon: Int, title: String, - entriesResId: Int, - entryValues: Int, + @ArrayRes entriesResId: Int, + @ArrayRes entryValues: Int, currentValue: String, onValueChanged: (String) -> Unit, ) { @@ -70,6 +74,7 @@ internal fun SettingsSelectItem( Box(modifier = Modifier.fillMaxWidth()) { SettingsItem( + trailingIcon = trailingIcon, title = title, summary = selectedOption?.label ?: "", onClick = { expanded = true }, @@ -102,6 +107,7 @@ internal data class Option(val label: String, val value: String) private fun PreviewSelectItem() { AppTheme { SettingsSelectItem( + trailingIcon = R.drawable.ic_language, title = "Language", entriesResId = R.array.language_entries, entryValues = R.array.language_entry_values, diff --git a/app/src/main/java/org/groundplatform/android/ui/settings/components/SettingsSwitchItem.kt b/app/src/main/java/org/groundplatform/android/ui/settings/components/SettingsSwitchItem.kt index 4a3ea549e4..ab46521bd4 100644 --- a/app/src/main/java/org/groundplatform/android/ui/settings/components/SettingsSwitchItem.kt +++ b/app/src/main/java/org/groundplatform/android/ui/settings/components/SettingsSwitchItem.kt @@ -15,27 +15,35 @@ */ package org.groundplatform.android.ui.settings.components +import androidx.annotation.DrawableRes import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.selection.toggleable +import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource import androidx.compose.ui.semantics.Role import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import org.groundplatform.android.R import org.groundplatform.android.ui.common.ExcludeFromJacocoGeneratedReport import org.groundplatform.ui.theme.AppTheme +import org.groundplatform.ui.theme.sizes /** * A reusable settings item component with a title, optional summary, and a switch toggle. * + * @param modifier The [Modifier] to be applied to the root of this item. + * @param trailingIcon Drawable resource for the icon shown next to the title. * @param title The primary text to be displayed for the setting. * @param summary Optional secondary text providing additional details about the setting. * @param checked Whether the switch is currently in the "on" position. @@ -43,6 +51,8 @@ import org.groundplatform.ui.theme.AppTheme */ @Composable internal fun SettingsSwitchItem( + modifier: Modifier = Modifier, + @DrawableRes trailingIcon: Int, title: String, summary: String? = null, checked: Boolean, @@ -50,11 +60,20 @@ internal fun SettingsSwitchItem( ) { Row( modifier = - Modifier.fillMaxWidth() + modifier + .fillMaxWidth() .toggleable(value = checked, onValueChange = onCheckedChange, role = Role.Switch) .padding(16.dp), verticalAlignment = Alignment.CenterVertically, ) { + Icon( + modifier = + Modifier.padding(end = MaterialTheme.sizes.settingsTrailingIconEndPadding) + .size(MaterialTheme.sizes.settingsTrailingIconSize), + painter = painterResource(trailingIcon), + contentDescription = null, + tint = MaterialTheme.colorScheme.onSurfaceVariant, + ) Column(modifier = Modifier.weight(1f)) { Text(text = title, style = MaterialTheme.typography.titleMedium) if (summary != null) { @@ -75,8 +94,20 @@ internal fun SettingsSwitchItem( private fun Preview() { AppTheme { Column(verticalArrangement = Arrangement.SpaceEvenly) { - SettingsSwitchItem(title = "Name", summary = "Value", checked = true, onCheckedChange = {}) - SettingsSwitchItem(title = "Name", summary = null, checked = false, onCheckedChange = {}) + SettingsSwitchItem( + trailingIcon = R.drawable.ic_cloud_upload, + title = "Name", + summary = "Value", + checked = true, + onCheckedChange = {}, + ) + SettingsSwitchItem( + trailingIcon = R.drawable.ic_cloud_upload, + title = "Name", + summary = null, + checked = false, + onCheckedChange = {}, + ) } } } diff --git a/app/src/main/res/drawable/ic_cloud_upload.xml b/app/src/main/res/drawable/ic_cloud_upload.xml new file mode 100644 index 0000000000..6ddff82dc6 --- /dev/null +++ b/app/src/main/res/drawable/ic_cloud_upload.xml @@ -0,0 +1,25 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_language.xml b/app/src/main/res/drawable/ic_language.xml new file mode 100644 index 0000000000..5792d2c7aa --- /dev/null +++ b/app/src/main/res/drawable/ic_language.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_measurement.xml b/app/src/main/res/drawable/ic_measurement.xml new file mode 100644 index 0000000000..c940828360 --- /dev/null +++ b/app/src/main/res/drawable/ic_measurement.xml @@ -0,0 +1,25 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_open_in_new.xml b/app/src/main/res/drawable/ic_open_in_new.xml new file mode 100644 index 0000000000..d7dabf4c19 --- /dev/null +++ b/app/src/main/res/drawable/ic_open_in_new.xml @@ -0,0 +1,11 @@ + + + diff --git a/core/ui/src/commonMain/kotlin/org/groundplatform/ui/theme/Size.kt b/core/ui/src/commonMain/kotlin/org/groundplatform/ui/theme/Size.kt index fd39f7dda1..123dd8bf12 100644 --- a/core/ui/src/commonMain/kotlin/org/groundplatform/ui/theme/Size.kt +++ b/core/ui/src/commonMain/kotlin/org/groundplatform/ui/theme/Size.kt @@ -30,6 +30,8 @@ data class Size( val progressIndicatorSize: Dp = 24.dp, val progressIndicatorStrokeWidth: Dp = 2.dp, val taskViewPadding: Dp = 16.dp, + val settingsTrailingIconSize: Dp = 24.dp, + val settingsTrailingIconEndPadding: Dp = 16.dp ) internal val LocalSizes = compositionLocalOf { Size() } From 8957c4320a2e16b813c847d3a11cff2f33a804ea Mon Sep 17 00:00:00 2001 From: andreia Date: Mon, 11 May 2026 16:50:50 +0200 Subject: [PATCH 2/7] move language names to untranslated strings --- app/src/main/res/values-es/strings.xml | 8 -------- app/src/main/res/values-fr/strings.xml | 8 +------- app/src/main/res/values-lo/strings.xml | 7 ------- app/src/main/res/values-pt/strings.xml | 8 -------- app/src/main/res/values-th/strings.xml | 7 ------- app/src/main/res/values-vi/strings.xml | 7 ------- app/src/main/res/values/strings-untranslated.xml | 7 +++++++ app/src/main/res/values/strings.xml | 8 -------- 8 files changed, 8 insertions(+), 52 deletions(-) diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 0570cd77db..d95ae9829b 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -205,14 +205,6 @@ Máx. 255 caracteres Área: %s - Inglés - Francés - Español - Portugués - - Vietnamita - Tailandés - Lao Restringido No listado Público diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index bcae668f71..e8a0c731d4 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -186,13 +186,7 @@ Claire 255 caractères maximum Surface: %s - Anglais - Français - Espagnol - Portugais - Vietnamien - Thaï - Lao + Restreint Non répertorié Public diff --git a/app/src/main/res/values-lo/strings.xml b/app/src/main/res/values-lo/strings.xml index 2d5a7eed3f..66ce194676 100644 --- a/app/src/main/res/values-lo/strings.xml +++ b/app/src/main/res/values-lo/strings.xml @@ -182,13 +182,6 @@ ລ້າງ ສູງສຸດ 255 ຕົວອັກສອນ ພື້ນທີ່: %s - ພາສາອັງກິດ - ພາສາຝຣັ່ງ - ພາສາສະແປນ - ພາສາໂປຣຕຸເກດ - ພາສາຫວຽດນາມ - ພາສາໄທ - ພາສາລາວ ຈຳກັດການເຂົ້າເຖິງ ບໍ່ສະແດງໃນລາຍການ ສາທາລະນະ diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 5b4289adfe..026589eabc 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -206,14 +206,6 @@ 255 caracteres no máx. Área: %s - Inglês - Francês - Espanhol - Português - - Vietnamita - Tailandês - Lao Restrito Não listado Público diff --git a/app/src/main/res/values-th/strings.xml b/app/src/main/res/values-th/strings.xml index e919d88be4..851bf81a78 100644 --- a/app/src/main/res/values-th/strings.xml +++ b/app/src/main/res/values-th/strings.xml @@ -184,13 +184,6 @@ ล้างข้อมูล สูงสุด 255 ตัวอักษร พื้นที่: %s - อังกฤษ - ฝรั่งเศส - สเปน - โปรตุเกส - เวียดนาม - ไทย - ลาว จำกัดสิทธิ์ ไม่แสดงสาธารณะ สาธารณะ diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index e5bc958b98..931bc22e97 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -181,13 +181,6 @@ Xóa Tối đa 255 ký tự Diện tích: %s - Tiếng Anh - Tiếng Pháp - Tiếng Tây Ban Nha - Tiếng Bồ Đào Nha - Tiếng Việt - Tiếng Thái - Tiếng Lào Hạn chế Không công khai Công khai diff --git a/app/src/main/res/values/strings-untranslated.xml b/app/src/main/res/values/strings-untranslated.xml index 1ad4ead270..8203045e0d 100644 --- a/app/src/main/res/values/strings-untranslated.xml +++ b/app/src/main/res/values/strings-untranslated.xml @@ -22,4 +22,11 @@ https://groundplatform.org/ ground-dev-sig.web.app /android/survey/ + English + Français + Español + ພາສາລາວ + Português + ไทย + Tiếng Việt diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5eb95adcf9..5c599718b8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -208,14 +208,6 @@ Area: %s - English - French - Spanish - Portuguese - Vietnamese - Thai - Lao - Restricted Unlisted Public From 4d6d2f81f06d472f1acd553f4eca65355798f583 Mon Sep 17 00:00:00 2001 From: andreia Date: Mon, 11 May 2026 16:50:58 +0200 Subject: [PATCH 3/7] add unit tests --- .../settings/components/SettingsItemTest.kt | 86 ++++++++++++ .../components/SettingsSwitchItemTest.kt | 122 ++++++++++++++++++ 2 files changed, 208 insertions(+) create mode 100644 app/src/test/java/org/groundplatform/android/ui/settings/components/SettingsItemTest.kt create mode 100644 app/src/test/java/org/groundplatform/android/ui/settings/components/SettingsSwitchItemTest.kt diff --git a/app/src/test/java/org/groundplatform/android/ui/settings/components/SettingsItemTest.kt b/app/src/test/java/org/groundplatform/android/ui/settings/components/SettingsItemTest.kt new file mode 100644 index 0000000000..a616b8804a --- /dev/null +++ b/app/src/test/java/org/groundplatform/android/ui/settings/components/SettingsItemTest.kt @@ -0,0 +1,86 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.groundplatform.android.ui.settings.components + +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.junit4.v2.createComposeRule +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import org.groundplatform.android.R +import org.groundplatform.ui.theme.AppTheme +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class SettingsItemTest { + + @get:Rule val composeTestRule = createComposeRule() + + @Test + fun `Displays title and summary`() { + composeTestRule.setContent { + AppTheme { + SettingsItem( + trailingIcon = R.drawable.ic_language, + title = "Title", + summary = "Summary", + onClick = {}, + ) + } + } + + composeTestRule.onNodeWithText("Title").assertIsDisplayed() + composeTestRule.onNodeWithText("Summary").assertIsDisplayed() + } + + @Test + fun `Should hide summary when null`() { + composeTestRule.setContent { + AppTheme { + SettingsItem( + trailingIcon = R.drawable.ic_language, + title = "Title", + summary = null, + onClick = {}, + ) + } + } + + composeTestRule.onNodeWithText("Title").assertIsDisplayed() + composeTestRule.onNodeWithText("Summary").assertDoesNotExist() + } + + @Test + fun `Invokes onClick action when clicked`() { + var clicked = false + + composeTestRule.setContent { + AppTheme { + SettingsItem( + trailingIcon = R.drawable.ic_language, + title = "Title", + summary = "Summary", + onClick = { clicked = true }, + ) + } + } + + composeTestRule.onNodeWithText("Title").performClick() + assert(clicked) + } +} diff --git a/app/src/test/java/org/groundplatform/android/ui/settings/components/SettingsSwitchItemTest.kt b/app/src/test/java/org/groundplatform/android/ui/settings/components/SettingsSwitchItemTest.kt new file mode 100644 index 0000000000..90ed5ab820 --- /dev/null +++ b/app/src/test/java/org/groundplatform/android/ui/settings/components/SettingsSwitchItemTest.kt @@ -0,0 +1,122 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.groundplatform.android.ui.settings.components + +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.assertIsOff +import androidx.compose.ui.test.assertIsOn +import androidx.compose.ui.test.junit4.v2.createComposeRule +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import org.groundplatform.android.R +import org.groundplatform.ui.theme.AppTheme +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class SettingsSwitchItemTest { + + @get:Rule val composeTestRule = createComposeRule() + + @Test + fun `Displays title and summary`() { + composeTestRule.setContent { + AppTheme { + SettingsSwitchItem( + trailingIcon = R.drawable.ic_cloud_upload, + title = "Title", + summary = "Summary", + checked = true, + onCheckedChange = {}, + ) + } + } + + composeTestRule.onNodeWithText("Title").assertIsDisplayed() + composeTestRule.onNodeWithText("Summary").assertIsDisplayed() + } + + @Test + fun `Hides summary when null`() { + composeTestRule.setContent { + AppTheme { + SettingsSwitchItem( + trailingIcon = R.drawable.ic_cloud_upload, + title = "Title", + summary = null, + checked = false, + onCheckedChange = {}, + ) + } + } + + composeTestRule.onNodeWithText("Title").assertIsDisplayed() + composeTestRule.onNodeWithText("Summary").assertDoesNotExist() + } + + @Test + fun `Switch should be on when checked state is true`() { + composeTestRule.setContent { + AppTheme { + SettingsSwitchItem( + trailingIcon = R.drawable.ic_cloud_upload, + title = "Title", + checked = true, + onCheckedChange = {}, + ) + } + } + + composeTestRule.onNodeWithText("Title").assertIsOn() + } + + @Test + fun `Switch should be off when checked state is false`() { + composeTestRule.setContent { + AppTheme { + SettingsSwitchItem( + trailingIcon = R.drawable.ic_cloud_upload, + title = "Title", + checked = false, + onCheckedChange = {}, + ) + } + } + + composeTestRule.onNodeWithText("Title").assertIsOff() + } + + @Test + fun `Invokes onCheckedChange with the correct value when clicked on`() { + var newValue: Boolean? = null + + composeTestRule.setContent { + AppTheme { + SettingsSwitchItem( + trailingIcon = R.drawable.ic_cloud_upload, + title = "Title", + checked = false, + onCheckedChange = { newValue = it }, + ) + } + } + + composeTestRule.onNodeWithText("Title").performClick() + assert(newValue == true) + } +} From 90b3da2cf4ce3b30d7aac06848f7c0aeaa58a2ac Mon Sep 17 00:00:00 2001 From: andreia Date: Mon, 11 May 2026 17:03:06 +0200 Subject: [PATCH 4/7] fix code style issues --- app/src/main/res/drawable/ic_cloud_upload.xml | 2 ++ app/src/main/res/drawable/ic_language.xml | 17 +++++++++++++++++ app/src/main/res/drawable/ic_measurement.xml | 2 ++ app/src/main/res/drawable/ic_open_in_new.xml | 17 +++++++++++++++++ .../kotlin/org/groundplatform/ui/theme/Size.kt | 2 +- 5 files changed, 39 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/drawable/ic_cloud_upload.xml b/app/src/main/res/drawable/ic_cloud_upload.xml index 6ddff82dc6..d3fc111e32 100644 --- a/app/src/main/res/drawable/ic_cloud_upload.xml +++ b/app/src/main/res/drawable/ic_cloud_upload.xml @@ -1,3 +1,5 @@ + + + Date: Mon, 11 May 2026 17:30:36 +0200 Subject: [PATCH 5/7] fix lint warnings for non-translatable strings --- app/src/main/res/values/strings-untranslated.xml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/src/main/res/values/strings-untranslated.xml b/app/src/main/res/values/strings-untranslated.xml index 8203045e0d..db3f97b500 100644 --- a/app/src/main/res/values/strings-untranslated.xml +++ b/app/src/main/res/values/strings-untranslated.xml @@ -22,11 +22,11 @@ https://groundplatform.org/ ground-dev-sig.web.app /android/survey/ - English - Français - Español - ພາສາລາວ - Português - ไทย - Tiếng Việt + English + Français + Español + ພາສາລາວ + Português + ไทย + Tiếng Việt From 76465a049607a9c28bcdcccaf608b997a39a1adc Mon Sep 17 00:00:00 2001 From: andreia Date: Tue, 12 May 2026 10:02:29 +0200 Subject: [PATCH 6/7] fix setting icon naming --- .../android/ui/settings/SettingsScreen.kt | 8 ++++---- .../ui/settings/components/SettingsItem.kt | 16 ++++++++-------- .../ui/settings/components/SettingsSelectItem.kt | 8 ++++---- .../ui/settings/components/SettingsSwitchItem.kt | 14 +++++++------- .../ui/settings/components/SettingsItemTest.kt | 6 +++--- .../components/SettingsSwitchItemTest.kt | 10 +++++----- .../kotlin/org/groundplatform/ui/theme/Size.kt | 4 ++-- 7 files changed, 33 insertions(+), 33 deletions(-) diff --git a/app/src/main/java/org/groundplatform/android/ui/settings/SettingsScreen.kt b/app/src/main/java/org/groundplatform/android/ui/settings/SettingsScreen.kt index ff11086f90..75192be703 100644 --- a/app/src/main/java/org/groundplatform/android/ui/settings/SettingsScreen.kt +++ b/app/src/main/java/org/groundplatform/android/ui/settings/SettingsScreen.kt @@ -99,7 +99,7 @@ internal fun SettingsScreen( SettingsCategory(stringResource(R.string.general_title)) { // Upload Media SettingsSwitchItem( - trailingIcon = R.drawable.ic_cloud_upload, + icon = R.drawable.ic_cloud_upload, title = stringResource(R.string.upload_media_title), summary = stringResource(R.string.over_wifi_summary), checked = settings.shouldUploadPhotosOnWifiOnly, @@ -108,7 +108,7 @@ internal fun SettingsScreen( // Language SettingsSelectItem( - trailingIcon = R.drawable.ic_language, + icon = R.drawable.ic_language, title = stringResource(R.string.select_language_title), entriesResId = R.array.language_entries, entryValues = R.array.language_entry_values, @@ -118,7 +118,7 @@ internal fun SettingsScreen( // Measurement Units SettingsSelectItem( - trailingIcon = R.drawable.ic_measurement, + icon = R.drawable.ic_measurement, title = stringResource(R.string.select_length_title), entriesResId = R.array.length_entries, entryValues = R.array.length_entry_values, @@ -132,7 +132,7 @@ internal fun SettingsScreen( // Help Section SettingsCategory(stringResource(R.string.help_title)) { SettingsItem( - trailingIcon = R.drawable.ic_open_in_new, + icon = R.drawable.ic_open_in_new, title = stringResource(R.string.visit_website_title), summary = stringResource(R.string.ground_website), onClick = onVisitWebsiteClick, diff --git a/app/src/main/java/org/groundplatform/android/ui/settings/components/SettingsItem.kt b/app/src/main/java/org/groundplatform/android/ui/settings/components/SettingsItem.kt index 7b92a474a0..f26986bffb 100644 --- a/app/src/main/java/org/groundplatform/android/ui/settings/components/SettingsItem.kt +++ b/app/src/main/java/org/groundplatform/android/ui/settings/components/SettingsItem.kt @@ -42,7 +42,7 @@ import org.groundplatform.ui.theme.sizes * A reusable UI component representing a single row in a settings screen. * * @param modifier The [Modifier] to be applied to the root of this item. - * @param trailingIcon Drawable resource for the icon shown next to the title. + * @param icon Drawable resource for the icon shown next to the title. * @param title The primary text to be displayed for the setting. * @param summary Optional secondary text to be displayed below the title, providing more detail. * @param onClick The callback to be invoked when the item is clicked. @@ -50,7 +50,7 @@ import org.groundplatform.ui.theme.sizes @Composable internal fun SettingsItem( modifier: Modifier = Modifier, - @DrawableRes trailingIcon: Int, + @DrawableRes icon: Int, title: String, summary: String? = null, onClick: () -> Unit, @@ -62,9 +62,9 @@ internal fun SettingsItem( ) { Icon( modifier = - Modifier.padding(end = MaterialTheme.sizes.settingsTrailingIconEndPadding) - .size(MaterialTheme.sizes.settingsTrailingIconSize), - painter = painterResource(trailingIcon), + Modifier.padding(end = MaterialTheme.sizes.settingsItemIconEndPadding) + .size(MaterialTheme.sizes.settingsItemIconSize), + painter = painterResource(icon), contentDescription = null, tint = MaterialTheme.colorScheme.onSurfaceVariant, ) @@ -88,19 +88,19 @@ private fun Preview() { AppTheme { Column(verticalArrangement = Arrangement.SpaceEvenly) { SettingsItem( - trailingIcon = R.drawable.ic_language, + icon = R.drawable.ic_language, title = "Name", summary = "Summary", onClick = {}, ) SettingsItem( - trailingIcon = R.drawable.ic_language, + icon = R.drawable.ic_language, title = "Name", summary = null, onClick = {}, ) SettingsItem( - trailingIcon = R.drawable.ic_language, + icon = R.drawable.ic_language, title = "Language", summary = "English", onClick = {}, diff --git a/app/src/main/java/org/groundplatform/android/ui/settings/components/SettingsSelectItem.kt b/app/src/main/java/org/groundplatform/android/ui/settings/components/SettingsSelectItem.kt index 48c2201a4a..03a5a4fd33 100644 --- a/app/src/main/java/org/groundplatform/android/ui/settings/components/SettingsSelectItem.kt +++ b/app/src/main/java/org/groundplatform/android/ui/settings/components/SettingsSelectItem.kt @@ -43,7 +43,7 @@ import org.groundplatform.ui.theme.AppTheme * * When clicked, it displays a dropdown menu with options populated from the provided resource IDs. * - * @param trailingIcon Drawable resource for the icon shown next to the title. + * @param icon Drawable resource for the icon shown next to the title. * @param title The title of the settings item. * @param entriesResId The resource ID of the string array containing the display labels. * @param entryValues The resource ID of the string array containing the underlying values. @@ -52,7 +52,7 @@ import org.groundplatform.ui.theme.AppTheme */ @Composable internal fun SettingsSelectItem( - @DrawableRes trailingIcon: Int, + @DrawableRes icon: Int, title: String, @ArrayRes entriesResId: Int, @ArrayRes entryValues: Int, @@ -74,7 +74,7 @@ internal fun SettingsSelectItem( Box(modifier = Modifier.fillMaxWidth()) { SettingsItem( - trailingIcon = trailingIcon, + icon = icon, title = title, summary = selectedOption?.label ?: "", onClick = { expanded = true }, @@ -107,7 +107,7 @@ internal data class Option(val label: String, val value: String) private fun PreviewSelectItem() { AppTheme { SettingsSelectItem( - trailingIcon = R.drawable.ic_language, + icon = R.drawable.ic_language, title = "Language", entriesResId = R.array.language_entries, entryValues = R.array.language_entry_values, diff --git a/app/src/main/java/org/groundplatform/android/ui/settings/components/SettingsSwitchItem.kt b/app/src/main/java/org/groundplatform/android/ui/settings/components/SettingsSwitchItem.kt index ab46521bd4..a1dfb8d4fb 100644 --- a/app/src/main/java/org/groundplatform/android/ui/settings/components/SettingsSwitchItem.kt +++ b/app/src/main/java/org/groundplatform/android/ui/settings/components/SettingsSwitchItem.kt @@ -43,7 +43,7 @@ import org.groundplatform.ui.theme.sizes * A reusable settings item component with a title, optional summary, and a switch toggle. * * @param modifier The [Modifier] to be applied to the root of this item. - * @param trailingIcon Drawable resource for the icon shown next to the title. + * @param icon Drawable resource for the icon shown next to the title. * @param title The primary text to be displayed for the setting. * @param summary Optional secondary text providing additional details about the setting. * @param checked Whether the switch is currently in the "on" position. @@ -52,7 +52,7 @@ import org.groundplatform.ui.theme.sizes @Composable internal fun SettingsSwitchItem( modifier: Modifier = Modifier, - @DrawableRes trailingIcon: Int, + @DrawableRes icon: Int, title: String, summary: String? = null, checked: Boolean, @@ -68,9 +68,9 @@ internal fun SettingsSwitchItem( ) { Icon( modifier = - Modifier.padding(end = MaterialTheme.sizes.settingsTrailingIconEndPadding) - .size(MaterialTheme.sizes.settingsTrailingIconSize), - painter = painterResource(trailingIcon), + Modifier.padding(end = MaterialTheme.sizes.settingsItemIconEndPadding) + .size(MaterialTheme.sizes.settingsItemIconSize), + painter = painterResource(icon), contentDescription = null, tint = MaterialTheme.colorScheme.onSurfaceVariant, ) @@ -95,14 +95,14 @@ private fun Preview() { AppTheme { Column(verticalArrangement = Arrangement.SpaceEvenly) { SettingsSwitchItem( - trailingIcon = R.drawable.ic_cloud_upload, + icon = R.drawable.ic_cloud_upload, title = "Name", summary = "Value", checked = true, onCheckedChange = {}, ) SettingsSwitchItem( - trailingIcon = R.drawable.ic_cloud_upload, + icon = R.drawable.ic_cloud_upload, title = "Name", summary = null, checked = false, diff --git a/app/src/test/java/org/groundplatform/android/ui/settings/components/SettingsItemTest.kt b/app/src/test/java/org/groundplatform/android/ui/settings/components/SettingsItemTest.kt index a616b8804a..c5b020bccb 100644 --- a/app/src/test/java/org/groundplatform/android/ui/settings/components/SettingsItemTest.kt +++ b/app/src/test/java/org/groundplatform/android/ui/settings/components/SettingsItemTest.kt @@ -36,7 +36,7 @@ class SettingsItemTest { composeTestRule.setContent { AppTheme { SettingsItem( - trailingIcon = R.drawable.ic_language, + icon = R.drawable.ic_language, title = "Title", summary = "Summary", onClick = {}, @@ -53,7 +53,7 @@ class SettingsItemTest { composeTestRule.setContent { AppTheme { SettingsItem( - trailingIcon = R.drawable.ic_language, + icon = R.drawable.ic_language, title = "Title", summary = null, onClick = {}, @@ -72,7 +72,7 @@ class SettingsItemTest { composeTestRule.setContent { AppTheme { SettingsItem( - trailingIcon = R.drawable.ic_language, + icon = R.drawable.ic_language, title = "Title", summary = "Summary", onClick = { clicked = true }, diff --git a/app/src/test/java/org/groundplatform/android/ui/settings/components/SettingsSwitchItemTest.kt b/app/src/test/java/org/groundplatform/android/ui/settings/components/SettingsSwitchItemTest.kt index 90ed5ab820..1df96f6e44 100644 --- a/app/src/test/java/org/groundplatform/android/ui/settings/components/SettingsSwitchItemTest.kt +++ b/app/src/test/java/org/groundplatform/android/ui/settings/components/SettingsSwitchItemTest.kt @@ -38,7 +38,7 @@ class SettingsSwitchItemTest { composeTestRule.setContent { AppTheme { SettingsSwitchItem( - trailingIcon = R.drawable.ic_cloud_upload, + icon = R.drawable.ic_cloud_upload, title = "Title", summary = "Summary", checked = true, @@ -56,7 +56,7 @@ class SettingsSwitchItemTest { composeTestRule.setContent { AppTheme { SettingsSwitchItem( - trailingIcon = R.drawable.ic_cloud_upload, + icon = R.drawable.ic_cloud_upload, title = "Title", summary = null, checked = false, @@ -74,7 +74,7 @@ class SettingsSwitchItemTest { composeTestRule.setContent { AppTheme { SettingsSwitchItem( - trailingIcon = R.drawable.ic_cloud_upload, + icon = R.drawable.ic_cloud_upload, title = "Title", checked = true, onCheckedChange = {}, @@ -90,7 +90,7 @@ class SettingsSwitchItemTest { composeTestRule.setContent { AppTheme { SettingsSwitchItem( - trailingIcon = R.drawable.ic_cloud_upload, + icon = R.drawable.ic_cloud_upload, title = "Title", checked = false, onCheckedChange = {}, @@ -108,7 +108,7 @@ class SettingsSwitchItemTest { composeTestRule.setContent { AppTheme { SettingsSwitchItem( - trailingIcon = R.drawable.ic_cloud_upload, + icon = R.drawable.ic_cloud_upload, title = "Title", checked = false, onCheckedChange = { newValue = it }, diff --git a/core/ui/src/commonMain/kotlin/org/groundplatform/ui/theme/Size.kt b/core/ui/src/commonMain/kotlin/org/groundplatform/ui/theme/Size.kt index e6882d68ac..013d030261 100644 --- a/core/ui/src/commonMain/kotlin/org/groundplatform/ui/theme/Size.kt +++ b/core/ui/src/commonMain/kotlin/org/groundplatform/ui/theme/Size.kt @@ -30,8 +30,8 @@ data class Size( val progressIndicatorSize: Dp = 24.dp, val progressIndicatorStrokeWidth: Dp = 2.dp, val taskViewPadding: Dp = 16.dp, - val settingsTrailingIconSize: Dp = 24.dp, - val settingsTrailingIconEndPadding: Dp = 16.dp, + val settingsItemIconSize: Dp = 24.dp, + val settingsItemIconEndPadding: Dp = 16.dp, ) internal val LocalSizes = compositionLocalOf { Size() } From 390395e296323b3a12ae0ab14b15bafdd4432473 Mon Sep 17 00:00:00 2001 From: andreia Date: Tue, 12 May 2026 10:15:07 +0200 Subject: [PATCH 7/7] re-add weight to SettingsItem Column --- .../ui/settings/components/SettingsItem.kt | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/org/groundplatform/android/ui/settings/components/SettingsItem.kt b/app/src/main/java/org/groundplatform/android/ui/settings/components/SettingsItem.kt index f26986bffb..5595b906bd 100644 --- a/app/src/main/java/org/groundplatform/android/ui/settings/components/SettingsItem.kt +++ b/app/src/main/java/org/groundplatform/android/ui/settings/components/SettingsItem.kt @@ -68,7 +68,7 @@ internal fun SettingsItem( contentDescription = null, tint = MaterialTheme.colorScheme.onSurfaceVariant, ) - Column { + Column(modifier = Modifier.weight(1f)) { Text(text = title, style = MaterialTheme.typography.titleMedium) if (summary != null) { Text( @@ -87,18 +87,8 @@ internal fun SettingsItem( private fun Preview() { AppTheme { Column(verticalArrangement = Arrangement.SpaceEvenly) { - SettingsItem( - icon = R.drawable.ic_language, - title = "Name", - summary = "Summary", - onClick = {}, - ) - SettingsItem( - icon = R.drawable.ic_language, - title = "Name", - summary = null, - onClick = {}, - ) + SettingsItem(icon = R.drawable.ic_language, title = "Name", summary = "Summary", onClick = {}) + SettingsItem(icon = R.drawable.ic_language, title = "Name", summary = null, onClick = {}) SettingsItem( icon = R.drawable.ic_language, title = "Language",