Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Dropdown Behavior in Questionnaire Items #2393

Open
wants to merge 33 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
24404bb
- Make the dropdown non-editable after an option is selected.
qaziabubakar-vd Dec 29, 2023
730cd1b
Show the dropdown once the text is deleted
qaziabubakar-vd Jan 3, 2024
5094e49
Merge branch 'master' into issue-2228-dropdown-non-editable
qaziabubakar-vd Jan 8, 2024
caeae47
Spotless Apply
qaziabubakar-vd Jan 8, 2024
9e30f45
Merge remote-tracking branch 'origin/issue-2228-dropdown-non-editable…
qaziabubakar-vd Jan 8, 2024
3bead63
Merge branch 'master' into issue-2228-dropdown-non-editable
f-odhiambo Jan 15, 2024
a6959c6
Merge branch 'master' into issue-2228-dropdown-non-editable
jingtang10 Feb 6, 2024
dd3ead3
Update datacapture/src/main/java/com/google/android/fhir/datacapture/…
qaziabubakar-vd Feb 26, 2024
d5d7355
Update header_view.xml according to the new design
qaziabubakar-vd Feb 26, 2024
916829e
Merge remote-tracking branch 'origin/issue-2228-dropdown-non-editable…
qaziabubakar-vd Feb 26, 2024
6fbdf2e
Merge branch 'master' into issue-2228-dropdown-non-editable
qaziabubakar-vd Feb 26, 2024
d6bf618
Update DropDownViewHolderFactory
qaziabubakar-vd Feb 26, 2024
ebdfb28
Merge branch 'master' into issue-2228-dropdown-non-editable
qaziabubakar-vd Feb 26, 2024
d6a4545
Update DropDownViewHolderFactory to have a clear icon once the option…
qaziabubakar-vd Feb 26, 2024
c1daa30
Update when the clearIcon is enabled
qaziabubakar-vd Mar 15, 2024
7bb2776
Update clearIcon visibility based on the readOnly property
qaziabubakar-vd Mar 15, 2024
32cb079
clear answer from the questionnaireViewItem when the user taps on the…
qaziabubakar-vd Mar 15, 2024
c9baa38
Add new tests
qaziabubakar-vd Mar 18, 2024
36d16fc
Add new code comments
qaziabubakar-vd Mar 18, 2024
a35e0a7
Merge remote-tracking branch 'Android-FHIR/master' into issue-2228-dr…
qaziabubakar-vd Mar 18, 2024
36a1806
Resolve conflicts
qaziabubakar-vd Mar 18, 2024
b305bb0
Merge branch 'master' into issue-2228-dropdown-non-editable
qaziabubakar-vd Mar 19, 2024
7e68464
Merge branch 'master' into issue-2228-dropdown-non-editable
dubdabasoduba Oct 23, 2024
dd187c3
Merge branch 'master' of github.com:google/android-fhir into issue-22…
FikriMilano Jan 28, 2025
a994dc3
Refactor implementation
FikriMilano Jan 28, 2025
fcf841e
Move clearInputIcon click listener to init function
FikriMilano Jan 28, 2025
12fb10b
Show dropdown once answer is cleared
FikriMilano Jan 28, 2025
d840f61
Remove ripple effect on clear icon
FikriMilano Jan 29, 2025
041d121
Add tests
FikriMilano Jan 29, 2025
c811179
spotless
FikriMilano Jan 29, 2025
3d1149b
Merge branch 'master' of github.com:google/android-fhir into issue-22…
FikriMilano Jan 29, 2025
00d235c
Rename test components
FikriMilano Jan 29, 2025
0e99a42
Merge branch 'master' into issue-2228-dropdown-non-editable
FikriMilano Feb 6, 2025
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
qaziabubakar-vd marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2023 Google LLC
* Copyright 2023-2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -274,6 +274,33 @@ class DropDownViewHolderFactoryEspressoTest {
.isEqualTo(3)
}

