From bcce40659f47b3a01ce75150933d0c042a1f535b Mon Sep 17 00:00:00 2001 From: Shobhit Agarwal Date: Sat, 14 Feb 2026 09:01:23 +0530 Subject: [PATCH 1/9] Migrate DateTaskFragment UI to Jetpack Compose * Create `DateTaskScreen` Composable using `OutlinedTextField` to replace the XML-based layout. * Remove `date_task_frag.xml` and associated data binding logic from `DateTaskFragment`. * Update `DateTaskFragment` to use `createComposeView` and `collectAsStateWithLifecycle` for state management. * Refactor `DateTaskFragmentTest` to use `composeTestRule` for UI verification and interactions. --- .../tasks/date/DateTaskFragment.kt | 67 +++++++-------- .../tasks/date/DateTaskScreen.kt | 83 +++++++++++++++++++ app/src/main/res/layout/date_task_frag.xml | 48 ----------- .../tasks/date/DateTaskFragmentTest.kt | 25 ++---- 4 files changed, 119 insertions(+), 104 deletions(-) create mode 100644 app/src/main/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskScreen.kt delete mode 100644 app/src/main/res/layout/date_task_frag.xml diff --git a/app/src/main/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskFragment.kt b/app/src/main/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskFragment.kt index 37a11ef299..d337592628 100644 --- a/app/src/main/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskFragment.kt +++ b/app/src/main/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskFragment.kt @@ -20,63 +20,52 @@ import android.content.DialogInterface import android.text.format.DateFormat import android.view.LayoutInflater import android.view.View -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.asLiveData +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.platform.LocalContext +import androidx.lifecycle.compose.collectAsStateWithLifecycle import dagger.hilt.android.AndroidEntryPoint -import java.text.SimpleDateFormat -import java.util.Calendar -import kotlinx.coroutines.flow.filterIsInstance -import kotlinx.coroutines.flow.map import org.groundplatform.android.R -import org.groundplatform.android.databinding.DateTaskFragBinding import org.groundplatform.android.model.submission.DateTimeTaskData import org.groundplatform.android.ui.datacollection.components.TaskView import org.groundplatform.android.ui.datacollection.components.TaskViewFactory import org.groundplatform.android.ui.datacollection.tasks.AbstractTaskFragment +import org.groundplatform.android.util.createComposeView import org.jetbrains.annotations.TestOnly +import java.text.SimpleDateFormat +import java.util.Calendar @AndroidEntryPoint class DateTaskFragment : AbstractTaskFragment() { private var datePickerDialog: DatePickerDialog? = null - lateinit var dateText: LiveData - lateinit var dateTextHint: LiveData + override fun onCreateTaskView(inflater: LayoutInflater): TaskView = + TaskViewFactory.createWithHeader(inflater) + + override fun onCreateTaskBody(inflater: LayoutInflater): View = createComposeView { + val taskData by viewModel.taskTaskData.collectAsStateWithLifecycle() + val context = LocalContext.current - override fun onTaskViewAttached() { - super.onTaskViewAttached() - dateText = - viewModel.taskTaskData - .filterIsInstance() - .map { taskData -> - if (taskData != null) { - val calendar = Calendar.getInstance() - calendar.timeInMillis = taskData.timeInMillis - DateFormat.getDateFormat(requireContext()).format(calendar.time) - } else { - "" - } + val dateText = + remember(taskData) { + val dateTimeTaskData = taskData as? DateTimeTaskData + if (dateTimeTaskData != null) { + val calendar = Calendar.getInstance() + calendar.timeInMillis = dateTimeTaskData.timeInMillis + DateFormat.getDateFormat(context).format(calendar.time) + } else { + "" } - .asLiveData() - - dateTextHint = - MutableLiveData().apply { - val dateFormat = DateFormat.getDateFormat(requireContext()) as SimpleDateFormat - val pattern = dateFormat.toPattern() - val hint = pattern.uppercase() - value = hint } - } - override fun onCreateTaskView(inflater: LayoutInflater): TaskView = - TaskViewFactory.createWithHeader(inflater) + val hintText = remember { + val dateFormat = DateFormat.getDateFormat(context) as SimpleDateFormat + val pattern = dateFormat.toPattern() + pattern.uppercase() + } - override fun onCreateTaskBody(inflater: LayoutInflater): View { - val taskBinding = DateTaskFragBinding.inflate(inflater) - taskBinding.lifecycleOwner = this - taskBinding.fragment = this - return taskBinding.root + DateTaskScreen(dateText = dateText, hintText = hintText, onDateClick = { showDateDialog() }) } fun showDateDialog() { diff --git a/app/src/main/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskScreen.kt b/app/src/main/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskScreen.kt new file mode 100644 index 0000000000..2429f0df03 --- /dev/null +++ b/app/src/main/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskScreen.kt @@ -0,0 +1,83 @@ +/* + * 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.datacollection.tasks.date + +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.interaction.PressInteraction +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import org.groundplatform.android.ui.common.ExcludeFromJacocoGeneratedReport +import org.groundplatform.android.ui.theme.AppTheme + +@Composable +fun DateTaskScreen( + dateText: String, + hintText: String, + onDateClick: () -> Unit, + modifier: Modifier = Modifier, +) { + Column(modifier = modifier) { + OutlinedTextField( + value = dateText, + onValueChange = {}, + readOnly = true, + placeholder = { Text(hintText) }, + modifier = Modifier.width(200.dp).testTag("dateInputText"), + interactionSource = + remember { MutableInteractionSource() } + .also { interactionSource -> + LaunchedEffect(interactionSource) { + interactionSource.interactions.collect { + if (it is PressInteraction.Release) { + onDateClick() + } + } + } + }, + ) + } +} + +@Preview(showBackground = true) +@Composable +@ExcludeFromJacocoGeneratedReport +fun DateTaskScreenPreview() { + AppTheme { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + DateTaskScreen(dateText = "", hintText = "DD/MM/YYYY", onDateClick = {}) + Spacer(modifier = Modifier.height(10.dp)) + DateTaskScreen(dateText = "14/02/2026", hintText = "DD/MM/YYYY", onDateClick = {}) + } + } +} diff --git a/app/src/main/res/layout/date_task_frag.xml b/app/src/main/res/layout/date_task_frag.xml deleted file mode 100644 index c683645d87..0000000000 --- a/app/src/main/res/layout/date_task_frag.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/test/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskFragmentTest.kt b/app/src/test/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskFragmentTest.kt index b9ccd583e0..3c212d0c68 100644 --- a/app/src/test/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskFragmentTest.kt +++ b/app/src/test/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskFragmentTest.kt @@ -19,16 +19,13 @@ import android.app.DatePickerDialog import android.view.View import android.view.ViewGroup import android.widget.LinearLayout +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.assertTextContains import androidx.compose.ui.test.isDisplayed import androidx.compose.ui.test.isNotDisplayed +import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText -import androidx.test.espresso.Espresso.onView -import androidx.test.espresso.action.ViewActions.click -import androidx.test.espresso.assertion.ViewAssertions.matches -import androidx.test.espresso.matcher.ViewMatchers.isDisplayed -import androidx.test.espresso.matcher.ViewMatchers.isEnabled -import androidx.test.espresso.matcher.ViewMatchers.withId -import androidx.test.espresso.matcher.ViewMatchers.withText +import androidx.compose.ui.test.performClick import com.google.common.truth.Truth.assertThat import dagger.hilt.android.testing.BindValue import dagger.hilt.android.testing.HiltAndroidTest @@ -46,9 +43,6 @@ import org.junit.runner.RunWith import org.mockito.Mock import org.robolectric.RobolectricTestRunner -// TODO: Add a test for selecting a date and verifying response. -// Issue URL: https://github.com/google/ground-android/issues/2134 - @HiltAndroidTest @RunWith(RobolectricTestRunner::class) class DateTaskFragmentTest : BaseTaskFragmentTest() { @@ -94,10 +88,7 @@ class DateTaskFragmentTest : BaseTaskFragmentTest(job, task) - onView(withId(R.id.user_date_response_text)) - .check(matches(withText(""))) - .check(matches(isDisplayed())) - .check(matches(isEnabled())) + composeTestRule.onNodeWithTag("dateInputText").assertIsDisplayed().assertTextContains("M/D/YY") runner().assertButtonIsDisabled("Next") } @@ -113,7 +104,7 @@ class DateTaskFragmentTest : BaseTaskFragmentTest Date: Sat, 14 Feb 2026 09:17:05 +0530 Subject: [PATCH 2/9] Add TODOs for UX improvements as per mocks --- .../android/ui/datacollection/tasks/date/DateTaskFragment.kt | 5 +++-- .../android/ui/datacollection/tasks/date/DateTaskScreen.kt | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskFragment.kt b/app/src/main/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskFragment.kt index d337592628..4988473cf0 100644 --- a/app/src/main/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskFragment.kt +++ b/app/src/main/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskFragment.kt @@ -25,6 +25,8 @@ import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext import androidx.lifecycle.compose.collectAsStateWithLifecycle import dagger.hilt.android.AndroidEntryPoint +import java.text.SimpleDateFormat +import java.util.Calendar import org.groundplatform.android.R import org.groundplatform.android.model.submission.DateTimeTaskData import org.groundplatform.android.ui.datacollection.components.TaskView @@ -32,8 +34,6 @@ import org.groundplatform.android.ui.datacollection.components.TaskViewFactory import org.groundplatform.android.ui.datacollection.tasks.AbstractTaskFragment import org.groundplatform.android.util.createComposeView import org.jetbrains.annotations.TestOnly -import java.text.SimpleDateFormat -import java.util.Calendar @AndroidEntryPoint class DateTaskFragment : AbstractTaskFragment() { @@ -68,6 +68,7 @@ class DateTaskFragment : AbstractTaskFragment() { DateTaskScreen(dateText = dateText, hintText = hintText, onDateClick = { showDateDialog() }) } + // TODO: Replace with bottom modal date picker. fun showDateDialog() { val calendar = Calendar.getInstance() val year = calendar[Calendar.YEAR] diff --git a/app/src/main/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskScreen.kt b/app/src/main/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskScreen.kt index 2429f0df03..28d9189834 100644 --- a/app/src/main/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskScreen.kt +++ b/app/src/main/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskScreen.kt @@ -36,6 +36,8 @@ import androidx.compose.ui.unit.dp import org.groundplatform.android.ui.common.ExcludeFromJacocoGeneratedReport import org.groundplatform.android.ui.theme.AppTheme +// TODO: Add trailing icon (close logo) for clearing selected date. + @Composable fun DateTaskScreen( dateText: String, @@ -44,6 +46,7 @@ fun DateTaskScreen( modifier: Modifier = Modifier, ) { Column(modifier = modifier) { + // TODO: Replace with simple text field. OutlinedTextField( value = dateText, onValueChange = {}, From 96161ea439ea63bcc26b74cb334d92e995fc2f10 Mon Sep 17 00:00:00 2001 From: Shobhit Agarwal Date: Sat, 14 Feb 2026 09:24:37 +0530 Subject: [PATCH 3/9] Code cleanup --- .../ui/datacollection/tasks/date/DateTaskFragment.kt | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskFragment.kt b/app/src/main/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskFragment.kt index 4988473cf0..3c8ed9fc0f 100644 --- a/app/src/main/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskFragment.kt +++ b/app/src/main/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskFragment.kt @@ -27,6 +27,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import dagger.hilt.android.AndroidEntryPoint import java.text.SimpleDateFormat import java.util.Calendar +import java.util.Date import org.groundplatform.android.R import org.groundplatform.android.model.submission.DateTimeTaskData import org.groundplatform.android.ui.datacollection.components.TaskView @@ -51,25 +52,21 @@ class DateTaskFragment : AbstractTaskFragment() { remember(taskData) { val dateTimeTaskData = taskData as? DateTimeTaskData if (dateTimeTaskData != null) { - val calendar = Calendar.getInstance() - calendar.timeInMillis = dateTimeTaskData.timeInMillis - DateFormat.getDateFormat(context).format(calendar.time) + DateFormat.getDateFormat(context).format(Date(dateTimeTaskData.timeInMillis)) } else { "" } } val hintText = remember { - val dateFormat = DateFormat.getDateFormat(context) as SimpleDateFormat - val pattern = dateFormat.toPattern() - pattern.uppercase() + (DateFormat.getDateFormat(context) as SimpleDateFormat).toPattern().uppercase() } DateTaskScreen(dateText = dateText, hintText = hintText, onDateClick = { showDateDialog() }) } // TODO: Replace with bottom modal date picker. - fun showDateDialog() { + private fun showDateDialog() { val calendar = Calendar.getInstance() val year = calendar[Calendar.YEAR] val month = calendar[Calendar.MONTH] From 23ac03d1d900475623a916666dd62d2a5ccfdf10 Mon Sep 17 00:00:00 2001 From: Shobhit Agarwal Date: Sat, 14 Feb 2026 15:38:33 +0530 Subject: [PATCH 4/9] Remove unused string --- app/src/main/res/values-es/strings.xml | 1 - app/src/main/res/values-fr/strings.xml | 1 - app/src/main/res/values-pt/strings.xml | 1 - app/src/main/res/values-vi/strings.xml | 1 - app/src/main/res/values/strings.xml | 1 - 5 files changed, 5 deletions(-) diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 76ebef5c6b..d9ee407a70 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -65,7 +65,6 @@ Términos de servicio Aceptar Estoy de acuerdo - Fecha Hora Estado de sincronización de datos diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index fb58e817a7..fc8683f4b3 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -57,7 +57,6 @@ Conditions générales Accepter J’accepte - Date l’heure État de synchronisation diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 75ad03d6aa..5a89aebb85 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -65,7 +65,6 @@ Termos de serviço Concordar Concordo - Data Hora Estado da sincronização de dados diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 3531578893..b27caae159 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -57,7 +57,6 @@ Điều khoản dịch vụ Đồng ý Tôi đồng ý - Ngày Thời gian Trạng thái đồng bộ dữ liệu diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 76e9d97bb9..fc2ce09bbb 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -66,7 +66,6 @@ Terms of service Agree I agree - Date Time Data sync status From 8a6aac5d3323f6a56232e68d2749b03e8efd19d2 Mon Sep 17 00:00:00 2001 From: Shobhit Agarwal Date: Sat, 14 Feb 2026 15:43:59 +0530 Subject: [PATCH 5/9] Apply suggestions --- .../tasks/date/DateTaskFragment.kt | 15 ++++++------- .../tasks/date/DateTaskScreen.kt | 21 +++++++++---------- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskFragment.kt b/app/src/main/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskFragment.kt index 3c8ed9fc0f..c7838d9112 100644 --- a/app/src/main/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskFragment.kt +++ b/app/src/main/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskFragment.kt @@ -25,9 +25,6 @@ import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext import androidx.lifecycle.compose.collectAsStateWithLifecycle import dagger.hilt.android.AndroidEntryPoint -import java.text.SimpleDateFormat -import java.util.Calendar -import java.util.Date import org.groundplatform.android.R import org.groundplatform.android.model.submission.DateTimeTaskData import org.groundplatform.android.ui.datacollection.components.TaskView @@ -35,6 +32,9 @@ import org.groundplatform.android.ui.datacollection.components.TaskViewFactory import org.groundplatform.android.ui.datacollection.tasks.AbstractTaskFragment import org.groundplatform.android.util.createComposeView import org.jetbrains.annotations.TestOnly +import java.text.SimpleDateFormat +import java.util.Calendar +import java.util.Date @AndroidEntryPoint class DateTaskFragment : AbstractTaskFragment() { @@ -50,12 +50,9 @@ class DateTaskFragment : AbstractTaskFragment() { val dateText = remember(taskData) { - val dateTimeTaskData = taskData as? DateTimeTaskData - if (dateTimeTaskData != null) { - DateFormat.getDateFormat(context).format(Date(dateTimeTaskData.timeInMillis)) - } else { - "" - } + (taskData as? DateTimeTaskData)?.let { + DateFormat.getDateFormat(context).format(Date(it.timeInMillis)) + } ?: "" } val hintText = remember { diff --git a/app/src/main/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskScreen.kt b/app/src/main/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskScreen.kt index 28d9189834..7aa4983849 100644 --- a/app/src/main/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskScreen.kt +++ b/app/src/main/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskScreen.kt @@ -45,6 +45,15 @@ fun DateTaskScreen( onDateClick: () -> Unit, modifier: Modifier = Modifier, ) { + val interactionSource = remember { MutableInteractionSource() } + LaunchedEffect(interactionSource) { + interactionSource.interactions.collect { interaction -> + if (interaction is PressInteraction.Release) { + onDateClick() + } + } + } + Column(modifier = modifier) { // TODO: Replace with simple text field. OutlinedTextField( @@ -53,17 +62,7 @@ fun DateTaskScreen( readOnly = true, placeholder = { Text(hintText) }, modifier = Modifier.width(200.dp).testTag("dateInputText"), - interactionSource = - remember { MutableInteractionSource() } - .also { interactionSource -> - LaunchedEffect(interactionSource) { - interactionSource.interactions.collect { - if (it is PressInteraction.Release) { - onDateClick() - } - } - } - }, + interactionSource = interactionSource, ) } } From ef8afb8d078fa3743b956852d57c13ed9979e12d Mon Sep 17 00:00:00 2001 From: Shobhit Agarwal Date: Sat, 14 Feb 2026 15:49:24 +0530 Subject: [PATCH 6/9] reformat imports --- .../ui/datacollection/tasks/date/DateTaskFragment.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskFragment.kt b/app/src/main/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskFragment.kt index c7838d9112..e8e91c6e7b 100644 --- a/app/src/main/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskFragment.kt +++ b/app/src/main/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskFragment.kt @@ -25,6 +25,9 @@ import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext import androidx.lifecycle.compose.collectAsStateWithLifecycle import dagger.hilt.android.AndroidEntryPoint +import java.text.SimpleDateFormat +import java.util.Calendar +import java.util.Date import org.groundplatform.android.R import org.groundplatform.android.model.submission.DateTimeTaskData import org.groundplatform.android.ui.datacollection.components.TaskView @@ -32,9 +35,6 @@ import org.groundplatform.android.ui.datacollection.components.TaskViewFactory import org.groundplatform.android.ui.datacollection.tasks.AbstractTaskFragment import org.groundplatform.android.util.createComposeView import org.jetbrains.annotations.TestOnly -import java.text.SimpleDateFormat -import java.util.Calendar -import java.util.Date @AndroidEntryPoint class DateTaskFragment : AbstractTaskFragment() { From a32de15ef65defff15320abe6e65f909095871cc Mon Sep 17 00:00:00 2001 From: Shobhit Agarwal Date: Sun, 15 Feb 2026 09:58:36 +0530 Subject: [PATCH 7/9] Reduce test flakiness --- .../tasks/date/DateTaskFragmentTest.kt | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/app/src/test/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskFragmentTest.kt b/app/src/test/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskFragmentTest.kt index 3c212d0c68..af07c074ca 100644 --- a/app/src/test/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskFragmentTest.kt +++ b/app/src/test/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskFragmentTest.kt @@ -16,6 +16,8 @@ package org.groundplatform.android.ui.datacollection.tasks.date import android.app.DatePickerDialog +import android.content.Context +import android.text.format.DateFormat import android.view.View import android.view.ViewGroup import android.widget.LinearLayout @@ -26,9 +28,11 @@ import androidx.compose.ui.test.isNotDisplayed import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import androidx.test.core.app.ApplicationProvider import com.google.common.truth.Truth.assertThat import dagger.hilt.android.testing.BindValue import dagger.hilt.android.testing.HiltAndroidTest +import java.text.SimpleDateFormat import javax.inject.Inject import org.groundplatform.android.R import org.groundplatform.android.model.job.Job @@ -88,7 +92,10 @@ class DateTaskFragmentTest : BaseTaskFragmentTest(job, task) - composeTestRule.onNodeWithTag("dateInputText").assertIsDisplayed().assertTextContains("M/D/YY") + composeTestRule + .onNodeWithTag("dateInputText") + .assertIsDisplayed() + .assertTextContains(getExpectedDateHint()) runner().assertButtonIsDisabled("Next") } @@ -158,6 +165,13 @@ class DateTaskFragmentTest : BaseTaskFragmentTest(job, task) - composeTestRule.onNodeWithText("M/D/YY").isDisplayed() + composeTestRule.onNodeWithText(getExpectedDateHint()).isDisplayed() + } + + private fun getExpectedDateHint(): String { + val context = ApplicationProvider.getApplicationContext() + val hint = (DateFormat.getDateFormat(context) as SimpleDateFormat).toPattern().uppercase() + assertThat(hint).isNotEmpty() + return hint } } From eaef64e2015ad8103283d29e5a42942513942a84 Mon Sep 17 00:00:00 2001 From: Shobhit Agarwal Date: Sun, 15 Feb 2026 10:12:23 +0530 Subject: [PATCH 8/9] Update e2e test --- .../android/e2etest/drivers/AndroidTestDriver.kt | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/e2eTest/src/main/java/org/groundplatform/android/e2etest/drivers/AndroidTestDriver.kt b/e2eTest/src/main/java/org/groundplatform/android/e2etest/drivers/AndroidTestDriver.kt index b52c7dd78a..ec4621de70 100644 --- a/e2eTest/src/main/java/org/groundplatform/android/e2etest/drivers/AndroidTestDriver.kt +++ b/e2eTest/src/main/java/org/groundplatform/android/e2etest/drivers/AndroidTestDriver.kt @@ -29,6 +29,7 @@ import androidx.compose.ui.test.hasTestTag import androidx.compose.ui.test.hasText import androidx.compose.ui.test.isEnabled import androidx.compose.ui.test.junit4.AndroidComposeTestRule +import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performScrollTo import androidx.compose.ui.test.performTextInput @@ -152,11 +153,7 @@ class AndroidTestDriver( } override fun setDate() { - val resName = composeRule.activity.resources.getResourceEntryName(R.id.user_date_response_text) - val packageName = composeRule.activity.packageName - val textInputField = device.findObject(By.res(packageName, resName)) - textInputField?.click() - + composeRule.onNodeWithTag("dateInputText").performClick() device.wait(Until.findObject(By.clazz(DatePicker::class.java)), DEFAULT_TIMEOUT) device.findObject(By.text("OK")).click() } From c147cb4a76b5c754c91e8f0b5eb7ab0a6f9a6a3a Mon Sep 17 00:00:00 2001 From: Shobhit Agarwal Date: Sun, 15 Feb 2026 10:17:35 +0530 Subject: [PATCH 9/9] Create a var for test tag --- .../ui/datacollection/tasks/date/DateTaskScreen.kt | 4 +++- .../ui/datacollection/tasks/date/DateTaskFragmentTest.kt | 8 ++++---- .../android/e2etest/drivers/AndroidTestDriver.kt | 5 +++-- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskScreen.kt b/app/src/main/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskScreen.kt index 7aa4983849..420aeaddb0 100644 --- a/app/src/main/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskScreen.kt +++ b/app/src/main/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskScreen.kt @@ -36,6 +36,8 @@ import androidx.compose.ui.unit.dp import org.groundplatform.android.ui.common.ExcludeFromJacocoGeneratedReport import org.groundplatform.android.ui.theme.AppTheme +const val DATE_TEXT_TEST_TAG: String = "date task input test tag" + // TODO: Add trailing icon (close logo) for clearing selected date. @Composable @@ -61,7 +63,7 @@ fun DateTaskScreen( onValueChange = {}, readOnly = true, placeholder = { Text(hintText) }, - modifier = Modifier.width(200.dp).testTag("dateInputText"), + modifier = Modifier.width(200.dp).testTag(DATE_TEXT_TEST_TAG), interactionSource = interactionSource, ) } diff --git a/app/src/test/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskFragmentTest.kt b/app/src/test/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskFragmentTest.kt index af07c074ca..ac808563da 100644 --- a/app/src/test/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskFragmentTest.kt +++ b/app/src/test/java/org/groundplatform/android/ui/datacollection/tasks/date/DateTaskFragmentTest.kt @@ -93,7 +93,7 @@ class DateTaskFragmentTest : BaseTaskFragmentTest(job, task) composeTestRule - .onNodeWithTag("dateInputText") + .onNodeWithTag(DATE_TEXT_TEST_TAG) .assertIsDisplayed() .assertTextContains(getExpectedDateHint()) @@ -111,7 +111,7 @@ class DateTaskFragmentTest : BaseTaskFragmentTest