@Test
fun shouldShowDropDownWhenClearIconIsClicked() {
val questionnaireViewItem =
QuestionnaireViewItem(
createAnswerOptions("Coding 1", "Coding 2", "Coding 3", "Add", "Subtract"),
responseValueStringOptions(),
validationResult = NotValidated,
answersChangedCallback = { _, _, _, _ -> },
)
val autoCompleteTextView =
viewHolder.itemView.findViewById<AutoCompleteTextView>(R.id.auto_complete)

runOnUI {
viewHolder.bind(questionnaireViewItem)
autoCompleteTextView.setText("Some Text")
}

// Delay the execution to allow the view hierarchy to be fully initialized
Thread.sleep(1000)
MJ1998 marked this conversation as resolved.
Show resolved Hide resolved

// Click the clear icon
onView(withId(R.id.clearIcon)).perform(click())

// Verify that the drop-down is shown
onView(withText("Coding 1")).inRoot(isPlatformPopup()).check(matches(isDisplayed()))
}

@Test
fun shouldReturnFilteredWithNoResultsDropDownMenuItems() {
val questionnaireViewItem =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,17 @@ package com.google.android.fhir.datacapture.views.factories
import android.content.Context
import android.graphics.drawable.Drawable
import android.text.Spanned
import android.text.method.TextKeyListener
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.FrameLayout
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.doOnNextLayout
import androidx.lifecycle.lifecycleScope
import com.google.android.fhir.datacapture.R
import com.google.android.fhir.datacapture.extensions.displayString
Expand All @@ -41,6 +45,7 @@ import com.google.android.fhir.datacapture.views.HeaderView
import com.google.android.fhir.datacapture.views.QuestionnaireViewItem
import com.google.android.material.textfield.MaterialAutoCompleteTextView
import com.google.android.material.textfield.TextInputLayout
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.hl7.fhir.r4.model.QuestionnaireResponse
import timber.log.Timber
Expand All @@ -52,14 +57,28 @@ internal object DropDownViewHolderFactory :
private lateinit var header: HeaderView
private lateinit var textInputLayout: TextInputLayout
private lateinit var autoCompleteTextView: MaterialAutoCompleteTextView
private lateinit var clearInputIcon: FrameLayout
override lateinit var questionnaireViewItem: QuestionnaireViewItem
private lateinit var context: AppCompatActivity

override fun init(itemView: View) {
header = itemView.findViewById(R.id.header)
textInputLayout = itemView.findViewById(R.id.text_input_layout)
autoCompleteTextView = itemView.findViewById(R.id.auto_complete)
clearInputIcon = itemView.findViewById(R.id.clear_input_icon)
context = itemView.context.tryUnwrapContext()!!
autoCompleteTextView.setOnFocusChangeListener { view, hasFocus ->
if (!hasFocus) {
(view.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager).hideSoftInputFromWindow(view.windowToken, 0)
}
}
clearInputIcon.setOnClickListener {
context.lifecycleScope.launch {
delay(200) // to show ripple effect on the icon before clearing the answer
MJ1998 marked this conversation as resolved.
Show resolved Hide resolved
questionnaireViewItem.clearAnswer()
autoCompleteTextView.doOnNextLayout { autoCompleteTextView.showDropDown() }
}
}
}

override fun bind(questionnaireViewItem: QuestionnaireViewItem) {
Expand Down Expand Up @@ -130,6 +149,10 @@ internal object DropDownViewHolderFactory :
}
}
}
val isEditable = questionnaireViewItem.answers.isEmpty()
if (!isEditable) autoCompleteTextView.clearFocus()
autoCompleteTextView.keyListener = if (isEditable) TextKeyListener.getInstance() else null
clearInputIcon.visibility = if (isEditable) View.GONE else View.VISIBLE

displayValidationResult(questionnaireViewItem.validationResult)
}
Expand Down
13 changes: 13 additions & 0 deletions datacapture/src/main/res/drawable/ic_clear.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal"
>
<path
android:fillColor="@android:color/white"
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"
/>
</vector>
47 changes: 36 additions & 11 deletions datacapture/src/main/res/layout/drop_down_view.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/item_margin_horizontal"
Expand All @@ -32,21 +33,45 @@
android:layout_height="wrap_content"
/>

<com.google.android.material.textfield.TextInputLayout
style="?attr/questionnaireDropdownLayoutStyle"
android:id="@+id/text_input_layout"
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
android:layout_height="wrap_content">

<com.google.android.material.textfield.MaterialAutoCompleteTextView
style="?attr/questionnaireDropDownSelectedTextStyle"
android:id="@+id/auto_complete"
android:drawablePadding="@dimen/icon_drawable_padding"
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/text_input_layout"
style="?attr/questionnaireDropdownLayoutStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content">

<com.google.android.material.textfield.MaterialAutoCompleteTextView
android:id="@+id/auto_complete"
style="?attr/questionnaireDropDownSelectedTextStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawablePadding="@dimen/icon_drawable_padding" />

</com.google.android.material.textfield.TextInputLayout>

<FrameLayout
android:id="@+id/clear_input_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
android:background="?attr/selectableItemBackgroundBorderless"
android:layout_gravity="end|center_vertical"
android:padding="@dimen/drop_down_clear_input_icon_ripple_padding"
android:layout_marginTop="@dimen/drop_down_clear_icon_margin_top"
android:layout_marginEnd="@dimen/drop_down_clear_icon_margin_end">

<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="center"
android:src="@drawable/ic_clear"
android:scaleType="fitCenter"
app:tint="#999999"/>

</FrameLayout>

</com.google.android.material.textfield.TextInputLayout>
</FrameLayout>

</LinearLayout>
3 changes: 3 additions & 0 deletions datacapture/src/main/res/values/dimens.xml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@

<!-- Dropdown -->
<dimen name="drop_down_padding">16dp</dimen>
<dimen name="drop_down_clear_icon_margin_end">38dp</dimen>
<dimen name="drop_down_clear_icon_margin_top">4dp</dimen>
<dimen name="drop_down_clear_input_icon_ripple_padding">4dp</dimen>

<!-- Item Answer Media -->
<dimen name="item_answer_media_image_size">48dp</dimen>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package com.google.android.fhir.datacapture.views.factories
import android.view.View
import android.widget.AutoCompleteTextView
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import com.google.android.fhir.datacapture.R
Expand Down Expand Up @@ -319,6 +320,58 @@ class DropDownViewHolderFactoryTest {
.isEqualTo(View.GONE)
}

@Test
fun shouldHideClearIconWhenTextIsEmpty() {
val answerOption =
Questionnaire.QuestionnaireItemAnswerOptionComponent().apply {
value =
Coding().apply {
code = "test-code"
display = "Test Code"
}
}

viewHolder.bind(
QuestionnaireViewItem(
Questionnaire.QuestionnaireItemComponent().apply { addAnswerOption(answerOption) },
QuestionnaireResponse.QuestionnaireResponseItemComponent(),
validationResult = NotValidated,
answersChangedCallback = { _, _, _, _ -> },
),
)

val clearIcon = viewHolder.itemView.findViewById<ImageView>(R.id.clearIcon)
assertThat(clearIcon.visibility).isEqualTo(View.GONE)
}

@Test
fun shouldShowClearIconWhenTextIsNotEmpty() {
val answerOption =
Questionnaire.QuestionnaireItemAnswerOptionComponent().apply {
value =
Coding().apply {
code = "test-code"
display = "Test Code"
}
}

viewHolder.bind(
QuestionnaireViewItem(
Questionnaire.QuestionnaireItemComponent().apply { addAnswerOption(answerOption) },
QuestionnaireResponse.QuestionnaireResponseItemComponent(),
validationResult = NotValidated,
answersChangedCallback = { _, _, _, _ -> },
),
)

val autoCompleteTextView =
viewHolder.itemView.findViewById<AutoCompleteTextView>(R.id.auto_complete)
val clearIcon = viewHolder.itemView.findViewById<ImageView>(R.id.clearIcon)
autoCompleteTextView.setText("Some Text")

assertThat(clearIcon.visibility).isEqualTo(View.VISIBLE)
}

@Test
fun bind_readOnly_shouldDisableView() {
viewHolder.bind(
Expand Down