From e38a0c6c567d611f89ab2b00d0ed175787113f4c Mon Sep 17 00:00:00 2001 From: andresmr Date: Thu, 1 Feb 2024 15:58:07 +0100 Subject: [PATCH 01/47] [ANDROAPP-5800] Replace xml view by ComposeView --- .../searchTrackEntity/SearchTEActivity.java | 14 +++++++------- .../ui/SearchScreenConfigurator.kt | 8 ++++---- app/src/main/res/layout-land/activity_search.xml | 4 ++-- app/src/main/res/layout/activity_search.xml | 4 ++-- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java index 713f3cdecd..f360cf28bd 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java @@ -1,6 +1,7 @@ package org.dhis2.usescases.searchTrackEntity; import static android.view.View.GONE; +import static org.dhis2.usescases.searchTrackEntity.ui.SearchFormScreenKt.initSearchScreen; import android.annotation.SuppressLint; import android.content.Context; @@ -24,7 +25,6 @@ import org.dhis2.bindings.ViewExtensionsKt; import org.dhis2.commons.Constants; import org.dhis2.commons.featureconfig.data.FeatureConfigRepository; -import org.dhis2.commons.featureconfig.model.Feature; import org.dhis2.commons.filters.FilterItem; import org.dhis2.commons.filters.FilterManager; import org.dhis2.commons.filters.Filters; @@ -35,7 +35,6 @@ import org.dhis2.data.forms.dataentry.ProgramAdapter; import org.dhis2.databinding.ActivitySearchBinding; import org.dhis2.databinding.SnackbarMinAttrBinding; -import org.dhis2.form.model.SearchRecords; import org.dhis2.form.ui.FormView; import org.dhis2.ui.ThemeManager; import org.dhis2.usescases.general.ActivityGlobalAbstract; @@ -155,10 +154,10 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { viewModel = new ViewModelProvider(this, viewModelFactory).get(SearchTEIViewModel.class); - initSearchForm(); - binding = DataBindingUtil.setContentView(this, R.layout.activity_search); + initSearchParameters(); + searchScreenConfigurator = new SearchScreenConfigurator( binding, isOpen -> { @@ -359,8 +358,8 @@ public void updateFilters(int totalFilters) { viewModel.refreshData(); } - private void initSearchForm() { - formView = new FormView.Builder() + private void initSearchParameters() { + /*formView = new FormView.Builder() .locationProvider(locationProvider) .onItemChangeListener(action -> { viewModel.updateQueryData(action); @@ -381,7 +380,8 @@ private void initSearchForm() { )) .build(); FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - transaction.replace(R.id.formViewContainer, formView).commit(); + transaction.replace(R.id.searchContainer, formView).commit();*/ + initSearchScreen(binding.searchContainer); } private void configureBottomNavigation() { diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/ui/SearchScreenConfigurator.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/ui/SearchScreenConfigurator.kt index 7653a8bef7..32c2736a9f 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/ui/SearchScreenConfigurator.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/ui/SearchScreenConfigurator.kt @@ -103,7 +103,7 @@ class SearchScreenConfigurator( private fun openFilters() { binding.filterRecyclerLayout.visibility = View.VISIBLE - binding.formViewContainer.visibility = View.GONE + binding.searchContainer.visibility = View.GONE if (isPortrait()) binding.navigationBar.hide() filterIsOpenCallback(true) changeBounds(R.id.filterRecyclerLayout, 16.dp) @@ -111,7 +111,7 @@ class SearchScreenConfigurator( fun closeBackdrop() { binding.filterRecyclerLayout.visibility = View.GONE - binding.formViewContainer.visibility = View.GONE + binding.searchContainer.visibility = View.GONE if (isPortrait()) binding.navigationBar.show() filterIsOpenCallback(false) changeBounds(R.id.backdropGuideTop, 0) @@ -119,10 +119,10 @@ class SearchScreenConfigurator( private fun openSearch() { binding.filterRecyclerLayout.visibility = View.GONE - binding.formViewContainer.visibility = View.VISIBLE + binding.searchContainer.visibility = View.VISIBLE if (isPortrait()) binding.navigationBar.hide() filterIsOpenCallback(false) - changeBounds(R.id.formViewContainer, 0) + changeBounds(R.id.searchContainer, 0) } private fun changeBounds(endID: Int, margin: Int) { diff --git a/app/src/main/res/layout-land/activity_search.xml b/app/src/main/res/layout-land/activity_search.xml index b4109e2e63..1b7c1bd809 100644 --- a/app/src/main/res/layout-land/activity_search.xml +++ b/app/src/main/res/layout-land/activity_search.xml @@ -200,8 +200,8 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> - - Date: Fri, 2 Feb 2024 11:36:46 +0100 Subject: [PATCH 02/47] [ANDROAPP-5800] Set up SearchParameter structure --- .../searchTrackEntity/SearchTEActivity.java | 16 ++++- .../searchTrackEntity/SearchTEModule.java | 16 +++++ .../SearchParametersRepository.kt | 10 +++ .../SearchParametersScreen.kt | 66 +++++++++++++++++++ .../SearchParametersViewModel.kt | 40 +++++++++++ .../SearchParametersViewModelFactory.kt | 16 +++++ .../searchparameters/model/SearchParameter.kt | 5 ++ .../model/SearchParametersUiState.kt | 7 ++ .../provider/ParameterSelectorItemProvider.kt | 38 +++++++++++ 9 files changed, 212 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersRepository.kt create mode 100644 app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt create mode 100644 app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersViewModel.kt create mode 100644 app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersViewModelFactory.kt create mode 100644 app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParameter.kt create mode 100644 app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParametersUiState.kt create mode 100644 app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java index f360cf28bd..d93bae3011 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java @@ -1,7 +1,7 @@ package org.dhis2.usescases.searchTrackEntity; import static android.view.View.GONE; -import static org.dhis2.usescases.searchTrackEntity.ui.SearchFormScreenKt.initSearchScreen; +import static org.dhis2.usescases.searchTrackEntity.searchparameters.SearchParametersScreenKt.initSearchScreen; import android.annotation.SuppressLint; import android.content.Context; @@ -40,6 +40,8 @@ import org.dhis2.usescases.general.ActivityGlobalAbstract; import org.dhis2.usescases.searchTrackEntity.listView.SearchTEList; import org.dhis2.usescases.searchTrackEntity.mapView.SearchTEMap; +import org.dhis2.usescases.searchTrackEntity.searchparameters.SearchParametersViewModel; +import org.dhis2.usescases.searchTrackEntity.searchparameters.SearchParametersViewModelFactory; import org.dhis2.usescases.searchTrackEntity.ui.SearchScreenConfigurator; import org.dhis2.utils.DateUtils; import org.dhis2.utils.OrientationUtilsKt; @@ -76,6 +78,9 @@ public class SearchTEActivity extends ActivityGlobalAbstract implements SearchTE @Inject SearchTeiViewModelFactory viewModelFactory; + @Inject + SearchParametersViewModelFactory searchParametersViewModelFactory; + @Inject SearchNavigator searchNavigator; @@ -100,6 +105,8 @@ public class SearchTEActivity extends ActivityGlobalAbstract implements SearchTE private SearchTEIViewModel viewModel; + private SearchParametersViewModel searchParametersViewModel; + private boolean initSearchNeeded = true; private FormView formView; public SearchTEComponent searchComponent; @@ -154,6 +161,11 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { viewModel = new ViewModelProvider(this, viewModelFactory).get(SearchTEIViewModel.class); + searchParametersViewModel = new ViewModelProvider( + this, + searchParametersViewModelFactory + ).get(SearchParametersViewModel.class); + binding = DataBindingUtil.setContentView(this, R.layout.activity_search); initSearchParameters(); @@ -381,7 +393,7 @@ private void initSearchParameters() { .build(); FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); transaction.replace(R.id.searchContainer, formView).commit();*/ - initSearchScreen(binding.searchContainer); + initSearchScreen(binding.searchContainer, searchParametersViewModel); } private void configureBottomNavigation() { diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEModule.java b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEModule.java index 55640a8dd7..af752bc751 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEModule.java +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEModule.java @@ -62,6 +62,8 @@ import org.dhis2.maps.usecases.MapStyleConfiguration; import org.dhis2.maps.utils.DhisMapUtils; import org.dhis2.ui.ThemeManager; +import org.dhis2.usescases.searchTrackEntity.searchparameters.SearchParametersRepository; +import org.dhis2.usescases.searchTrackEntity.searchparameters.SearchParametersViewModelFactory; import org.dhis2.usescases.searchTrackEntity.ui.mapper.TEICardMapper; import org.dhis2.utils.DateUtils; import org.dhis2.utils.analytics.AnalyticsHelper; @@ -287,6 +289,20 @@ SearchTeiViewModelFactory providesViewModelFactory( ); } + @Provides + @PerActivity + SearchParametersViewModelFactory provideSearchParametersViewModelFactory( + SearchParametersRepository searchParametersRepository + ) { + return new SearchParametersViewModelFactory(searchParametersRepository); + } + + @Provides + @PerActivity + SearchParametersRepository provideSearchParametersRepository() { + return new SearchParametersRepository(); + } + @Provides @PerActivity MapDataRepository mapDataRepository( diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersRepository.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersRepository.kt new file mode 100644 index 0000000000..193d0a9f86 --- /dev/null +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersRepository.kt @@ -0,0 +1,10 @@ +package org.dhis2.usescases.searchTrackEntity.searchparameters + +import org.dhis2.usescases.searchTrackEntity.searchparameters.model.SearchParameter + +class SearchParametersRepository { + + fun searchParameters(): List { + return listOf(SearchParameter("uid")) + } +} diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt new file mode 100644 index 0000000000..da8b66cede --- /dev/null +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt @@ -0,0 +1,66 @@ +package org.dhis2.usescases.searchTrackEntity.searchparameters + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import org.dhis2.usescases.searchTrackEntity.searchparameters.provider.provideParameterSelectorItem +import org.hisp.dhis.mobile.ui.designsystem.component.Button +import org.hisp.dhis.mobile.ui.designsystem.component.ButtonStyle +import org.hisp.dhis.mobile.ui.designsystem.component.parameter.ParameterSelectorItem +import org.hisp.dhis.mobile.ui.designsystem.theme.Shape + +@Composable +fun SearchParametersScreen(viewModel: SearchParametersViewModel) { + Column( + modifier = Modifier + .fillMaxSize() + .background(color = Color.White, shape = Shape.LargeTop), + ) { + Column( + modifier = Modifier + .weight(1F) + .verticalScroll(rememberScrollState()), + ) { + viewModel.uiState.items.forEach { searchParameter -> + ParameterSelectorItem( + model = provideParameterSelectorItem(searchParameter), + ) + } + } + + Button( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp, 8.dp, 16.dp, 8.dp), + style = ButtonStyle.FILLED, + text = "Search", + ) { + } + } +} + +@Preview(showBackground = true) +@Composable +fun SearchFormPreview() { + SearchParametersScreen( + SearchParametersViewModel( + repository = SearchParametersRepository(), + ), + ) +} + +fun initSearchScreen(composeView: ComposeView, viewModel: SearchParametersViewModel) { + composeView.setContent { + SearchParametersScreen(viewModel) + } +} diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersViewModel.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersViewModel.kt new file mode 100644 index 0000000000..b179290353 --- /dev/null +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersViewModel.kt @@ -0,0 +1,40 @@ +package org.dhis2.usescases.searchTrackEntity.searchparameters + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import org.dhis2.usescases.searchTrackEntity.searchparameters.model.SearchParametersUiState +import java.io.IOException + +class SearchParametersViewModel( + val repository: SearchParametersRepository, +) : ViewModel() { + + var uiState by mutableStateOf(SearchParametersUiState()) + private set + + private var fetchJob: Job? = null + + init { + fetchSearchParameters() + } + + private fun fetchSearchParameters() { + fetchJob?.cancel() + fetchJob = viewModelScope.launch { + try { + val searchParameters = repository.searchParameters() + uiState = uiState.copy(items = searchParameters) + } catch (ioe: IOException) { + /*_uiState.update { + val messages = getMessagesFromThrowable(ioe) + it.copy(userMessages = messages) + }*/ + } + } + } +} diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersViewModelFactory.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersViewModelFactory.kt new file mode 100644 index 0000000000..ea06b5802d --- /dev/null +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersViewModelFactory.kt @@ -0,0 +1,16 @@ +package org.dhis2.usescases.searchTrackEntity.searchparameters + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider + +@Suppress("UNCHECKED_CAST") +class SearchParametersViewModelFactory( + private val repository: SearchParametersRepository, +) : ViewModelProvider.Factory { + + override fun create(modelClass: Class): T { + return SearchParametersViewModel( + repository = repository, + ) as T + } +} diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParameter.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParameter.kt new file mode 100644 index 0000000000..b4d97ba8f8 --- /dev/null +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParameter.kt @@ -0,0 +1,5 @@ +package org.dhis2.usescases.searchTrackEntity.searchparameters.model + +data class SearchParameter( + val uid: String, +) diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParametersUiState.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParametersUiState.kt new file mode 100644 index 0000000000..6462208fa1 --- /dev/null +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParametersUiState.kt @@ -0,0 +1,7 @@ +package org.dhis2.usescases.searchTrackEntity.searchparameters.model + +data class SearchParametersUiState( + val items: List = listOf(), +) + +val SearchParametersUiState.isSearchEnable: Boolean get() = items.isNotEmpty() diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt new file mode 100644 index 0000000000..4c8f86f018 --- /dev/null +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt @@ -0,0 +1,38 @@ +package org.dhis2.usescases.searchTrackEntity.searchparameters.provider + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import org.dhis2.usescases.searchTrackEntity.searchparameters.model.SearchParameter +import org.hisp.dhis.mobile.ui.designsystem.component.InputShellState +import org.hisp.dhis.mobile.ui.designsystem.component.InputStyle +import org.hisp.dhis.mobile.ui.designsystem.component.InputText +import org.hisp.dhis.mobile.ui.designsystem.component.parameter.model.ParameterSelectorItemModel + +@Composable +fun provideParameterSelectorItem(searchParameter: SearchParameter): ParameterSelectorItemModel { + var inputTextValue1: String? by remember { mutableStateOf(null) } + + return ParameterSelectorItemModel( + label = "Label", + helper = "Optional", + inputField = { + InputText( + title = "Label", + state = InputShellState.UNFOCUSED, + inputText = inputTextValue1, + inputStyle = InputStyle.ParameterInputStyle(), + onValueChanged = { + inputTextValue1 = it + }, + ) + }, + status = if (inputTextValue1.isNullOrEmpty()) { + ParameterSelectorItemModel.Status.CLOSED + } else { + ParameterSelectorItemModel.Status.OPENED + }, + ) +} From 14447e6b7cf18b1c631ce081f5326b25d25c5afc Mon Sep 17 00:00:00 2001 From: andresmr Date: Fri, 2 Feb 2024 15:13:16 +0100 Subject: [PATCH 03/47] [ANDROAPP-5800] Implement SearchParametersRepository --- .../searchTrackEntity/SearchTEActivity.java | 2 +- .../searchTrackEntity/SearchTEModule.java | 8 +- .../SearchParametersRepository.kt | 112 +++++++++++++++++- .../SearchParametersScreen.kt | 28 +++-- .../SearchParametersViewModel.kt | 14 ++- .../searchparameters/model/SearchParameter.kt | 6 + .../provider/ParameterSelectorItemProvider.kt | 23 ++-- .../org/dhis2/form/data/SearchRepository.kt | 4 +- 8 files changed, 168 insertions(+), 29 deletions(-) diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java index d93bae3011..9d68424676 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java @@ -393,7 +393,7 @@ private void initSearchParameters() { .build(); FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); transaction.replace(R.id.searchContainer, formView).commit();*/ - initSearchScreen(binding.searchContainer, searchParametersViewModel); + initSearchScreen(binding.searchContainer, searchParametersViewModel, initialProgram, tEType); } private void configureBottomNavigation() { diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEModule.java b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEModule.java index af752bc751..50ffe6e19e 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEModule.java +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEModule.java @@ -21,6 +21,7 @@ import org.dhis2.commons.resources.ColorUtils; import org.dhis2.commons.resources.ResourceManager; import org.dhis2.commons.schedulers.SchedulerProvider; +import org.dhis2.commons.viewmodel.DispatcherProvider; import org.dhis2.data.dhislogic.DhisEnrollmentUtils; import org.dhis2.data.dhislogic.DhisPeriodUtils; import org.dhis2.data.enrollment.EnrollmentUiDataHelper; @@ -299,8 +300,11 @@ SearchParametersViewModelFactory provideSearchParametersViewModelFactory( @Provides @PerActivity - SearchParametersRepository provideSearchParametersRepository() { - return new SearchParametersRepository(); + SearchParametersRepository provideSearchParametersRepository( + D2 d2, + DispatcherProvider dispatcherProvider + ) { + return new SearchParametersRepository(d2, dispatcherProvider); } @Provides diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersRepository.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersRepository.kt index 193d0a9f86..e324d2967e 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersRepository.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersRepository.kt @@ -1,10 +1,116 @@ package org.dhis2.usescases.searchTrackEntity.searchparameters +import kotlinx.coroutines.withContext +import org.dhis2.commons.viewmodel.DispatcherProvider +import org.dhis2.form.model.OptionSetConfiguration import org.dhis2.usescases.searchTrackEntity.searchparameters.model.SearchParameter +import org.hisp.dhis.android.core.D2 +import org.hisp.dhis.android.core.arch.repositories.scope.RepositoryScope +import org.hisp.dhis.android.core.common.ValueType -class SearchParametersRepository { +class SearchParametersRepository( + private val d2: D2, + private val dispatcher: DispatcherProvider, - fun searchParameters(): List { - return listOf(SearchParameter("uid")) +) { + + suspend fun searchParameters(programUid: String?, teiTypeUid: String): List = + withContext(dispatcher.io()) { + programUid?.let { + programTrackedEntityAttributes(programUid) + } ?: trackedEntitySearchFields(teiTypeUid) + } + + private fun programTrackedEntityAttributes(programUid: String): List { + val searchableAttributes = d2.programModule().programTrackedEntityAttributes() + .withRenderType() + .byProgram().eq(programUid) + .blockingGet().filter { programAttribute -> + val isSearchable = programAttribute.searchable()!! + val isUnique = d2.trackedEntityModule().trackedEntityAttributes() + .uid(programAttribute.trackedEntityAttribute()!!.uid()) + .blockingGet()?.unique() === java.lang.Boolean.TRUE + isSearchable || isUnique + } + + return searchableAttributes.mapNotNull { programAttribute -> + d2.trackedEntityModule().trackedEntityAttributes() + .uid(programAttribute.trackedEntityAttribute()!!.uid()) + .blockingGet()?.let { attribute -> + + val optionSetConfiguration = attribute.optionSet()?.let { + OptionSetConfiguration.config( + d2.optionModule().options() + .byOptionSetUid().eq(attribute.optionSet()!!.uid()) + .blockingCount(), + ) { + d2.optionModule().options() + .byOptionSetUid().eq(attribute.optionSet()!!.uid()) + .orderBySortOrder(RepositoryScope.OrderByDirection.ASC) + .blockingGet() + } + } + /*fieldViewModelFactory.createForAttribute( + attribute, + programAttribute, + currentSearchValues[attribute.uid()], + true, + optionSetConfiguration, + )*/ + SearchParameter( + uid = attribute.uid(), + label = attribute.displayFormName() ?: "", + valueType = attribute.valueType()!!, + ) + } + }.filter { parameter -> + parameter.valueType !== ValueType.IMAGE && + parameter.valueType !== ValueType.COORDINATE && + parameter.valueType !== ValueType.FILE_RESOURCE + } + } + + private fun trackedEntitySearchFields(teiTypeUid: String): List { + val teTypeAttributes = d2.trackedEntityModule().trackedEntityTypeAttributes() + .byTrackedEntityTypeUid().eq(teiTypeUid) + .bySearchable().isTrue + .blockingGet() + + return teTypeAttributes.mapNotNull { typeAttribute -> + d2.trackedEntityModule().trackedEntityAttributes() + .uid(typeAttribute.trackedEntityAttribute()!!.uid()) + .blockingGet()?.let { attribute -> + + val optionSetConfiguration = attribute.optionSet()?.let { + OptionSetConfiguration.config( + d2.optionModule().options() + .byOptionSetUid().eq(attribute.optionSet()!!.uid()) + .blockingCount(), + ) { + d2.optionModule().options() + .byOptionSetUid().eq(attribute.optionSet()!!.uid()) + .orderBySortOrder(RepositoryScope.OrderByDirection.ASC) + .blockingGet() + } + } + + /*fieldViewModelFactory.createForAttribute( + attribute, + null, + currentSearchValues[attribute.uid()], + true, + optionSetConfiguration, + )*/ + SearchParameter( + attribute.uid(), + attribute.displayFormName() ?: "", + attribute.valueType()!!, + ) + } + }.filter { parameter -> + parameter.valueType !== ValueType.IMAGE && + parameter.valueType !== ValueType.COORDINATE && + parameter.valueType !== ValueType.FILE_RESOURCE + } } } diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt index da8b66cede..cb433c77c0 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt @@ -33,7 +33,12 @@ fun SearchParametersScreen(viewModel: SearchParametersViewModel) { ) { viewModel.uiState.items.forEach { searchParameter -> ParameterSelectorItem( - model = provideParameterSelectorItem(searchParameter), + model = provideParameterSelectorItem( + searchParameter = searchParameter, + onValueChange = { uid, value -> + viewModel.updateParameter(uid, value) + }, + ), ) } } @@ -52,14 +57,23 @@ fun SearchParametersScreen(viewModel: SearchParametersViewModel) { @Preview(showBackground = true) @Composable fun SearchFormPreview() { - SearchParametersScreen( - SearchParametersViewModel( - repository = SearchParametersRepository(), - ), - ) + /* SearchParametersScreen( + SearchParametersViewModel( + repository = SearchParametersRepository(), + ), + )*/ } -fun initSearchScreen(composeView: ComposeView, viewModel: SearchParametersViewModel) { +fun initSearchScreen( + composeView: ComposeView, + viewModel: SearchParametersViewModel, + program: String?, + teiType: String, +) { + viewModel.fetchSearchParameters( + programUid = program, + teiTypeUid = teiType, + ) composeView.setContent { SearchParametersScreen(viewModel) } diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersViewModel.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersViewModel.kt index b179290353..6b0589b178 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersViewModel.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersViewModel.kt @@ -19,15 +19,14 @@ class SearchParametersViewModel( private var fetchJob: Job? = null - init { - fetchSearchParameters() - } - - private fun fetchSearchParameters() { + fun fetchSearchParameters( + programUid: String?, + teiTypeUid: String, + ) { fetchJob?.cancel() fetchJob = viewModelScope.launch { try { - val searchParameters = repository.searchParameters() + val searchParameters = repository.searchParameters(programUid, teiTypeUid) uiState = uiState.copy(items = searchParameters) } catch (ioe: IOException) { /*_uiState.update { @@ -37,4 +36,7 @@ class SearchParametersViewModel( } } } + + fun updateParameter(uid: String, value: String?) { + } } diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParameter.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParameter.kt index b4d97ba8f8..9b971dd61d 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParameter.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParameter.kt @@ -1,5 +1,11 @@ package org.dhis2.usescases.searchTrackEntity.searchparameters.model +import org.hisp.dhis.android.core.common.ValueType + data class SearchParameter( val uid: String, + val label: String, + val valueType: ValueType, + val helper: String = "Optional", + val value: String? = null, ) diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt index 4c8f86f018..f0c12cb46e 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt @@ -12,24 +12,31 @@ import org.hisp.dhis.mobile.ui.designsystem.component.InputText import org.hisp.dhis.mobile.ui.designsystem.component.parameter.model.ParameterSelectorItemModel @Composable -fun provideParameterSelectorItem(searchParameter: SearchParameter): ParameterSelectorItemModel { - var inputTextValue1: String? by remember { mutableStateOf(null) } +fun provideParameterSelectorItem( + searchParameter: SearchParameter, + onValueChange: (uid: String, value: String?) -> Unit, +): ParameterSelectorItemModel { + var inputTextValue: String? by remember(searchParameter.value) { mutableStateOf(null) } return ParameterSelectorItemModel( - label = "Label", - helper = "Optional", + label = searchParameter.label, + helper = searchParameter.helper, inputField = { InputText( - title = "Label", + title = searchParameter.label, state = InputShellState.UNFOCUSED, - inputText = inputTextValue1, + inputText = inputTextValue, inputStyle = InputStyle.ParameterInputStyle(), onValueChanged = { - inputTextValue1 = it + inputTextValue = it + onValueChange( + searchParameter.uid, + it, + ) }, ) }, - status = if (inputTextValue1.isNullOrEmpty()) { + status = if (inputTextValue.isNullOrEmpty()) { ParameterSelectorItemModel.Status.CLOSED } else { ParameterSelectorItemModel.Status.OPENED diff --git a/form/src/main/java/org/dhis2/form/data/SearchRepository.kt b/form/src/main/java/org/dhis2/form/data/SearchRepository.kt index 944b75914e..3296fddb82 100644 --- a/form/src/main/java/org/dhis2/form/data/SearchRepository.kt +++ b/form/src/main/java/org/dhis2/form/data/SearchRepository.kt @@ -77,11 +77,11 @@ class SearchRepository( .withRenderType() .byProgram().eq(programUid) .blockingGet().filter { programAttribute -> - val isSearcheable = programAttribute.searchable()!! + val isSearchable = programAttribute.searchable()!! val isUnique = d2.trackedEntityModule().trackedEntityAttributes() .uid(programAttribute.trackedEntityAttribute()!!.uid()) .blockingGet()?.unique() === java.lang.Boolean.TRUE - isSearcheable || isUnique + isSearchable || isUnique } searchableAttributes.mapNotNull { programAttribute -> d2.trackedEntityModule().trackedEntityAttributes() From bb3acf87126be85080d0641e5b732cb97bc50157 Mon Sep 17 00:00:00 2001 From: andresmr Date: Fri, 2 Feb 2024 18:39:14 +0100 Subject: [PATCH 04/47] [ANDROAPP-5800] update search query --- .../searchTrackEntity/SearchRepository.java | 16 ++-- .../SearchRepositoryImpl.java | 43 ----------- .../searchTrackEntity/SearchTEActivity.java | 74 ++++++------------- .../SearchTEContractsModule.java | 9 +-- .../searchTrackEntity/SearchTEIViewModel.kt | 25 ++----- .../searchTrackEntity/SearchTEPresenter.java | 8 -- .../SearchParametersScreen.kt | 12 +-- .../SearchParametersViewModel.kt | 3 - .../java/org/dhis2/form/model/ActionType.kt | 1 - .../main/java/org/dhis2/form/ui/FormView.kt | 4 - .../java/org/dhis2/form/ui/FormViewModel.kt | 14 ---- .../org/dhis2/form/ui/intent/FormIntent.kt | 4 - 12 files changed, 47 insertions(+), 166 deletions(-) diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchRepository.java b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchRepository.java index cf5675ecd5..7733bde7bb 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchRepository.java +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchRepository.java @@ -9,7 +9,6 @@ import org.dhis2.commons.data.EventViewModel; import org.dhis2.commons.data.SearchTeiModel; import org.dhis2.commons.data.tuples.Pair; -import org.dhis2.commons.filters.sorting.SortingItem; import org.dhis2.data.search.SearchParametersModel; import org.hisp.dhis.android.core.arch.call.D2Progress; import org.hisp.dhis.android.core.organisationunit.OrganisationUnit; @@ -37,8 +36,6 @@ public interface SearchRepository { @NonNull Flowable> searchTeiForMap(SearchParametersModel searchParametersModel, boolean isOnline); - SearchTeiModel getTrackedEntityInfo(String teiUid, Program selectedProgram, SortingItem sortingItem); - @NonNull Observable> saveToEnroll(@NonNull String teiType, @NonNull String orgUnitUID, @NonNull String programUid, @Nullable String teiUid, HashMap queryDatam, Date enrollmentDate, @Nullable String fromRelationshipUid); @@ -52,8 +49,6 @@ public interface SearchRepository { List getEventsForMap(List teis); - EventViewModel getEventInfo(String enrollmentUid); - Observable downloadTei(String teiUid); TeiDownloadResult download(String teiUid, @Nullable String enrollmentUid, String reason); @@ -63,15 +58,22 @@ public interface SearchRepository { TrackedEntitySearchCollectionRepository getFilteredRepository(SearchParametersModel searchParametersModel); void setCurrentProgram(@Nullable String currentProgram); + boolean programStagesHaveCoordinates(String programUid); + boolean teTypeAttributesHaveCoordinates(String typeId); + boolean programAttributesHaveCoordinates(String programUid); + boolean eventsHaveCoordinates(String programUid); List getProgramVisualizationGroups(String programUid); - @Nullable Program getProgram(@Nullable String programUid); - @Nullable String currentProgram(); + @Nullable + Program getProgram(@Nullable String programUid); + + @Nullable + String currentProgram(); @NotNull Map filterQueryForProgram(@NotNull Map queryData, @org.jetbrains.annotations.Nullable String programUid); diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchRepositoryImpl.java b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchRepositoryImpl.java index 2e548590ef..2abaaa70ab 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchRepositoryImpl.java +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchRepositoryImpl.java @@ -670,49 +670,6 @@ private String orgUnitName(String orgUnitUid){ return orgUnitNameCache.get(orgUnitUid); } - @Override - public SearchTeiModel getTrackedEntityInfo(String teiUid, Program selectedProgram, SortingItem sortingItem) { - return transform( - d2.trackedEntityModule().trackedEntitySearch().uid(teiUid).blockingGet(), - selectedProgram, - true, - sortingItem - ); - - } - - @Override - public EventViewModel getEventInfo(String uid) { - Event event = d2.eventModule().events().uid(uid).blockingGet(); - - ProgramStage stage = d2.programModule().programStages() - .uid(event.programStage()) - .blockingGet(); - - OrganisationUnit organisationUnit = d2.organisationUnitModule() - .organisationUnits() - .uid(event.organisationUnit()) - .blockingGet(); - - return new EventViewModel(EventViewModelType.EVENT, - stage, - event, - 0, - null, - true, - true, - organisationUnit.displayName(), - null, - null, - false, - false, - false, - false, - periodUtils.getPeriodUIString(stage.periodType(), event.eventDate() != null ? event.eventDate() : event.dueDate(), Locale.getDefault()), - null - ); - } - @Override public Observable downloadTei(String teiUid) { downloadRepository = d2.trackedEntityModule().trackedEntityInstanceDownloader() diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java index 9d68424676..d3a31d370e 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java @@ -35,7 +35,6 @@ import org.dhis2.data.forms.dataentry.ProgramAdapter; import org.dhis2.databinding.ActivitySearchBinding; import org.dhis2.databinding.SnackbarMinAttrBinding; -import org.dhis2.form.ui.FormView; import org.dhis2.ui.ThemeManager; import org.dhis2.usescases.general.ActivityGlobalAbstract; import org.dhis2.usescases.searchTrackEntity.listView.SearchTEList; @@ -108,7 +107,6 @@ public class SearchTEActivity extends ActivityGlobalAbstract implements SearchTE private SearchParametersViewModel searchParametersViewModel; private boolean initSearchNeeded = true; - private FormView formView; public SearchTEComponent searchComponent; private int initialPage = 0; @@ -185,7 +183,6 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { ViewExtensionsKt.clipWithRoundedCorners(binding.mainComponent, ExtensionsKt.getDp(16)); binding.searchButton.setOnClickListener(v -> { if (OrientationUtilsKt.isPortrait()) searchScreenConfigurator.closeBackdrop(); - formView.onEditionFinish(); viewModel.onSearchClick(minNumberOfAttributes -> { showSnackbar( v, @@ -371,29 +368,11 @@ public void updateFilters(int totalFilters) { } private void initSearchParameters() { - /*formView = new FormView.Builder() - .locationProvider(locationProvider) - .onItemChangeListener(action -> { - viewModel.updateQueryData(action); + initSearchScreen(binding.searchContainer, searchParametersViewModel, initialProgram, tEType, + (uid, value) -> { + viewModel.updateQuery(uid, value); return Unit.INSTANCE; - }) - .activityForResultListener(() -> { - initSearchNeeded = false; - return Unit.INSTANCE; - }) - .onFieldItemsRendered(isEmpty -> Unit.INSTANCE) - .needToForceUpdate(true) - .setActionIconsActivation(false) - .factory(getSupportFragmentManager()) - .setRecords(new SearchRecords( - initialProgram, - tEType, - initialQuery - )) - .build(); - FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - transaction.replace(R.id.searchContainer, formView).commit();*/ - initSearchScreen(binding.searchContainer, searchParametersViewModel, initialProgram, tEType); + }); } private void configureBottomNavigation() { @@ -403,36 +382,33 @@ private void configureBottomNavigation() { } binding.mainComponent.setVisibility(View.VISIBLE); switch (item.getItemId()) { - case R.id.navigation_list_view: + case R.id.navigation_list_view -> { viewModel.setListScreen(); showList(); showSearchAndFilterButtons(); - break; - case R.id.navigation_map_view: - networkUtils.performIfOnline( - this, - () -> { - presenter.trackSearchMapVisualization(); - viewModel.setMapScreen(); - showMap(); - showSearchAndFilterButtons(); - return null; - }, - () -> { - binding.navigationBar.selectItemAt(0); - return null; - }, - getString(R.string.msg_network_connection_maps) - ); - - break; - case R.id.navigation_analytics: + } + case R.id.navigation_map_view -> networkUtils.performIfOnline( + this, + () -> { + presenter.trackSearchMapVisualization(); + viewModel.setMapScreen(); + showMap(); + showSearchAndFilterButtons(); + return null; + }, + () -> { + binding.navigationBar.selectItemAt(0); + return null; + }, + getString(R.string.msg_network_connection_maps) + ); + case R.id.navigation_analytics -> { presenter.trackSearchAnalytics(); viewModel.setAnalyticsScreen(); fromAnalytics = true; showAnalytics(); hideSearchAndFilterButtons(); - break; + } } return true; }); @@ -662,10 +638,6 @@ public void clearFilters() { if (viewModel.filterIsOpen()) { filtersAdapter.notifyDataSetChanged(); FilterManager.getInstance().clearAllFilters(); - } else { - formView.onEditionFinish(); - formView.clearValues(); - presenter.onClearClick(); } } diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEContractsModule.java b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEContractsModule.java index 980e3223eb..d17adb100b 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEContractsModule.java +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEContractsModule.java @@ -5,21 +5,18 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import org.dhis2.maps.model.StageStyle; -import org.dhis2.form.model.FieldUiModel; -import org.dhis2.usescases.general.AbstractActivityContracts; import org.dhis2.commons.filters.FilterItem; import org.dhis2.commons.filters.FilterManager; import org.dhis2.commons.filters.Filters; +import org.dhis2.maps.model.StageStyle; +import org.dhis2.usescases.general.AbstractActivityContracts; import org.hisp.dhis.android.core.arch.call.D2Progress; import org.hisp.dhis.android.core.organisationunit.OrganisationUnit; import org.hisp.dhis.android.core.program.Program; import org.hisp.dhis.android.core.trackedentity.TrackedEntityType; -import java.util.Date; import java.util.HashMap; import java.util.List; -import java.util.Map; import io.reactivex.Observable; import io.reactivex.functions.Consumer; @@ -78,8 +75,6 @@ public interface Presenter { void onBackClick(); - void onClearClick(); - void onEnrollClick(HashMap queryData); void enroll(String programUid, String teiUid, HashMap queryData); diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt index c9889afc9e..487e86a201 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt @@ -18,8 +18,6 @@ import org.dhis2.commons.idlingresource.SearchIdlingResourceSingleton import org.dhis2.commons.network.NetworkUtils import org.dhis2.commons.viewmodel.DispatcherProvider import org.dhis2.data.search.SearchParametersModel -import org.dhis2.form.model.ActionType -import org.dhis2.form.model.RowAction import org.dhis2.maps.layer.basemaps.BaseMapStyle import org.dhis2.maps.usecases.MapStyleConfiguration import org.dhis2.usescases.searchTrackEntity.listView.SearchResult @@ -201,23 +199,12 @@ class SearchTEIViewModel( performSearch() } - fun updateQueryData(rowAction: RowAction) { - if (rowAction.type == ActionType.ON_SAVE || rowAction.type == ActionType.ON_TEXT_CHANGE) { - if (rowAction.value != null) { - queryData[rowAction.id] = rowAction.value!! - } else { - queryData.remove(rowAction.id) - } - updateSearch() - } else if (rowAction.type == ActionType.ON_CLEAR) { - clearQueryData() - } - } + fun updateQuery(uid: String, value: String?) { + value?.let { + queryData[uid] = it + } ?: queryData.remove(uid) - private fun clearQueryData() { - queryData.clear() updateSearch() - performSearch() } private fun updateSearch() { @@ -395,7 +382,7 @@ class SearchTEIViewModel( private fun minAttributesToSearchCheck(): Boolean { return searchRepository.getProgram(initialProgramUid)?.let { program -> - program.minAttributesRequiredToSearch() ?: 0 <= queryData.size + (program.minAttributesRequiredToSearch() ?: 0) <= queryData.size } ?: true } @@ -501,7 +488,7 @@ class SearchTEIViewModel( listOf( SearchResult( SearchResult.SearchResultType.SEARCH_OR_CREATE, - searchRepository.getTrackedEntityType().displayName(), + searchRepository.trackedEntityType.displayName(), ), ) diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEPresenter.java b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEPresenter.java index c7ca1a3835..5c05ef28dc 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEPresenter.java +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEPresenter.java @@ -29,13 +29,11 @@ import org.dhis2.commons.filters.FilterManager; import org.dhis2.commons.filters.data.FilterRepository; import org.dhis2.commons.matomo.MatomoAnalyticsController; -import org.dhis2.commons.network.NetworkUtils; import org.dhis2.commons.orgunitselector.OUTreeFragment; import org.dhis2.commons.orgunitselector.OrgUnitSelectorScope; import org.dhis2.commons.prefs.Preference; import org.dhis2.commons.prefs.PreferenceProvider; import org.dhis2.commons.resources.ColorUtils; -import org.dhis2.commons.resources.D2ErrorUtils; import org.dhis2.commons.resources.ObjectStyleUtils; import org.dhis2.commons.resources.ResourceManager; import org.dhis2.commons.schedulers.SchedulerProvider; @@ -260,12 +258,6 @@ private boolean isPreviousAndCurrentProgramTheSame(Program programSelected, Stri programSelected == selectedProgram; } - @Override - public void onClearClick() { - searchRepository.setCurrentProgram(selectedProgram != null ? selectedProgram.uid() : null); - currentProgram.onNext(selectedProgram != null ? selectedProgram.uid() : ""); - } - @Override public void onBackClick() { view.onBackClicked(); diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt index cb433c77c0..1b2f89ebd6 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt @@ -20,7 +20,10 @@ import org.hisp.dhis.mobile.ui.designsystem.component.parameter.ParameterSelecto import org.hisp.dhis.mobile.ui.designsystem.theme.Shape @Composable -fun SearchParametersScreen(viewModel: SearchParametersViewModel) { +fun SearchParametersScreen( + viewModel: SearchParametersViewModel, + onValueChange: (uid: String, value: String?) -> Unit, +) { Column( modifier = Modifier .fillMaxSize() @@ -35,9 +38,7 @@ fun SearchParametersScreen(viewModel: SearchParametersViewModel) { ParameterSelectorItem( model = provideParameterSelectorItem( searchParameter = searchParameter, - onValueChange = { uid, value -> - viewModel.updateParameter(uid, value) - }, + onValueChange = onValueChange, ), ) } @@ -69,12 +70,13 @@ fun initSearchScreen( viewModel: SearchParametersViewModel, program: String?, teiType: String, + onValueChange: (uid: String, value: String?) -> Unit, ) { viewModel.fetchSearchParameters( programUid = program, teiTypeUid = teiType, ) composeView.setContent { - SearchParametersScreen(viewModel) + SearchParametersScreen(viewModel, onValueChange) } } diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersViewModel.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersViewModel.kt index 6b0589b178..27f8d82acb 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersViewModel.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersViewModel.kt @@ -36,7 +36,4 @@ class SearchParametersViewModel( } } } - - fun updateParameter(uid: String, value: String?) { - } } diff --git a/form/src/main/java/org/dhis2/form/model/ActionType.kt b/form/src/main/java/org/dhis2/form/model/ActionType.kt index 03e8f24f94..36d127010e 100644 --- a/form/src/main/java/org/dhis2/form/model/ActionType.kt +++ b/form/src/main/java/org/dhis2/form/model/ActionType.kt @@ -6,7 +6,6 @@ enum class ActionType { ON_SAVE, ON_TEXT_CHANGE, ON_SECTION_CHANGE, - ON_CLEAR, ON_FINISH, ON_REQUEST_COORDINATES, ON_CANCEL_REQUEST_COORDINATES, diff --git a/form/src/main/java/org/dhis2/form/ui/FormView.kt b/form/src/main/java/org/dhis2/form/ui/FormView.kt index dbb727030c..4a68235ecf 100644 --- a/form/src/main/java/org/dhis2/form/ui/FormView.kt +++ b/form/src/main/java/org/dhis2/form/ui/FormView.kt @@ -1128,10 +1128,6 @@ class FormView : Fragment() { return if (value.isEmpty()) 0 else -Integer.valueOf(value) } - fun clearValues() { - intentHandler(FormIntent.OnClear()) - } - fun onBackPressed() { viewModel.runDataIntegrityCheck(backButtonPressed = true) } diff --git a/form/src/main/java/org/dhis2/form/ui/FormViewModel.kt b/form/src/main/java/org/dhis2/form/ui/FormViewModel.kt index 538cc3b52b..99a7856132 100644 --- a/form/src/main/java/org/dhis2/form/ui/FormViewModel.kt +++ b/form/src/main/java/org/dhis2/form/ui/FormViewModel.kt @@ -219,14 +219,6 @@ class FormViewModel( ) } - ActionType.ON_CLEAR -> { - repository.removeAllValues() - StoreResult( - action.id, - ValueStoreResult.VALUE_CHANGED, - ) - } - ActionType.ON_FINISH -> { repository.setFocusedItem(action) StoreResult( @@ -358,12 +350,6 @@ class FormViewModel( private fun rowActionFromIntent(intent: FormIntent): RowAction { return when (intent) { - is FormIntent.OnClear -> createRowAction( - uid = "", - value = null, - actionType = ActionType.ON_CLEAR, - ) - is FormIntent.ClearValue -> createRowAction(intent.uid, null) is FormIntent.SelectLocationFromCoordinates -> { val error = checkFieldError( diff --git a/form/src/main/java/org/dhis2/form/ui/intent/FormIntent.kt b/form/src/main/java/org/dhis2/form/ui/intent/FormIntent.kt index 98d42aace7..6c0c64d08c 100644 --- a/form/src/main/java/org/dhis2/form/ui/intent/FormIntent.kt +++ b/form/src/main/java/org/dhis2/form/ui/intent/FormIntent.kt @@ -9,10 +9,6 @@ sealed class FormIntent : MviIntent { val extraData: String? = null, ) : FormIntent() - data class OnClear( - val extraData: String? = null, - ) : FormIntent() - data class OnFocus( val uid: String, val value: String?, From 2d3bb06ec04e8e8c2cddf0028d6544e533993b06 Mon Sep 17 00:00:00 2001 From: andresmr Date: Mon, 5 Feb 2024 13:59:13 +0100 Subject: [PATCH 05/47] [ANDROAPP-500] Remove deprecated steps in walkthrough --- .../java/org/dhis2/utils/HelpManager.java | 71 +++---------------- 1 file changed, 10 insertions(+), 61 deletions(-) diff --git a/app/src/main/java/org/dhis2/utils/HelpManager.java b/app/src/main/java/org/dhis2/utils/HelpManager.java index 0118edcdec..01fd4fd278 100644 --- a/app/src/main/java/org/dhis2/utils/HelpManager.java +++ b/app/src/main/java/org/dhis2/utils/HelpManager.java @@ -20,7 +20,6 @@ public class HelpManager { private static HelpManager instance; private List help; - private String screen; private NestedScrollView scrollView; public enum TutorialName { @@ -35,16 +34,10 @@ public static HelpManager getInstance() { return instance; } - public boolean isReady(){ + public boolean isReady() { return help != null; } - - public void setScreenHelp(String screen, List steps) { - help = steps; - this.screen = screen; - } - public void showHelp() { if (help != null) { if (scrollView != null) @@ -62,10 +55,6 @@ public void showHelp() { } } - public boolean isTutorialReadyForScreen(String screen) { - return this.screen != null && this.screen.equals(screen) && help != null && !help.isEmpty(); - } - public void setScroll(NestedScrollView scrollView) { this.scrollView = scrollView; } @@ -73,24 +62,12 @@ public void setScroll(NestedScrollView scrollView) { public void show(ActivityGlobalAbstract activity, TutorialName name, SparseBooleanArray stepCondition) { help = new ArrayList<>(); switch (name) { - case PROGRAM_FRAGMENT: - help = programFragmentTutorial(activity, stepCondition); - break; - case SETTINGS_FRAGMENT: - help = settingsFragmentTutorial(activity); - break; - case TEI_DASHBOARD: - help = teiDashboardTutorial(activity); - break; - case TEI_SEARCH: - help = teiSearchTutorial(activity); - break; - case PROGRAM_EVENT_LIST: - help = programEventListTutorial(activity, stepCondition); - break; - case EVENT_INITIAL: - help = eventInitialTutorial(activity, stepCondition); - break; + case PROGRAM_FRAGMENT -> help = programFragmentTutorial(activity, stepCondition); + case SETTINGS_FRAGMENT -> help = settingsFragmentTutorial(activity); + case TEI_DASHBOARD -> help = teiDashboardTutorial(activity); + case TEI_SEARCH -> help = teiSearchTutorial(activity); + case PROGRAM_EVENT_LIST -> help = programEventListTutorial(activity, stepCondition); + case EVENT_INITIAL -> help = eventInitialTutorial(activity, stepCondition); } if (!help.isEmpty()) showHelp(); @@ -155,24 +132,10 @@ private List teiSearchTutorial(ActivityGlobalAbstract activit .focusOn(activity.findViewById(R.id.program_spinner)) .closeOnTouch(true) .build(); - FancyShowCaseView tuto3 = new FancyShowCaseView.Builder(activity) - .title(activity.getString(R.string.tuto_search_3_v2)) - .enableAutoTextPosition() - .focusOn(activity.findViewById(R.id.searchButton)) - .closeOnTouch(true) - .build(); - FancyShowCaseView tuto4 = new FancyShowCaseView.Builder(activity) - .focusOn(activity.findViewById(R.id.clearFilterSearchButton)) - .title(activity.getString(R.string.tuto_search_4_v2)) - .enableAutoTextPosition() - .closeOnTouch(true) - .build(); ArrayList steps = new ArrayList<>(); steps.add(tuto1); steps.add(tuto2); - steps.add(tuto3); - steps.add(tuto4); return steps; } @@ -190,19 +153,7 @@ private List teiDashboardTutorial(ActivityGlobalAbstract acti .titleGravity(Gravity.BOTTOM) .closeOnTouch(true) .build(); - FancyShowCaseView tuto4 = new FancyShowCaseView.Builder(activity) - .title(activity.getString(R.string.tuto_dashboard_4)) - .enableAutoTextPosition() - .focusOn(activity.findViewById(R.id.follow_up)) - .closeOnTouch(true) - .build(); - FancyShowCaseView tuto5 = new FancyShowCaseView.Builder(activity) - .title(activity.getString(R.string.tuto_dashboard_5)) - .enableAutoTextPosition() - .focusShape(FocusShape.ROUNDED_RECTANGLE) - .closeOnTouch(true) - .build(); - FancyShowCaseView tuto6 = new FancyShowCaseView.Builder(activity) + FancyShowCaseView tuto3 = new FancyShowCaseView.Builder(activity) .title(activity.getString(R.string.tuto_dashboard_6)) .enableAutoTextPosition() .focusOn(activity.findViewById(R.id.tei_recycler)) @@ -210,7 +161,7 @@ private List teiDashboardTutorial(ActivityGlobalAbstract acti .titleGravity(Gravity.TOP) .closeOnTouch(true) .build(); - FancyShowCaseView tuto7 = new FancyShowCaseView.Builder(activity) + FancyShowCaseView tuto4 = new FancyShowCaseView.Builder(activity) .title(activity.getString(R.string.tuto_dashboard_7)) .enableAutoTextPosition() .focusOn(activity.findViewById(R.id.navigationBar)) @@ -221,10 +172,8 @@ private List teiDashboardTutorial(ActivityGlobalAbstract acti ArrayList steps = new ArrayList<>(); steps.add(tuto1); steps.add(tuto2); + steps.add(tuto3); steps.add(tuto4); - steps.add(tuto5); - steps.add(tuto6); - steps.add(tuto7); return steps; } From cfcf67e413fd08b31c7bd8b399a8365816f7edc1 Mon Sep 17 00:00:00 2001 From: andresmr Date: Tue, 6 Feb 2024 14:20:27 +0100 Subject: [PATCH 06/47] [ANDROAPP-500] Implement search and clear button --- .../searchTrackEntity/SearchTEActivity.java | 40 ++++++++------- .../SearchTEContractsModule.java | 2 + .../searchTrackEntity/SearchTEIViewModel.kt | 16 ++++-- .../searchTrackEntity/SearchTEPresenter.java | 6 +++ .../searchTrackEntity/SearchTEScreenState.kt | 11 ---- .../SearchParametersScreen.kt | 51 ++++++++++++++++--- .../ui/SearchScreenConfigurator.kt | 13 ++--- .../main/res/layout-land/activity_search.xml | 33 +----------- app/src/main/res/layout/activity_search.xml | 38 ++------------ 9 files changed, 97 insertions(+), 113 deletions(-) diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java index d3a31d370e..854f111e98 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java @@ -181,19 +181,6 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { binding.setPresenter(presenter); binding.setTotalFilters(FilterManager.getInstance().getTotalFilters()); ViewExtensionsKt.clipWithRoundedCorners(binding.mainComponent, ExtensionsKt.getDp(16)); - binding.searchButton.setOnClickListener(v -> { - if (OrientationUtilsKt.isPortrait()) searchScreenConfigurator.closeBackdrop(); - viewModel.onSearchClick(minNumberOfAttributes -> { - showSnackbar( - v, - String.format(getString(R.string.search_min_num_attr), - minNumberOfAttributes), - getString(R.string.button_ok) - - ); - return Unit.INSTANCE; - }); - }); binding.filterRecyclerLayout.setAdapter(filtersAdapter); @@ -372,7 +359,28 @@ private void initSearchParameters() { (uid, value) -> { viewModel.updateQuery(uid, value); return Unit.INSTANCE; - }); + }, + () -> { + if (OrientationUtilsKt.isPortrait()) searchScreenConfigurator.closeBackdrop(); + viewModel.onSearchClick(minNumberOfAttributes -> { + showSnackbar( + binding.searchContainer, + String.format(getString(R.string.search_min_num_attr), + minNumberOfAttributes), + getString(R.string.button_ok) + + ); + return Unit.INSTANCE; + }); + return Unit.INSTANCE; + }, + () -> { + presenter.clearFilterClick(); + presenter.onClearClick(); + viewModel.clearQueryData(); + return Unit.INSTANCE; + } + ); } private void configureBottomNavigation() { @@ -457,9 +465,6 @@ private void showAnalytics() { private void hideSearchAndFilterButtons() { binding.searchFilterGeneral.setVisibility(GONE); binding.filterCounter.setVisibility(GONE); - if (OrientationUtilsKt.isLandscape()) { - binding.searchButton.setVisibility(GONE); - } } private void showSearchAndFilterButtons() { @@ -467,7 +472,6 @@ private void showSearchAndFilterButtons() { fromAnalytics = false; binding.searchFilterGeneral.setVisibility(View.VISIBLE); binding.filterCounter.setVisibility(binding.getTotalFilters() > 0 ? View.VISIBLE : View.GONE); - binding.searchButton.setVisibility(OrientationUtilsKt.isLandscape() ? View.VISIBLE : GONE); } } diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEContractsModule.java b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEContractsModule.java index d17adb100b..3879cf4067 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEContractsModule.java +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEContractsModule.java @@ -73,6 +73,8 @@ public interface Presenter { void setProgram(Program programSelected); + void onClearClick(); + void onBackClick(); void onEnrollClick(HashMap queryData); diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt index 487e86a201..9206bd2330 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt @@ -200,11 +200,19 @@ class SearchTEIViewModel( } fun updateQuery(uid: String, value: String?) { - value?.let { - queryData[uid] = it - } ?: queryData.remove(uid) + if (value.isNullOrEmpty()) { + queryData.remove(uid) + } else { + queryData[uid] = value + } + + updateSearch() + } + fun clearQueryData() { + queryData.clear() updateSearch() + performSearch() } private fun updateSearch() { @@ -392,7 +400,7 @@ class SearchTEIViewModel( } ?: false } - fun canDisplayResult(itemCount: Int, onlineTooManyResults: Boolean): Boolean { + private fun canDisplayResult(itemCount: Int, onlineTooManyResults: Boolean): Boolean { return !onlineTooManyResults && when (initialProgramUid) { null -> itemCount <= TEI_TYPE_SEARCH_MAX_RESULTS else -> diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEPresenter.java b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEPresenter.java index 5c05ef28dc..6da8f9076d 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEPresenter.java +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEPresenter.java @@ -258,6 +258,12 @@ private boolean isPreviousAndCurrentProgramTheSame(Program programSelected, Stri programSelected == selectedProgram; } + @Override + public void onClearClick() { + searchRepository.setCurrentProgram(selectedProgram != null ? selectedProgram.uid() : null); + currentProgram.onNext(selectedProgram != null ? selectedProgram.uid() : ""); + } + @Override public void onBackClick() { view.onBackClicked(); diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEScreenState.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEScreenState.kt index 3ad735ac27..79c98423c8 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEScreenState.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEScreenState.kt @@ -14,21 +14,10 @@ data class SearchList( val searchForm: SearchForm, val searchFilters: SearchFilters, ) : SearchTEScreenState(listType, previousSate) { - fun displaySearchButton(): Boolean { - return searchForm.isOpened - } - - fun displayResetSearchButton(): Boolean { - return searchForm.isOpened && searchForm.queryHasData - } fun displayResetFiltersButton(): Boolean { return searchFilters.isOpened and searchFilters.hasActiveFilters } - - fun displayResetInLandscape(): Boolean { - return searchForm.queryHasData || displayResetFiltersButton() - } } data class SearchForm( diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt index 1b2f89ebd6..0a1982dff1 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt @@ -7,22 +7,31 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll +import androidx.compose.material.Icon +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Cancel import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import org.dhis2.usescases.searchTrackEntity.SearchDispatchers import org.dhis2.usescases.searchTrackEntity.searchparameters.provider.provideParameterSelectorItem +import org.hisp.dhis.android.core.D2Manager import org.hisp.dhis.mobile.ui.designsystem.component.Button import org.hisp.dhis.mobile.ui.designsystem.component.ButtonStyle import org.hisp.dhis.mobile.ui.designsystem.component.parameter.ParameterSelectorItem import org.hisp.dhis.mobile.ui.designsystem.theme.Shape +import org.hisp.dhis.mobile.ui.designsystem.theme.SurfaceColor @Composable fun SearchParametersScreen( viewModel: SearchParametersViewModel, onValueChange: (uid: String, value: String?) -> Unit, + onSearchClick: () -> Unit, + onClear: () -> Unit, ) { Column( modifier = Modifier @@ -42,6 +51,22 @@ fun SearchParametersScreen( ), ) } + + Button( + modifier = Modifier + .align(Alignment.CenterHorizontally), + style = ButtonStyle.TEXT, + text = "Clear search", + icon = { + Icon( + imageVector = Icons.Outlined.Cancel, + contentDescription = "Clear search", + tint = SurfaceColor.Primary, + ) + }, + ) { + onClear() + } } Button( @@ -51,6 +76,7 @@ fun SearchParametersScreen( style = ButtonStyle.FILLED, text = "Search", ) { + onSearchClick() } } } @@ -58,11 +84,17 @@ fun SearchParametersScreen( @Preview(showBackground = true) @Composable fun SearchFormPreview() { - /* SearchParametersScreen( - SearchParametersViewModel( - repository = SearchParametersRepository(), - ), - )*/ + SearchParametersScreen( + SearchParametersViewModel( + repository = SearchParametersRepository( + D2Manager.getD2(), + SearchDispatchers(), + ), + ), + onValueChange = { _, _ -> }, + onSearchClick = {}, + onClear = {}, + ) } fun initSearchScreen( @@ -71,12 +103,19 @@ fun initSearchScreen( program: String?, teiType: String, onValueChange: (uid: String, value: String?) -> Unit, + onSearchClick: () -> Unit, + onClear: () -> Unit, ) { viewModel.fetchSearchParameters( programUid = program, teiTypeUid = teiType, ) composeView.setContent { - SearchParametersScreen(viewModel, onValueChange) + SearchParametersScreen( + viewModel = viewModel, + onValueChange = onValueChange, + onSearchClick = onSearchClick, + onClear = onClear, + ) } } diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/ui/SearchScreenConfigurator.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/ui/SearchScreenConfigurator.kt index 32c2736a9f..3b4748145a 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/ui/SearchScreenConfigurator.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/ui/SearchScreenConfigurator.kt @@ -37,8 +37,7 @@ class SearchScreenConfigurator( searchConfiguration.searchForm.isOpened -> openSearch() else -> closeBackdrop() } - binding.searchButton.display(searchConfiguration.displaySearchButton()) - binding.clearFilterSearchButton.display(searchConfiguration.displayResetSearchButton()) + binding.clearFilters?.display(searchConfiguration.displayResetFiltersButton()) syncButtonVisibility(!searchConfiguration.searchForm.isOpened) setFiltersVisibility(!searchConfiguration.searchForm.isOpened) @@ -61,13 +60,7 @@ class SearchScreenConfigurator( } else { openSearch() } - binding.searchButton.display( - searchConfiguration.displaySearchButton() || - !searchConfiguration.searchFilters.isOpened, - ) - binding.clearFilterSearchButton.display( - searchConfiguration.displayResetInLandscape(), - ) + syncButtonVisibility(true) setFiltersVisibility(true) binding.minAttributeMessage.setContent { @@ -97,7 +90,7 @@ class SearchScreenConfigurator( private fun setFiltersVisibility(showFilters: Boolean) { binding.filterCounter.visibility = - if (showFilters && binding.totalFilters ?: 0 > 0) View.VISIBLE else View.GONE + if (showFilters && (binding.totalFilters ?: 0) > 0) View.VISIBLE else View.GONE binding.searchFilterGeneral.visibility = if (showFilters) View.VISIBLE else View.GONE } diff --git a/app/src/main/res/layout-land/activity_search.xml b/app/src/main/res/layout-land/activity_search.xml index 1b7c1bd809..9accfefc64 100644 --- a/app/src/main/res/layout-land/activity_search.xml +++ b/app/src/main/res/layout-land/activity_search.xml @@ -104,7 +104,8 @@ android:textColor="?colorPrimary" android:textSize="10sp" android:visibility="@{totalFilters>0?View.VISIBLE:View.GONE}" - tools:text="1" /> + tools:text="1" + tools:ignore="SmallSp" /> - - - - diff --git a/app/src/main/res/layout/activity_search.xml b/app/src/main/res/layout/activity_search.xml index 02cc93289d..d50d4c2678 100644 --- a/app/src/main/res/layout/activity_search.xml +++ b/app/src/main/res/layout/activity_search.xml @@ -80,6 +80,7 @@ android:text='@{""+totalFilters}' android:textColor="?colorPrimary" android:textSize="10sp" + tools:ignore="SmallSp" tools:text="1" /> @@ -160,8 +161,8 @@ android:id="@+id/filterRecyclerLayout" android:layout_width="match_parent" android:layout_height="0dp" - android:paddingBottom="50dp" android:clipToPadding="false" + android:paddingBottom="50dp" android:visibility="gone" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layout_constraintBottom_toTopOf="@id/maxHeightFilterGuide" @@ -175,12 +176,13 @@ - - - - From a412e2b5218aaa0f9a1cccab4958fe9fe21df597 Mon Sep 17 00:00:00 2001 From: andresmr Date: Tue, 6 Feb 2024 17:10:01 +0100 Subject: [PATCH 07/47] [ANDROAPP-500] Implement SnackBar for min attributes search --- .../SearchParametersScreen.kt | 100 ++++++++++++------ 1 file changed, 66 insertions(+), 34 deletions(-) diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt index 0a1982dff1..b9c8985c14 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt @@ -8,15 +8,20 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.Icon +import androidx.compose.material.Scaffold +import androidx.compose.material.SnackbarHost import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Cancel +import androidx.compose.material.rememberScaffoldState import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import kotlinx.coroutines.launch import org.dhis2.usescases.searchTrackEntity.SearchDispatchers import org.dhis2.usescases.searchTrackEntity.searchparameters.provider.provideParameterSelectorItem import org.hisp.dhis.android.core.D2Manager @@ -33,51 +38,78 @@ fun SearchParametersScreen( onSearchClick: () -> Unit, onClear: () -> Unit, ) { - Column( - modifier = Modifier - .fillMaxSize() - .background(color = Color.White, shape = Shape.LargeTop), + val scaffoldState = rememberScaffoldState() + val snackBarHostState = scaffoldState.snackbarHostState + val coroutineScope = rememberCoroutineScope() + + Scaffold( + backgroundColor = Color.Transparent, + scaffoldState = scaffoldState, + snackbarHost = { + SnackbarHost( + hostState = snackBarHostState, + modifier = Modifier.padding( + start = 8.dp, + top = 8.dp, + end = 8.dp, + bottom = 48.dp, + ), + ) + }, ) { Column( modifier = Modifier - .weight(1F) - .verticalScroll(rememberScrollState()), + .fillMaxSize() + .background(color = Color.White, shape = Shape.LargeTop) + .padding(it), ) { - viewModel.uiState.items.forEach { searchParameter -> - ParameterSelectorItem( - model = provideParameterSelectorItem( - searchParameter = searchParameter, - onValueChange = onValueChange, - ), - ) + Column( + modifier = Modifier + .weight(1F) + .verticalScroll(rememberScrollState()), + ) { + viewModel.uiState.items.forEach { searchParameter -> + ParameterSelectorItem( + model = provideParameterSelectorItem( + searchParameter = searchParameter, + onValueChange = onValueChange, + ), + ) + } + + Button( + modifier = Modifier + .align(Alignment.CenterHorizontally), + style = ButtonStyle.TEXT, + text = "Clear search", + icon = { + Icon( + imageVector = Icons.Outlined.Cancel, + contentDescription = "Clear search", + tint = SurfaceColor.Primary, + ) + }, + ) { + onClear() + } } Button( modifier = Modifier - .align(Alignment.CenterHorizontally), - style = ButtonStyle.TEXT, - text = "Clear search", - icon = { - Icon( - imageVector = Icons.Outlined.Cancel, - contentDescription = "Clear search", - tint = SurfaceColor.Primary, - ) - }, + .fillMaxWidth() + .padding(16.dp, 8.dp, 16.dp, 8.dp), + style = ButtonStyle.FILLED, + text = "Search", ) { - onClear() +// onSearchClick() + /*val minAttributes= 2 + val message = stringResource(R.string.search_min_attributes_message) + .format("$minAttributes")*/ + coroutineScope.launch { + snackBarHostState.showSnackbar("This is a snackBar") + } } } - - Button( - modifier = Modifier - .fillMaxWidth() - .padding(16.dp, 8.dp, 16.dp, 8.dp), - style = ButtonStyle.FILLED, - text = "Search", - ) { - onSearchClick() - } } } From c600945183d9f7088e83d63d71597df1761fcd72 Mon Sep 17 00:00:00 2001 From: andresmr Date: Wed, 7 Feb 2024 11:21:11 +0100 Subject: [PATCH 08/47] [ANDROAPP-500] Refactor search and clear button to SearchTEIViewModel --- .../searchTrackEntity/SearchTEActivity.java | 34 +----- .../searchTrackEntity/SearchTEIViewModel.kt | 44 +++++-- .../searchTrackEntity/SearchTEModule.java | 17 +-- .../usescases/searchTrackEntity/SearchTEUi.kt | 111 +----------------- .../SearchTeiViewModelFactory.kt | 6 + .../SearchParametersScreen.kt | 55 +++++---- .../SearchParametersViewModel.kt | 39 ------ .../SearchParametersViewModelFactory.kt | 16 --- .../model/SearchParametersUiState.kt | 3 +- .../ui/SearchScreenConfigurator.kt | 23 ---- .../searchTrackEntity/ui/SearchTEUi.kt | 107 +---------------- .../main/res/layout-land/activity_search.xml | 27 ++--- app/src/main/res/layout/activity_search.xml | 8 +- .../commons/resources/ResourceManager.kt | 3 + 14 files changed, 104 insertions(+), 389 deletions(-) delete mode 100644 app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersViewModel.kt delete mode 100644 app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersViewModelFactory.kt diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java index 854f111e98..af92d9ca2a 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java @@ -39,8 +39,6 @@ import org.dhis2.usescases.general.ActivityGlobalAbstract; import org.dhis2.usescases.searchTrackEntity.listView.SearchTEList; import org.dhis2.usescases.searchTrackEntity.mapView.SearchTEMap; -import org.dhis2.usescases.searchTrackEntity.searchparameters.SearchParametersViewModel; -import org.dhis2.usescases.searchTrackEntity.searchparameters.SearchParametersViewModelFactory; import org.dhis2.usescases.searchTrackEntity.ui.SearchScreenConfigurator; import org.dhis2.utils.DateUtils; import org.dhis2.utils.OrientationUtilsKt; @@ -77,9 +75,6 @@ public class SearchTEActivity extends ActivityGlobalAbstract implements SearchTE @Inject SearchTeiViewModelFactory viewModelFactory; - @Inject - SearchParametersViewModelFactory searchParametersViewModelFactory; - @Inject SearchNavigator searchNavigator; @@ -104,8 +99,6 @@ public class SearchTEActivity extends ActivityGlobalAbstract implements SearchTE private SearchTEIViewModel viewModel; - private SearchParametersViewModel searchParametersViewModel; - private boolean initSearchNeeded = true; public SearchTEComponent searchComponent; private int initialPage = 0; @@ -159,11 +152,6 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { viewModel = new ViewModelProvider(this, viewModelFactory).get(SearchTEIViewModel.class); - searchParametersViewModel = new ViewModelProvider( - this, - searchParametersViewModelFactory - ).get(SearchParametersViewModel.class); - binding = DataBindingUtil.setContentView(this, R.layout.activity_search); initSearchParameters(); @@ -355,29 +343,9 @@ public void updateFilters(int totalFilters) { } private void initSearchParameters() { - initSearchScreen(binding.searchContainer, searchParametersViewModel, initialProgram, tEType, - (uid, value) -> { - viewModel.updateQuery(uid, value); - return Unit.INSTANCE; - }, - () -> { - if (OrientationUtilsKt.isPortrait()) searchScreenConfigurator.closeBackdrop(); - viewModel.onSearchClick(minNumberOfAttributes -> { - showSnackbar( - binding.searchContainer, - String.format(getString(R.string.search_min_num_attr), - minNumberOfAttributes), - getString(R.string.button_ok) - - ); - return Unit.INSTANCE; - }); - return Unit.INSTANCE; - }, + initSearchScreen(binding.searchContainer, viewModel, initialProgram, tEType, () -> { - presenter.clearFilterClick(); presenter.onClearClick(); - viewModel.clearQueryData(); return Unit.INSTANCE; } ); diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt index 9206bd2330..f296718dd6 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt @@ -1,5 +1,8 @@ package org.dhis2.usescases.searchTrackEntity +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel @@ -7,20 +10,25 @@ import androidx.lifecycle.viewModelScope import androidx.paging.PagingData import androidx.paging.cachedIn import androidx.paging.map +import kotlinx.coroutines.Job import kotlinx.coroutines.async import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import org.dhis2.R import org.dhis2.commons.data.SearchTeiModel import org.dhis2.commons.filters.FilterManager import org.dhis2.commons.idlingresource.SearchIdlingResourceSingleton import org.dhis2.commons.network.NetworkUtils +import org.dhis2.commons.resources.ResourceManager import org.dhis2.commons.viewmodel.DispatcherProvider import org.dhis2.data.search.SearchParametersModel import org.dhis2.maps.layer.basemaps.BaseMapStyle import org.dhis2.maps.usecases.MapStyleConfiguration import org.dhis2.usescases.searchTrackEntity.listView.SearchResult +import org.dhis2.usescases.searchTrackEntity.searchparameters.SearchParametersRepository +import org.dhis2.usescases.searchTrackEntity.searchparameters.model.SearchParametersUiState import org.dhis2.usescases.searchTrackEntity.ui.UnableToSearchOutsideData import org.dhis2.utils.customviews.navigationbar.NavigationPageConfigurator import org.hisp.dhis.android.core.maintenance.D2ErrorCode @@ -38,6 +46,8 @@ class SearchTEIViewModel( private val networkUtils: NetworkUtils, private val dispatchers: DispatcherProvider, private val mapStyleConfig: MapStyleConfiguration, + private val searchParametersRepository: SearchParametersRepository, + private val resourceManager: ResourceManager, ) : ViewModel() { private val _pageConfiguration = MutableLiveData() @@ -74,6 +84,11 @@ class SearchTEIViewModel( private val _filtersOpened = MutableLiveData(false) val filtersOpened: LiveData = _filtersOpened + var uiState by mutableStateOf(SearchParametersUiState()) + private set + + private var fetchJob: Job? = null + init { viewModelScope.launch(dispatchers.io()) { createButtonScrollVisibility.postValue( @@ -349,12 +364,12 @@ class SearchTEIViewModel( } } - fun onSearchClick(onMinAttributes: (Int) -> Unit = {}) { + fun onSearchClick() { searchRepository.clearFetchedList() - performSearch(onMinAttributes) + performSearch() } - private fun performSearch(onMinAttributes: (Int) -> Unit = {}) { + private fun performSearch() { viewModelScope.launch { if (canPerformSearch()) { searching = queryData.isNotEmpty() @@ -375,11 +390,14 @@ class SearchTEIViewModel( else -> searching = false } } else { - onMinAttributes( - searchRepository.getProgram(initialProgramUid) - ?.minAttributesRequiredToSearch() - ?: 0, + val minAttributesToSearch = searchRepository.getProgram(initialProgramUid) + ?.minAttributesRequiredToSearch() + ?: 0 + val message = resourceManager.getString( + R.string.search_min_num_attr, + minAttributesToSearch, ) + uiState = uiState.copy(minAttributesMessage = message) } } } @@ -687,4 +705,16 @@ class SearchTEIViewModel( fun onLegacyInteractionConsumed() { _legacyInteraction.value = null } + + fun fetchSearchParameters( + programUid: String?, + teiTypeUid: String, + ) { + fetchJob?.cancel() + fetchJob = viewModelScope.launch { + val searchParameters = + searchParametersRepository.searchParameters(programUid, teiTypeUid) + uiState = uiState.copy(items = searchParameters) + } + } } diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEModule.java b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEModule.java index 50ffe6e19e..0bf4e54946 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEModule.java +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEModule.java @@ -64,7 +64,6 @@ import org.dhis2.maps.utils.DhisMapUtils; import org.dhis2.ui.ThemeManager; import org.dhis2.usescases.searchTrackEntity.searchparameters.SearchParametersRepository; -import org.dhis2.usescases.searchTrackEntity.searchparameters.SearchParametersViewModelFactory; import org.dhis2.usescases.searchTrackEntity.ui.mapper.TEICardMapper; import org.dhis2.utils.DateUtils; import org.dhis2.utils.analytics.AnalyticsHelper; @@ -275,7 +274,9 @@ SearchTeiViewModelFactory providesViewModelFactory( MapDataRepository mapDataRepository, NetworkUtils networkUtils, D2 d2, - FilterRepository filterRepository + FilterRepository filterRepository, + SearchParametersRepository searchParametersRepository, + ResourceManager resourceManager ) { return new SearchTeiViewModelFactory( searchRepository, @@ -286,18 +287,12 @@ SearchTeiViewModelFactory providesViewModelFactory( mapDataRepository, networkUtils, new SearchDispatchers(), - new MapStyleConfiguration(d2) + new MapStyleConfiguration(d2), + searchParametersRepository, + resourceManager ); } - @Provides - @PerActivity - SearchParametersViewModelFactory provideSearchParametersViewModelFactory( - SearchParametersRepository searchParametersRepository - ) { - return new SearchParametersViewModelFactory(searchParametersRepository); - } - @Provides @PerActivity SearchParametersRepository provideSearchParametersRepository( diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEUi.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEUi.kt index 0a25fc3d3f..44f045cd1d 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEUi.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEUi.kt @@ -1,6 +1,5 @@ package org.dhis2.usescases.searchTrackEntity -import android.annotation.SuppressLint import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.slideInVertically @@ -24,17 +23,8 @@ import androidx.compose.material.ButtonDefaults import androidx.compose.material.CircularProgressIndicator import androidx.compose.material.Icon import androidx.compose.material.LocalTextStyle -import androidx.compose.material.Snackbar -import androidx.compose.material.SnackbarHost -import androidx.compose.material.SnackbarHostState import androidx.compose.material.Text -import androidx.compose.material.TextButton import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -43,15 +33,11 @@ import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource -import androidx.compose.ui.text.AnnotatedString -import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.font.Font import androidx.compose.ui.text.font.FontFamily -import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import kotlinx.coroutines.launch import org.dhis2.R import org.dhis2.usescases.searchTrackEntity.listView.SearchResult @@ -65,11 +51,13 @@ fun SearchResult( LoadingContent( loadingDescription = stringResource(R.string.search_loading_more), ) + SearchResult.SearchResultType.SEARCH_OUTSIDE -> SearchOutsideProgram( resultText = stringResource(R.string.search_no_results_in_program), buttonText = stringResource(R.string.search_outside_action), onSearchOutsideClick = onSearchOutsideClick, ) + SearchResult.SearchResultType.NO_MORE_RESULTS -> NoMoreResults() SearchResult.SearchResultType.TOO_MANY_RESULTS -> TooManyResults() SearchResult.SearchResultType.NO_RESULTS -> NoResults() @@ -128,7 +116,8 @@ fun FullSearchButton(modifier: Modifier, visible: Boolean = true, onClick: () -> exit = slideOutVertically(), ) { SearchButton( - modifier = Modifier.fillMaxWidth() + modifier = Modifier + .fillMaxWidth() .height(48.dp), onClick = onClick, ) @@ -334,86 +323,6 @@ fun CreateNewButton(modifier: Modifier, extended: Boolean = true, onClick: () -> } } -@Composable -fun MinAttributesMessage(minAttributes: Int) { - val message = stringResource(R.string.search_min_attributes_message) - .format("$minAttributes") - var lineCount by remember { mutableStateOf(1) } - Row( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp), - ) { - Icon( - modifier = Modifier - .alignBy { it.measuredHeight / 2 } - .size(9.dp), - painter = painterResource(id = R.drawable.ic_info_outline), - contentDescription = "", - tint = colorResource(id = R.color.primaryBgTextColor), - ) - Spacer(modifier = Modifier.size(9.dp)) - Text( - modifier = Modifier - .alignBy { - if (lineCount > 0) { - it.measuredHeight / (2 * lineCount) - } else { - it.measuredHeight / 2 - } - } - .align(Alignment.CenterVertically), - text = AnnotatedString( - text = message, - spanStyles = listOf( - AnnotatedString.Range( - SpanStyle(fontWeight = FontWeight.Bold), - start = message.indexOf("$minAttributes"), - end = message.indexOf("$minAttributes") + "$minAttributes".length, - ), - ), - ), - fontSize = 12.sp, - color = colorResource(id = R.color.primaryBgTextColor), - style = LocalTextStyle.current.copy( - lineHeight = 16.sp, - fontFamily = FontFamily(Font(R.font.rubik_regular)), - ), - onTextLayout = { - lineCount = it.lineCount - }, - ) - } -} - -@SuppressLint("CoroutineCreationDuringComposition") -@Composable -fun MinAttributesSnackbar(minAttributes: Int) { - val message = stringResource(R.string.search_min_attributes_message) - .format("$minAttributes") - - val snackState = remember { SnackbarHostState() } - val snackScope = rememberCoroutineScope() - - SnackbarHost( - hostState = snackState, - snackbar = { snackBarData -> - Snackbar( - modifier = Modifier, - action = { - TextButton(onClick = { }) { - Text(text = stringResource(id = R.string.button_ok)) - } - }, - ) { - Text(text = snackBarData.message) - } - }, - ) - - snackScope.launch { snackState.showSnackbar(message) } -} - @ExperimentalAnimationApi @Preview(showBackground = true, backgroundColor = 0x2C98F0) @Composable @@ -443,18 +352,6 @@ fun CreateNewButtonPreview() { CreateNewButton(modifier = Modifier, extended = false) {} } -@Preview(showBackground = true, backgroundColor = 0x2C98F0) -@Composable -fun MinAttributesMessage() { - MinAttributesMessage(2) -} - -@Preview(showBackground = true, backgroundColor = 0x2C98F0) -@Composable -fun MinAttributesSnackBarPreview() { - MinAttributesSnackbar(2) -} - @Preview(showBackground = true) @Composable fun LoadingMoreResultsPreview() { diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTeiViewModelFactory.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTeiViewModelFactory.kt index 46ff618035..2e5deeb5da 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTeiViewModelFactory.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTeiViewModelFactory.kt @@ -3,8 +3,10 @@ package org.dhis2.usescases.searchTrackEntity import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import org.dhis2.commons.network.NetworkUtils +import org.dhis2.commons.resources.ResourceManager import org.dhis2.commons.viewmodel.DispatcherProvider import org.dhis2.maps.usecases.MapStyleConfiguration +import org.dhis2.usescases.searchTrackEntity.searchparameters.SearchParametersRepository @Suppress("UNCHECKED_CAST") class SearchTeiViewModelFactory( @@ -17,6 +19,8 @@ class SearchTeiViewModelFactory( private val networkUtils: NetworkUtils, private val dispatchers: DispatcherProvider, private val mapStyleConfig: MapStyleConfiguration, + private val searchParameterRepository: SearchParametersRepository, + private val resourceManager: ResourceManager, ) : ViewModelProvider.Factory { override fun create(modelClass: Class): T { return SearchTEIViewModel( @@ -29,6 +33,8 @@ class SearchTeiViewModelFactory( networkUtils, dispatchers, mapStyleConfig, + searchParameterRepository, + resourceManager, ) as T } } diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt index b9c8985c14..827551d3ff 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt @@ -22,9 +22,11 @@ import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import kotlinx.coroutines.launch -import org.dhis2.usescases.searchTrackEntity.SearchDispatchers +import org.dhis2.usescases.searchTrackEntity.SearchTEIViewModel +import org.dhis2.usescases.searchTrackEntity.searchparameters.model.SearchParameter +import org.dhis2.usescases.searchTrackEntity.searchparameters.model.SearchParametersUiState import org.dhis2.usescases.searchTrackEntity.searchparameters.provider.provideParameterSelectorItem -import org.hisp.dhis.android.core.D2Manager +import org.hisp.dhis.android.core.common.ValueType import org.hisp.dhis.mobile.ui.designsystem.component.Button import org.hisp.dhis.mobile.ui.designsystem.component.ButtonStyle import org.hisp.dhis.mobile.ui.designsystem.component.parameter.ParameterSelectorItem @@ -33,7 +35,7 @@ import org.hisp.dhis.mobile.ui.designsystem.theme.SurfaceColor @Composable fun SearchParametersScreen( - viewModel: SearchParametersViewModel, + uiState: SearchParametersUiState, onValueChange: (uid: String, value: String?) -> Unit, onSearchClick: () -> Unit, onClear: () -> Unit, @@ -42,6 +44,12 @@ fun SearchParametersScreen( val snackBarHostState = scaffoldState.snackbarHostState val coroutineScope = rememberCoroutineScope() + uiState.minAttributesMessage?.let { + coroutineScope.launch { + snackBarHostState.showSnackbar(it) + } + } + Scaffold( backgroundColor = Color.Transparent, scaffoldState = scaffoldState, @@ -68,7 +76,7 @@ fun SearchParametersScreen( .weight(1F) .verticalScroll(rememberScrollState()), ) { - viewModel.uiState.items.forEach { searchParameter -> + uiState.items.forEach { searchParameter -> ParameterSelectorItem( model = provideParameterSelectorItem( searchParameter = searchParameter, @@ -101,13 +109,7 @@ fun SearchParametersScreen( style = ButtonStyle.FILLED, text = "Search", ) { -// onSearchClick() - /*val minAttributes= 2 - val message = stringResource(R.string.search_min_attributes_message) - .format("$minAttributes")*/ - coroutineScope.launch { - snackBarHostState.showSnackbar("This is a snackBar") - } + onSearchClick() } } } @@ -117,10 +119,18 @@ fun SearchParametersScreen( @Composable fun SearchFormPreview() { SearchParametersScreen( - SearchParametersViewModel( - repository = SearchParametersRepository( - D2Manager.getD2(), - SearchDispatchers(), + uiState = SearchParametersUiState( + items = listOf( + SearchParameter( + "uid1", + "Label 1", + ValueType.TEXT, + ), + SearchParameter( + "uid2", + "Label 2", + ValueType.TEXT, + ), ), ), onValueChange = { _, _ -> }, @@ -131,11 +141,9 @@ fun SearchFormPreview() { fun initSearchScreen( composeView: ComposeView, - viewModel: SearchParametersViewModel, + viewModel: SearchTEIViewModel, program: String?, teiType: String, - onValueChange: (uid: String, value: String?) -> Unit, - onSearchClick: () -> Unit, onClear: () -> Unit, ) { viewModel.fetchSearchParameters( @@ -144,10 +152,13 @@ fun initSearchScreen( ) composeView.setContent { SearchParametersScreen( - viewModel = viewModel, - onValueChange = onValueChange, - onSearchClick = onSearchClick, - onClear = onClear, + uiState = viewModel.uiState, + onValueChange = viewModel::updateQuery, + onSearchClick = viewModel::onSearchClick, + onClear = { + onClear() + viewModel.clearQueryData() + }, ) } } diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersViewModel.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersViewModel.kt deleted file mode 100644 index 27f8d82acb..0000000000 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersViewModel.kt +++ /dev/null @@ -1,39 +0,0 @@ -package org.dhis2.usescases.searchTrackEntity.searchparameters - -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import kotlinx.coroutines.Job -import kotlinx.coroutines.launch -import org.dhis2.usescases.searchTrackEntity.searchparameters.model.SearchParametersUiState -import java.io.IOException - -class SearchParametersViewModel( - val repository: SearchParametersRepository, -) : ViewModel() { - - var uiState by mutableStateOf(SearchParametersUiState()) - private set - - private var fetchJob: Job? = null - - fun fetchSearchParameters( - programUid: String?, - teiTypeUid: String, - ) { - fetchJob?.cancel() - fetchJob = viewModelScope.launch { - try { - val searchParameters = repository.searchParameters(programUid, teiTypeUid) - uiState = uiState.copy(items = searchParameters) - } catch (ioe: IOException) { - /*_uiState.update { - val messages = getMessagesFromThrowable(ioe) - it.copy(userMessages = messages) - }*/ - } - } - } -} diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersViewModelFactory.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersViewModelFactory.kt deleted file mode 100644 index ea06b5802d..0000000000 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersViewModelFactory.kt +++ /dev/null @@ -1,16 +0,0 @@ -package org.dhis2.usescases.searchTrackEntity.searchparameters - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider - -@Suppress("UNCHECKED_CAST") -class SearchParametersViewModelFactory( - private val repository: SearchParametersRepository, -) : ViewModelProvider.Factory { - - override fun create(modelClass: Class): T { - return SearchParametersViewModel( - repository = repository, - ) as T - } -} diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParametersUiState.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParametersUiState.kt index 6462208fa1..4892465859 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParametersUiState.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParametersUiState.kt @@ -2,6 +2,5 @@ package org.dhis2.usescases.searchTrackEntity.searchparameters.model data class SearchParametersUiState( val items: List = listOf(), + val minAttributesMessage: String? = null, ) - -val SearchParametersUiState.isSearchEnable: Boolean get() = items.isNotEmpty() diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/ui/SearchScreenConfigurator.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/ui/SearchScreenConfigurator.kt index 3b4748145a..94770bf982 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/ui/SearchScreenConfigurator.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/ui/SearchScreenConfigurator.kt @@ -3,7 +3,6 @@ package org.dhis2.usescases.searchTrackEntity.ui import android.transition.TransitionManager import android.view.View import androidx.constraintlayout.widget.ConstraintSet -import com.google.android.material.composethemeadapter.MdcTheme import org.dhis2.R import org.dhis2.bindings.display import org.dhis2.bindings.dp @@ -41,17 +40,6 @@ class SearchScreenConfigurator( binding.clearFilters?.display(searchConfiguration.displayResetFiltersButton()) syncButtonVisibility(!searchConfiguration.searchForm.isOpened) setFiltersVisibility(!searchConfiguration.searchForm.isOpened) - binding.minAttributeMessage.setContent { - MdcTheme { - if (searchConfiguration.searchForm.minAttributesToSearch > 0 && - searchConfiguration.searchForm.isOpened - ) { - MinAttributesMessage( - minAttributes = searchConfiguration.searchForm.minAttributesToSearch, - ) - } - } - } } private fun configureLandscapeListScreen(searchConfiguration: SearchList) { @@ -63,17 +51,6 @@ class SearchScreenConfigurator( syncButtonVisibility(true) setFiltersVisibility(true) - binding.minAttributeMessage.setContent { - MdcTheme { - if (searchConfiguration.searchForm.minAttributesToSearch > 0 && - !searchConfiguration.searchFilters.isOpened - ) { - MinAttributesMessage( - minAttributes = searchConfiguration.searchForm.minAttributesToSearch, - ) - } - } - } } private fun configureLandscapeAnalyticsScreen(expanded: Boolean) { diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/ui/SearchTEUi.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/ui/SearchTEUi.kt index 5c89d07c70..5a4959e4bb 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/ui/SearchTEUi.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/ui/SearchTEUi.kt @@ -29,17 +29,8 @@ import androidx.compose.material.CircularProgressIndicator import androidx.compose.material.Icon import androidx.compose.material.IconButton import androidx.compose.material.LocalTextStyle -import androidx.compose.material.Snackbar -import androidx.compose.material.SnackbarHost -import androidx.compose.material.SnackbarHostState import androidx.compose.material.Text -import androidx.compose.material.TextButton import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -51,18 +42,14 @@ import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource -import androidx.compose.ui.text.AnnotatedString -import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.font.Font import androidx.compose.ui.text.font.FontFamily -import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.intl.Locale import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.toLowerCase import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import kotlinx.coroutines.launch import org.dhis2.R import org.dhis2.commons.filters.workingLists.WorkingListChipGroup import org.dhis2.commons.filters.workingLists.WorkingListViewModel @@ -78,12 +65,14 @@ fun SearchResultUi(searchResult: SearchResult, onSearchOutsideClick: () -> Unit) LoadingContent( loadingDescription = stringResource(R.string.search_loading_more), ) + SearchResult.SearchResultType.SEARCH_OUTSIDE -> SearchOutsideProgram( resultText = stringResource(R.string.search_no_results_in_program) .format(searchResult.extraData!!), buttonText = stringResource(R.string.search_outside_action), onSearchOutsideClick = onSearchOutsideClick, ) + SearchResult.SearchResultType.NO_MORE_RESULTS -> NoMoreResults() SearchResult.SearchResultType.TOO_MANY_RESULTS -> TooManyResults() SearchResult.SearchResultType.NO_RESULTS -> NoResults() @@ -92,6 +81,7 @@ fun SearchResultUi(searchResult: SearchResult, onSearchOutsideClick: () -> Unit) SearchResult.SearchResultType.NO_MORE_RESULTS_OFFLINE -> NoMoreResults( message = stringResource(id = R.string.search_no_more_results_offline), ) + SearchResult.SearchResultType.UNABLE_SEARCH_OUTSIDE -> UnableToSearchOutside( uiData = searchResult.uiData as UnableToSearchOutsideData, ) @@ -521,85 +511,6 @@ fun CreateNewButton(modifier: Modifier, extended: Boolean = true, onClick: () -> } } -@Composable -fun MinAttributesMessage(minAttributes: Int) { - val message = stringResource(R.string.search_min_attributes_message) - .format("$minAttributes") - var lineCount by remember { mutableStateOf(1) } - Row( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp), - ) { - Icon( - modifier = Modifier - .alignBy { it.measuredHeight / 2 } - .size(9.dp), - painter = painterResource(id = R.drawable.ic_info_outline), - contentDescription = "", - tint = colorResource(id = R.color.primaryBgTextColor), - ) - Spacer(modifier = Modifier.size(9.dp)) - Text( - modifier = Modifier - .alignBy { - if (lineCount > 0) { - it.measuredHeight / (2 * lineCount) - } else { - it.measuredHeight / 2 - } - } - .align(Alignment.CenterVertically), - text = AnnotatedString( - text = message, - spanStyles = listOf( - AnnotatedString.Range( - SpanStyle(fontWeight = FontWeight.Bold), - start = message.indexOf("$minAttributes"), - end = message.indexOf("$minAttributes") + "$minAttributes".length, - ), - ), - ), - fontSize = 12.sp, - color = colorResource(id = R.color.primaryBgTextColor), - style = LocalTextStyle.current.copy( - lineHeight = 16.sp, - fontFamily = FontFamily(Font(R.font.rubik_regular)), - ), - onTextLayout = { - lineCount = it.lineCount - }, - ) - } -} - -@Composable -fun MinAttributesSnackbar(minAttributes: Int) { - val message = stringResource(R.string.search_min_attributes_message) - .format("$minAttributes") - - val snackState = remember { SnackbarHostState() } - val snackScope = rememberCoroutineScope() - - SnackbarHost( - hostState = snackState, - snackbar = { snackBarData -> - Snackbar( - modifier = Modifier, - action = { - TextButton(onClick = { }) { - Text(text = stringResource(id = R.string.button_ok)) - } - }, - ) { - Text(text = snackBarData.message) - } - }, - ) - - snackScope.launch { snackState.showSnackbar(message) } -} - @ExperimentalAnimationApi @Preview(showBackground = true, backgroundColor = 0x2C98F0) @Composable @@ -628,18 +539,6 @@ fun CreateNewButtonPreview() { CreateNewButton(modifier = Modifier, extended = false) {} } -@Preview(showBackground = true, backgroundColor = 0x2C98F0) -@Composable -fun MinAttributesMessage() { - MinAttributesMessage(2) -} - -@Preview(showBackground = true, backgroundColor = 0x2C98F0) -@Composable -fun MinAttributesSnackBarPreview() { - MinAttributesSnackbar(2) -} - @Preview(showBackground = true) @Composable fun LoadingMoreResultsPreview() { diff --git a/app/src/main/res/layout-land/activity_search.xml b/app/src/main/res/layout-land/activity_search.xml index 9accfefc64..5f3e0c6fac 100644 --- a/app/src/main/res/layout-land/activity_search.xml +++ b/app/src/main/res/layout-land/activity_search.xml @@ -38,6 +38,7 @@ + @@ -104,8 +105,8 @@ android:textColor="?colorPrimary" android:textSize="10sp" android:visibility="@{totalFilters>0?View.VISIBLE:View.GONE}" - tools:text="1" - tools:ignore="SmallSp" /> + tools:ignore="SmallSp" + tools:text="1" /> - - + app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintStart_toStartOf="parent" /> - + app:layout_constraintEnd_toEndOf="@id/backdropGuideDiv"> \ No newline at end of file diff --git a/app/src/main/res/layout/activity_search.xml b/app/src/main/res/layout/activity_search.xml index d50d4c2678..ab99cecc79 100644 --- a/app/src/main/res/layout/activity_search.xml +++ b/app/src/main/res/layout/activity_search.xml @@ -191,12 +191,6 @@ app:srcCompat="@drawable/ic_refresh" app:tint="?colorPrimary" /> - - Date: Wed, 7 Feb 2024 11:45:05 +0100 Subject: [PATCH 09/47] [ANDROAPP-500] rebase develop and update library --- .../provider/ParameterSelectorItemProvider.kt | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt index f0c12cb46e..67b80aebd6 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt @@ -5,6 +5,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.ui.text.input.TextFieldValue import org.dhis2.usescases.searchTrackEntity.searchparameters.model.SearchParameter import org.hisp.dhis.mobile.ui.designsystem.component.InputShellState import org.hisp.dhis.mobile.ui.designsystem.component.InputStyle @@ -16,7 +17,9 @@ fun provideParameterSelectorItem( searchParameter: SearchParameter, onValueChange: (uid: String, value: String?) -> Unit, ): ParameterSelectorItemModel { - var inputTextValue: String? by remember(searchParameter.value) { mutableStateOf(null) } + var value by remember(searchParameter.value) { + mutableStateOf(TextFieldValue(searchParameter.value ?: "")) + } return ParameterSelectorItemModel( label = searchParameter.label, @@ -25,18 +28,18 @@ fun provideParameterSelectorItem( InputText( title = searchParameter.label, state = InputShellState.UNFOCUSED, - inputText = inputTextValue, + inputTextFieldValue = value, inputStyle = InputStyle.ParameterInputStyle(), onValueChanged = { - inputTextValue = it + value = it ?: TextFieldValue() onValueChange( searchParameter.uid, - it, + value.text, ) }, ) }, - status = if (inputTextValue.isNullOrEmpty()) { + status = if (value.text.isEmpty()) { ParameterSelectorItemModel.Status.CLOSED } else { ParameterSelectorItemModel.Status.OPENED From bb503318f21477d50408d088e504638760050365 Mon Sep 17 00:00:00 2001 From: andresmr Date: Wed, 7 Feb 2024 12:19:25 +0100 Subject: [PATCH 10/47] [ANDROAPP-500] show min attributes message --- .../searchTrackEntity/SearchTEIViewModel.kt | 1 + .../searchparameters/SearchParametersScreen.kt | 13 +++++++++++-- .../model/SearchParametersUiState.kt | 3 +++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt index f296718dd6..231bbeb7d2 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt @@ -398,6 +398,7 @@ class SearchTEIViewModel( minAttributesToSearch, ) uiState = uiState.copy(minAttributesMessage = message) + uiState.shouldShowMinAttributeWarning.emit(true) } } } diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt index 827551d3ff..f63d5c789f 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt @@ -9,6 +9,7 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.Icon import androidx.compose.material.Scaffold +import androidx.compose.material.SnackbarDuration import androidx.compose.material.SnackbarHost import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Cancel @@ -21,6 +22,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch import org.dhis2.usescases.searchTrackEntity.SearchTEIViewModel import org.dhis2.usescases.searchTrackEntity.searchparameters.model.SearchParameter @@ -44,9 +46,16 @@ fun SearchParametersScreen( val snackBarHostState = scaffoldState.snackbarHostState val coroutineScope = rememberCoroutineScope() - uiState.minAttributesMessage?.let { + uiState.minAttributesMessage?.let { message -> coroutineScope.launch { - snackBarHostState.showSnackbar(it) + uiState.shouldShowMinAttributeWarning.collectLatest { + if (it) { + snackBarHostState.showSnackbar( + message = message, + duration = SnackbarDuration.Short, + ) + } + } } } diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParametersUiState.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParametersUiState.kt index 4892465859..da5c146c0e 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParametersUiState.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParametersUiState.kt @@ -1,6 +1,9 @@ package org.dhis2.usescases.searchTrackEntity.searchparameters.model +import kotlinx.coroutines.flow.MutableSharedFlow + data class SearchParametersUiState( val items: List = listOf(), val minAttributesMessage: String? = null, + val shouldShowMinAttributeWarning: MutableSharedFlow = MutableSharedFlow(), ) From a4883ac456aa610b4960fc18faf1cfcc373284b5 Mon Sep 17 00:00:00 2001 From: andresmr Date: Thu, 8 Feb 2024 16:59:20 +0100 Subject: [PATCH 11/47] [ANDROAPP-5800] handle focus --- .../provider/ParameterSelectorItemProvider.kt | 69 ++++++++++++++++--- 1 file changed, 59 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt index 67b80aebd6..2692b9d232 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt @@ -5,6 +5,10 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.text.input.TextFieldValue import org.dhis2.usescases.searchTrackEntity.searchparameters.model.SearchParameter import org.hisp.dhis.mobile.ui.designsystem.component.InputShellState @@ -17,20 +21,42 @@ fun provideParameterSelectorItem( searchParameter: SearchParameter, onValueChange: (uid: String, value: String?) -> Unit, ): ParameterSelectorItemModel { + val focusRequester = remember { FocusRequester() } + var value by remember(searchParameter.value) { mutableStateOf(TextFieldValue(searchParameter.value ?: "")) } + var status by remember { + mutableStateOf( + if (value.text.isEmpty()) { + ParameterSelectorItemModel.Status.CLOSED + } else { + ParameterSelectorItemModel.Status.UNFOCUSED + }, + ) + } + + val modifierWithFocus = Modifier + .focusRequester(focusRequester) + .onFocusChanged { + status = if (it.isFocused) { + ParameterSelectorItemModel.Status.FOCUSED + } else { + ParameterSelectorItemModel.Status.UNFOCUSED + } + } + return ParameterSelectorItemModel( label = searchParameter.label, helper = searchParameter.helper, inputField = { - InputText( - title = searchParameter.label, - state = InputShellState.UNFOCUSED, - inputTextFieldValue = value, - inputStyle = InputStyle.ParameterInputStyle(), - onValueChanged = { + ProvideInputField( + modifier = modifierWithFocus, + searchParameter = searchParameter, + status = status, + value = value, + onValueChange = { value = it ?: TextFieldValue() onValueChange( searchParameter.uid, @@ -39,10 +65,33 @@ fun provideParameterSelectorItem( }, ) }, - status = if (value.text.isEmpty()) { - ParameterSelectorItemModel.Status.CLOSED - } else { - ParameterSelectorItemModel.Status.OPENED + status = status, + onExpand = { + status = ParameterSelectorItemModel.Status.UNFOCUSED + }, + ) +} + +@Composable +fun ProvideInputField( + modifier: Modifier, + searchParameter: SearchParameter, + status: ParameterSelectorItemModel.Status, + value: TextFieldValue?, + onValueChange: (value: TextFieldValue?) -> Unit, + +) { + InputText( + modifier = modifier, + title = searchParameter.label, + state = when (status) { + ParameterSelectorItemModel.Status.FOCUSED -> InputShellState.FOCUSED + else -> InputShellState.UNFOCUSED + }, + inputTextFieldValue = value, + inputStyle = InputStyle.ParameterInputStyle(), + onValueChanged = { + onValueChange.invoke(it) }, ) } From 056d4c1ffb9e6218992803d9701856cdd45d6b0e Mon Sep 17 00:00:00 2001 From: andresmr Date: Thu, 8 Feb 2024 17:38:30 +0100 Subject: [PATCH 12/47] [ANDROAPP-5800] update design library --- .../dhis2/usescases/searchTrackEntity/SearchRepository.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchRepository.java b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchRepository.java index 7733bde7bb..db0341e5e1 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchRepository.java +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchRepository.java @@ -2,13 +2,11 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.lifecycle.LiveData; -import androidx.paging.PagedList; -import androidx.paging.PagingData; import org.dhis2.commons.data.EventViewModel; import org.dhis2.commons.data.SearchTeiModel; import org.dhis2.commons.data.tuples.Pair; +import org.dhis2.commons.filters.sorting.SortingItem; import org.dhis2.data.search.SearchParametersModel; import org.hisp.dhis.android.core.arch.call.D2Progress; import org.hisp.dhis.android.core.organisationunit.OrganisationUnit; From 95fbc6afa5738652be8375cf37e62e6ceb1f537c Mon Sep 17 00:00:00 2001 From: andresmr Date: Fri, 9 Feb 2024 14:43:19 +0100 Subject: [PATCH 13/47] [ANDROAPP-5800] clear search parameters --- .../searchTrackEntity/SearchTEIViewModel.kt | 20 +++++++++++++++++++ .../provider/ParameterSelectorItemProvider.kt | 4 +++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt index 231bbeb7d2..f5b1b2b126 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt @@ -221,15 +221,35 @@ class SearchTEIViewModel( queryData[uid] = value } + updateSearchParameters(uid, value) updateSearch() } + private fun updateSearchParameters(uid: String, value: String?) { + val updatedItems = uiState.items.map { + if (it.uid == uid) { + it.copy(value = value) + } else { + it + } + } + uiState = uiState.copy(items = updatedItems) + } + fun clearQueryData() { queryData.clear() + clearSearchParameters() updateSearch() performSearch() } + private fun clearSearchParameters() { + val updatedItems = uiState.items.map { + it.copy(value = null) + } + uiState = uiState.copy(items = updatedItems) + } + private fun updateSearch() { if (_screenState.value is SearchList) { val currentSearchList = _screenState.value as SearchList diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt index 2692b9d232..7dca4deb7d 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt @@ -9,6 +9,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.onFocusChanged +import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.TextFieldValue import org.dhis2.usescases.searchTrackEntity.searchparameters.model.SearchParameter import org.hisp.dhis.mobile.ui.designsystem.component.InputShellState @@ -23,8 +24,9 @@ fun provideParameterSelectorItem( ): ParameterSelectorItemModel { val focusRequester = remember { FocusRequester() } + val textSelection = TextRange(searchParameter.value?.length ?: 0) var value by remember(searchParameter.value) { - mutableStateOf(TextFieldValue(searchParameter.value ?: "")) + mutableStateOf(TextFieldValue(searchParameter.value ?: "", textSelection)) } var status by remember { From 9f659f98a532dd28554cc7cf7ef82f56e98cca53 Mon Sep 17 00:00:00 2001 From: andresmr Date: Mon, 12 Feb 2024 10:23:41 +0100 Subject: [PATCH 14/47] [ANDROAPP-5800] Use fieldUiModel for parameter provider --- .../searchTrackEntity/SearchTEActivity.java | 11 +++- .../searchTrackEntity/SearchTEIViewModel.kt | 38 +++++++++-- .../searchTrackEntity/SearchTEModule.java | 5 +- .../SearchParametersRepository.kt | 45 +++++-------- .../SearchParametersScreen.kt | 40 ++++++++--- .../model/SearchParametersUiState.kt | 3 +- .../provider/ParameterSelectorItemProvider.kt | 66 ++++++------------- .../ui/provider/inputfield/FieldProvider.kt | 2 +- 8 files changed, 118 insertions(+), 92 deletions(-) diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java index af92d9ca2a..1f3d95e304 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java @@ -31,6 +31,7 @@ import org.dhis2.commons.filters.FiltersAdapter; import org.dhis2.commons.network.NetworkUtils; import org.dhis2.commons.orgunitselector.OUTreeFragment; +import org.dhis2.commons.resources.ResourceManager; import org.dhis2.commons.sync.SyncContext; import org.dhis2.data.forms.dataentry.ProgramAdapter; import org.dhis2.databinding.ActivitySearchBinding; @@ -87,6 +88,9 @@ public class SearchTEActivity extends ActivityGlobalAbstract implements SearchTE @Inject FeatureConfigRepository featureConfig; + @Inject + ResourceManager resourceManager; + private static final String INITIAL_PAGE = "initialPage"; private String initialProgram; @@ -343,7 +347,12 @@ public void updateFilters(int totalFilters) { } private void initSearchParameters() { - initSearchScreen(binding.searchContainer, viewModel, initialProgram, tEType, + initSearchScreen( + binding.searchContainer, + viewModel, + initialProgram, + tEType, + resourceManager, () -> { presenter.onClearClick(); return Unit.INSTANCE; diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt index f5b1b2b126..1877c987da 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt @@ -24,6 +24,8 @@ import org.dhis2.commons.network.NetworkUtils import org.dhis2.commons.resources.ResourceManager import org.dhis2.commons.viewmodel.DispatcherProvider import org.dhis2.data.search.SearchParametersModel +import org.dhis2.form.model.FieldUiModelImpl +import org.dhis2.form.ui.intent.FormIntent import org.dhis2.maps.layer.basemaps.BaseMapStyle import org.dhis2.maps.usecases.MapStyleConfiguration import org.dhis2.usescases.searchTrackEntity.listView.SearchResult @@ -214,7 +216,7 @@ class SearchTEIViewModel( performSearch() } - fun updateQuery(uid: String, value: String?) { + private fun updateQuery(uid: String, value: String?) { if (value.isNullOrEmpty()) { queryData.remove(uid) } else { @@ -228,7 +230,7 @@ class SearchTEIViewModel( private fun updateSearchParameters(uid: String, value: String?) { val updatedItems = uiState.items.map { if (it.uid == uid) { - it.copy(value = value) + (it as FieldUiModelImpl).copy(value = value) } else { it } @@ -245,7 +247,7 @@ class SearchTEIViewModel( private fun clearSearchParameters() { val updatedItems = uiState.items.map { - it.copy(value = null) + (it as FieldUiModelImpl).copy(value = null) } uiState = uiState.copy(items = updatedItems) } @@ -733,9 +735,35 @@ class SearchTEIViewModel( ) { fetchJob?.cancel() fetchJob = viewModelScope.launch { - val searchParameters = + val fieldUiModels = searchParametersRepository.searchParameters(programUid, teiTypeUid) - uiState = uiState.copy(items = searchParameters) + uiState = uiState.copy(items = fieldUiModels) + } + } + + fun onParameterIntent(formIntent: FormIntent) { + when (formIntent) { + is FormIntent.OnTextChange -> { + updateQuery( + formIntent.uid, + formIntent.value, + ) + } + is FormIntent.OnFocus -> { + /*val updatedItems = uiState.items.map { + if (it.uid == formIntent.uid) { + (it as FieldUiModelImpl).copy(focused = true) + } else { + it + } + } + uiState = uiState.copy(items = updatedItems)*/ + } + + is FormIntent.ClearValue -> { + } + + else -> {} } } } diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEModule.java b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEModule.java index 0bf4e54946..435697699b 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEModule.java +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEModule.java @@ -297,9 +297,10 @@ SearchTeiViewModelFactory providesViewModelFactory( @PerActivity SearchParametersRepository provideSearchParametersRepository( D2 d2, - DispatcherProvider dispatcherProvider + DispatcherProvider dispatcherProvider, + FieldViewModelFactory fieldViewModelFactory ) { - return new SearchParametersRepository(d2, dispatcherProvider); + return new SearchParametersRepository(d2, dispatcherProvider, fieldViewModelFactory); } @Provides diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersRepository.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersRepository.kt index e324d2967e..8392dd8c19 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersRepository.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersRepository.kt @@ -2,8 +2,9 @@ package org.dhis2.usescases.searchTrackEntity.searchparameters import kotlinx.coroutines.withContext import org.dhis2.commons.viewmodel.DispatcherProvider +import org.dhis2.form.model.FieldUiModel import org.dhis2.form.model.OptionSetConfiguration -import org.dhis2.usescases.searchTrackEntity.searchparameters.model.SearchParameter +import org.dhis2.form.ui.FieldViewModelFactory import org.hisp.dhis.android.core.D2 import org.hisp.dhis.android.core.arch.repositories.scope.RepositoryScope import org.hisp.dhis.android.core.common.ValueType @@ -11,17 +12,17 @@ import org.hisp.dhis.android.core.common.ValueType class SearchParametersRepository( private val d2: D2, private val dispatcher: DispatcherProvider, - + private val fieldViewModelFactory: FieldViewModelFactory, ) { - suspend fun searchParameters(programUid: String?, teiTypeUid: String): List = + suspend fun searchParameters(programUid: String?, teiTypeUid: String): List = withContext(dispatcher.io()) { programUid?.let { programTrackedEntityAttributes(programUid) } ?: trackedEntitySearchFields(teiTypeUid) } - private fun programTrackedEntityAttributes(programUid: String): List { + private fun programTrackedEntityAttributes(programUid: String): List { val searchableAttributes = d2.programModule().programTrackedEntityAttributes() .withRenderType() .byProgram().eq(programUid) @@ -50,17 +51,12 @@ class SearchParametersRepository( .blockingGet() } } - /*fieldViewModelFactory.createForAttribute( - attribute, - programAttribute, - currentSearchValues[attribute.uid()], - true, - optionSetConfiguration, - )*/ - SearchParameter( - uid = attribute.uid(), - label = attribute.displayFormName() ?: "", - valueType = attribute.valueType()!!, + fieldViewModelFactory.createForAttribute( + trackedEntityAttribute = attribute, + programTrackedEntityAttribute = programAttribute, + value = null, + editable = true, + optionSetConfiguration = optionSetConfiguration, ) } }.filter { parameter -> @@ -70,7 +66,7 @@ class SearchParametersRepository( } } - private fun trackedEntitySearchFields(teiTypeUid: String): List { + private fun trackedEntitySearchFields(teiTypeUid: String): List { val teTypeAttributes = d2.trackedEntityModule().trackedEntityTypeAttributes() .byTrackedEntityTypeUid().eq(teiTypeUid) .bySearchable().isTrue @@ -94,17 +90,12 @@ class SearchParametersRepository( } } - /*fieldViewModelFactory.createForAttribute( - attribute, - null, - currentSearchValues[attribute.uid()], - true, - optionSetConfiguration, - )*/ - SearchParameter( - attribute.uid(), - attribute.displayFormName() ?: "", - attribute.valueType()!!, + fieldViewModelFactory.createForAttribute( + trackedEntityAttribute = attribute, + programTrackedEntityAttribute = null, + value = null, + editable = true, + optionSetConfiguration = optionSetConfiguration, ) } }.filter { parameter -> diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt index f63d5c789f..3490ae5b2a 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt @@ -15,20 +15,24 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Cancel import androidx.compose.material.rememberScaffoldState import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch +import org.dhis2.commons.resources.ResourceManager +import org.dhis2.form.model.FieldUiModel +import org.dhis2.form.ui.event.RecyclerViewUiEvents +import org.dhis2.form.ui.intent.FormIntent import org.dhis2.usescases.searchTrackEntity.SearchTEIViewModel -import org.dhis2.usescases.searchTrackEntity.searchparameters.model.SearchParameter import org.dhis2.usescases.searchTrackEntity.searchparameters.model.SearchParametersUiState import org.dhis2.usescases.searchTrackEntity.searchparameters.provider.provideParameterSelectorItem -import org.hisp.dhis.android.core.common.ValueType import org.hisp.dhis.mobile.ui.designsystem.component.Button import org.hisp.dhis.mobile.ui.designsystem.component.ButtonStyle import org.hisp.dhis.mobile.ui.designsystem.component.parameter.ParameterSelectorItem @@ -37,14 +41,27 @@ import org.hisp.dhis.mobile.ui.designsystem.theme.SurfaceColor @Composable fun SearchParametersScreen( + resourceManager: ResourceManager, uiState: SearchParametersUiState, - onValueChange: (uid: String, value: String?) -> Unit, + intentHandler: (FormIntent) -> Unit, onSearchClick: () -> Unit, onClear: () -> Unit, ) { val scaffoldState = rememberScaffoldState() val snackBarHostState = scaffoldState.snackbarHostState val coroutineScope = rememberCoroutineScope() + val focusManager = LocalFocusManager.current + val callback = remember { + object : FieldUiModel.Callback { + override fun intent(intent: FormIntent) { + intentHandler.invoke(intent) + } + + override fun recyclerViewUiEvents(uiEvent: RecyclerViewUiEvents) { +// uiEventHandler(uiEvent) + } + } + } uiState.minAttributesMessage?.let { message -> coroutineScope.launch { @@ -85,11 +102,14 @@ fun SearchParametersScreen( .weight(1F) .verticalScroll(rememberScrollState()), ) { - uiState.items.forEach { searchParameter -> + uiState.items.forEach { fieldUiModel -> + fieldUiModel.setCallback(callback) ParameterSelectorItem( model = provideParameterSelectorItem( - searchParameter = searchParameter, - onValueChange = onValueChange, + resources = resourceManager, + focusManager = focusManager, + fieldUiModel = fieldUiModel, + callback = callback, ), ) } @@ -127,7 +147,7 @@ fun SearchParametersScreen( @Preview(showBackground = true) @Composable fun SearchFormPreview() { - SearchParametersScreen( + /*SearchParametersScreen( uiState = SearchParametersUiState( items = listOf( SearchParameter( @@ -145,7 +165,7 @@ fun SearchFormPreview() { onValueChange = { _, _ -> }, onSearchClick = {}, onClear = {}, - ) + )*/ } fun initSearchScreen( @@ -153,6 +173,7 @@ fun initSearchScreen( viewModel: SearchTEIViewModel, program: String?, teiType: String, + resources: ResourceManager, onClear: () -> Unit, ) { viewModel.fetchSearchParameters( @@ -161,9 +182,10 @@ fun initSearchScreen( ) composeView.setContent { SearchParametersScreen( + resourceManager = resources, uiState = viewModel.uiState, - onValueChange = viewModel::updateQuery, onSearchClick = viewModel::onSearchClick, + intentHandler = viewModel::onParameterIntent, onClear = { onClear() viewModel.clearQueryData() diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParametersUiState.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParametersUiState.kt index da5c146c0e..fb91ff1f91 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParametersUiState.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParametersUiState.kt @@ -1,9 +1,10 @@ package org.dhis2.usescases.searchTrackEntity.searchparameters.model import kotlinx.coroutines.flow.MutableSharedFlow +import org.dhis2.form.model.FieldUiModel data class SearchParametersUiState( - val items: List = listOf(), + val items: List = listOf(), val minAttributesMessage: String? = null, val shouldShowMinAttributeWarning: MutableSharedFlow = MutableSharedFlow(), ) diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt index 7dca4deb7d..5ec7ea67bc 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt @@ -6,27 +6,29 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusManager import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.TextFieldValue -import org.dhis2.usescases.searchTrackEntity.searchparameters.model.SearchParameter -import org.hisp.dhis.mobile.ui.designsystem.component.InputShellState -import org.hisp.dhis.mobile.ui.designsystem.component.InputStyle -import org.hisp.dhis.mobile.ui.designsystem.component.InputText +import org.dhis2.commons.resources.ResourceManager +import org.dhis2.form.model.FieldUiModel +import org.dhis2.form.ui.provider.inputfield.FieldProvider import org.hisp.dhis.mobile.ui.designsystem.component.parameter.model.ParameterSelectorItemModel @Composable fun provideParameterSelectorItem( - searchParameter: SearchParameter, - onValueChange: (uid: String, value: String?) -> Unit, + resources: ResourceManager, + focusManager: FocusManager, + fieldUiModel: FieldUiModel, + callback: FieldUiModel.Callback, ): ParameterSelectorItemModel { val focusRequester = remember { FocusRequester() } - val textSelection = TextRange(searchParameter.value?.length ?: 0) - var value by remember(searchParameter.value) { - mutableStateOf(TextFieldValue(searchParameter.value ?: "", textSelection)) + val textSelection = TextRange(fieldUiModel.value?.length ?: 0) + val value by remember(fieldUiModel.value) { + mutableStateOf(TextFieldValue(fieldUiModel.value ?: "", textSelection)) } var status by remember { @@ -50,21 +52,17 @@ fun provideParameterSelectorItem( } return ParameterSelectorItemModel( - label = searchParameter.label, - helper = searchParameter.helper, + label = fieldUiModel.label, + helper = "Optional", inputField = { - ProvideInputField( + FieldProvider( modifier = modifierWithFocus, - searchParameter = searchParameter, - status = status, - value = value, - onValueChange = { - value = it ?: TextFieldValue() - onValueChange( - searchParameter.uid, - value.text, - ) - }, + fieldUiModel = fieldUiModel, + uiEventHandler = callback::recyclerViewUiEvents, + intentHandler = callback::intent, + resources = resources, + focusManager = focusManager, + onNextClicked = { }, ) }, status = status, @@ -73,27 +71,3 @@ fun provideParameterSelectorItem( }, ) } - -@Composable -fun ProvideInputField( - modifier: Modifier, - searchParameter: SearchParameter, - status: ParameterSelectorItemModel.Status, - value: TextFieldValue?, - onValueChange: (value: TextFieldValue?) -> Unit, - -) { - InputText( - modifier = modifier, - title = searchParameter.label, - state = when (status) { - ParameterSelectorItemModel.Status.FOCUSED -> InputShellState.FOCUSED - else -> InputShellState.UNFOCUSED - }, - inputTextFieldValue = value, - inputStyle = InputStyle.ParameterInputStyle(), - onValueChanged = { - onValueChange.invoke(it) - }, - ) -} diff --git a/form/src/main/java/org/dhis2/form/ui/provider/inputfield/FieldProvider.kt b/form/src/main/java/org/dhis2/form/ui/provider/inputfield/FieldProvider.kt index 625a51a7e5..0d296ac333 100644 --- a/form/src/main/java/org/dhis2/form/ui/provider/inputfield/FieldProvider.kt +++ b/form/src/main/java/org/dhis2/form/ui/provider/inputfield/FieldProvider.kt @@ -55,7 +55,7 @@ import org.hisp.dhis.mobile.ui.designsystem.component.internal.RegExValidations @OptIn(ExperimentalFoundationApi::class) @Composable -internal fun FieldProvider( +fun FieldProvider( modifier: Modifier, fieldUiModel: FieldUiModel, uiEventHandler: (RecyclerViewUiEvents) -> Unit, From 874be46e22358d7c08636f317305fa6d897e6118 Mon Sep 17 00:00:00 2001 From: andresmr Date: Mon, 12 Feb 2024 11:29:12 +0100 Subject: [PATCH 15/47] [ANDROAPP-5800] insert inputstyle as parameter --- .../provider/ParameterSelectorItemProvider.kt | 2 + .../ui/provider/inputfield/AgeProvider.kt | 3 ++ .../provider/inputfield/CheckBoxProvider.kt | 7 +++ .../ui/provider/inputfield/DateProvider.kt | 3 ++ .../provider/inputfield/DropdownProvider.kt | 3 ++ .../ui/provider/inputfield/FieldProvider.kt | 51 +++++++++++++++++++ .../InputsForTextValueTypeProvider.kt | 11 ++++ .../inputfield/MatrixInputProvider.kt | 3 ++ .../inputfield/RadioButtonProvider.kt | 5 ++ .../inputfield/SequentialInputProvider.kt | 3 ++ .../ui/provider/inputfield/SwitchProvider.kt | 3 ++ .../inputfield/UnitIntervalInputProvider.kt | 3 ++ 12 files changed, 97 insertions(+) diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt index 5ec7ea67bc..5e860910e9 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt @@ -15,6 +15,7 @@ import androidx.compose.ui.text.input.TextFieldValue import org.dhis2.commons.resources.ResourceManager import org.dhis2.form.model.FieldUiModel import org.dhis2.form.ui.provider.inputfield.FieldProvider +import org.hisp.dhis.mobile.ui.designsystem.component.InputStyle import org.hisp.dhis.mobile.ui.designsystem.component.parameter.model.ParameterSelectorItemModel @Composable @@ -57,6 +58,7 @@ fun provideParameterSelectorItem( inputField = { FieldProvider( modifier = modifierWithFocus, + inputStyle = InputStyle.ParameterInputStyle(), fieldUiModel = fieldUiModel, uiEventHandler = callback::recyclerViewUiEvents, intentHandler = callback::intent, diff --git a/form/src/main/java/org/dhis2/form/ui/provider/inputfield/AgeProvider.kt b/form/src/main/java/org/dhis2/form/ui/provider/inputfield/AgeProvider.kt index ce1e2c866e..2adb4d7de2 100644 --- a/form/src/main/java/org/dhis2/form/ui/provider/inputfield/AgeProvider.kt +++ b/form/src/main/java/org/dhis2/form/ui/provider/inputfield/AgeProvider.kt @@ -18,6 +18,7 @@ import org.dhis2.form.ui.intent.FormIntent import org.hisp.dhis.android.core.common.ValueType import org.hisp.dhis.mobile.ui.designsystem.component.AgeInputType import org.hisp.dhis.mobile.ui.designsystem.component.InputAge +import org.hisp.dhis.mobile.ui.designsystem.component.InputStyle import org.hisp.dhis.mobile.ui.designsystem.component.TimeUnitValues import java.text.SimpleDateFormat import java.util.Calendar @@ -27,6 +28,7 @@ import java.util.Locale @Composable fun ProvideInputAge( modifier: Modifier, + inputStyle: InputStyle, fieldUiModel: FieldUiModel, intentHandler: (FormIntent) -> Unit, uiEventHandler: (RecyclerViewUiEvents) -> Unit, @@ -71,6 +73,7 @@ fun ProvideInputAge( InputAge( title = fieldUiModel.label, inputType = inputType, + inputStyle = inputStyle, onCalendarActionClicked = { uiEventHandler.invoke( RecyclerViewUiEvents.OpenCustomCalendar( diff --git a/form/src/main/java/org/dhis2/form/ui/provider/inputfield/CheckBoxProvider.kt b/form/src/main/java/org/dhis2/form/ui/provider/inputfield/CheckBoxProvider.kt index 84333f9865..c0bfdea890 100644 --- a/form/src/main/java/org/dhis2/form/ui/provider/inputfield/CheckBoxProvider.kt +++ b/form/src/main/java/org/dhis2/form/ui/provider/inputfield/CheckBoxProvider.kt @@ -13,11 +13,13 @@ import org.dhis2.form.model.FieldUiModel import org.dhis2.form.ui.intent.FormIntent import org.hisp.dhis.mobile.ui.designsystem.component.CheckBoxData import org.hisp.dhis.mobile.ui.designsystem.component.InputCheckBox +import org.hisp.dhis.mobile.ui.designsystem.component.InputStyle import org.hisp.dhis.mobile.ui.designsystem.component.InputYesOnlyCheckBox @Composable internal fun ProvideCheckBoxInput( modifier: Modifier, + inputStyle: InputStyle, fieldUiModel: FieldUiModel, intentHandler: (FormIntent) -> Unit, focusRequester: FocusRequester, @@ -33,6 +35,7 @@ internal fun ProvideCheckBoxInput( InputCheckBox( modifier = modifier, + inputStyle = inputStyle, title = fieldUiModel.label, checkBoxData = data, orientation = fieldUiModel.orientation(), @@ -63,6 +66,7 @@ internal fun ProvideCheckBoxInput( @Composable internal fun ProvideYesNoCheckBoxInput( modifier: Modifier, + inputStyle: InputStyle, fieldUiModel: FieldUiModel, intentHandler: (FormIntent) -> Unit, resources: ResourceManager, @@ -85,6 +89,7 @@ internal fun ProvideYesNoCheckBoxInput( InputCheckBox( modifier = modifier, + inputStyle = inputStyle, title = fieldUiModel.label, checkBoxData = data, orientation = fieldUiModel.orientation(), @@ -130,6 +135,7 @@ internal fun ProvideYesNoCheckBoxInput( @Composable internal fun ProvideYesOnlyCheckBoxInput( modifier: Modifier, + inputStyle: InputStyle, fieldUiModel: FieldUiModel, intentHandler: (FormIntent) -> Unit, focusRequester: FocusRequester, @@ -143,6 +149,7 @@ internal fun ProvideYesOnlyCheckBoxInput( InputYesOnlyCheckBox( modifier = modifier, + inputStyle = inputStyle, checkBoxData = cbData, state = fieldUiModel.inputState(), supportingText = fieldUiModel.supportingText(), diff --git a/form/src/main/java/org/dhis2/form/ui/provider/inputfield/DateProvider.kt b/form/src/main/java/org/dhis2/form/ui/provider/inputfield/DateProvider.kt index 3083b5dfc3..65a31588ea 100644 --- a/form/src/main/java/org/dhis2/form/ui/provider/inputfield/DateProvider.kt +++ b/form/src/main/java/org/dhis2/form/ui/provider/inputfield/DateProvider.kt @@ -21,6 +21,7 @@ import org.dhis2.form.ui.intent.FormIntent import org.hisp.dhis.android.core.common.ValueType import org.hisp.dhis.mobile.ui.designsystem.component.DateTimeActionIconType import org.hisp.dhis.mobile.ui.designsystem.component.InputDateTime +import org.hisp.dhis.mobile.ui.designsystem.component.InputStyle import org.hisp.dhis.mobile.ui.designsystem.component.internal.DateTimeTransformation import org.hisp.dhis.mobile.ui.designsystem.component.internal.DateTransformation import org.hisp.dhis.mobile.ui.designsystem.component.internal.TimeTransformation @@ -28,6 +29,7 @@ import org.hisp.dhis.mobile.ui.designsystem.component.internal.TimeTransformatio @Composable fun ProvideInputDate( modifier: Modifier, + inputStyle: InputStyle, fieldUiModel: FieldUiModel, intentHandler: (FormIntent) -> Unit, uiEventHandler: (RecyclerViewUiEvents) -> Unit, @@ -91,6 +93,7 @@ fun ProvideInputDate( } }, modifier = modifier.semantics { contentDescription = formatStoredDateToUI(value.text, fieldUiModel.valueType) }, + inputStyle = inputStyle, state = fieldUiModel.inputState(), legendData = fieldUiModel.legend(), supportingText = fieldUiModel.supportingText(), diff --git a/form/src/main/java/org/dhis2/form/ui/provider/inputfield/DropdownProvider.kt b/form/src/main/java/org/dhis2/form/ui/provider/inputfield/DropdownProvider.kt index 93be6d4dbe..00ce8262e9 100644 --- a/form/src/main/java/org/dhis2/form/ui/provider/inputfield/DropdownProvider.kt +++ b/form/src/main/java/org/dhis2/form/ui/provider/inputfield/DropdownProvider.kt @@ -12,10 +12,12 @@ import org.dhis2.form.extensions.supportingText import org.dhis2.form.model.FieldUiModel import org.hisp.dhis.mobile.ui.designsystem.component.DropdownItem import org.hisp.dhis.mobile.ui.designsystem.component.InputDropDown +import org.hisp.dhis.mobile.ui.designsystem.component.InputStyle @Composable fun ProvideDropdownInput( modifier: Modifier, + inputStyle: InputStyle, fieldUiModel: FieldUiModel, ) { var selectedItem by remember(fieldUiModel) { @@ -27,6 +29,7 @@ fun ProvideDropdownInput( val dropdownItems = selectableOptions?.map { DropdownItem(it.displayName() ?: it.code() ?: "") } InputDropDown( modifier = modifier, + inputStyle = inputStyle, title = fieldUiModel.label, state = fieldUiModel.inputState(), selectedItem = DropdownItem(selectedItem.label), diff --git a/form/src/main/java/org/dhis2/form/ui/provider/inputfield/FieldProvider.kt b/form/src/main/java/org/dhis2/form/ui/provider/inputfield/FieldProvider.kt index 0d296ac333..d7a2c5ad6a 100644 --- a/form/src/main/java/org/dhis2/form/ui/provider/inputfield/FieldProvider.kt +++ b/form/src/main/java/org/dhis2/form/ui/provider/inputfield/FieldProvider.kt @@ -51,12 +51,14 @@ import org.hisp.dhis.mobile.ui.designsystem.component.InputPercentage import org.hisp.dhis.mobile.ui.designsystem.component.InputPhoneNumber import org.hisp.dhis.mobile.ui.designsystem.component.InputPositiveInteger import org.hisp.dhis.mobile.ui.designsystem.component.InputPositiveIntegerOrZero +import org.hisp.dhis.mobile.ui.designsystem.component.InputStyle import org.hisp.dhis.mobile.ui.designsystem.component.internal.RegExValidations @OptIn(ExperimentalFoundationApi::class) @Composable fun FieldProvider( modifier: Modifier, + inputStyle: InputStyle = InputStyle.DataInputStyle(), fieldUiModel: FieldUiModel, uiEventHandler: (RecyclerViewUiEvents) -> Unit, intentHandler: (FormIntent) -> Unit, @@ -105,6 +107,7 @@ fun FieldProvider( ValueType.TEXT -> { ProvideInputsForValueTypeText( modifier = modifierWithFocus, + inputStyle = inputStyle, fieldUiModel = fieldUiModel, intentHandler = intentHandler, uiEventHandler = uiEventHandler, @@ -116,6 +119,7 @@ fun FieldProvider( ValueType.INTEGER_POSITIVE -> { ProvideIntegerPositive( modifier = modifierWithFocus, + inputStyle = inputStyle, fieldUiModel = fieldUiModel, intentHandler = intentHandler, focusManager = focusManager, @@ -126,6 +130,7 @@ fun FieldProvider( ValueType.INTEGER_ZERO_OR_POSITIVE -> { ProvideIntegerPositiveOrZero( modifier = modifierWithFocus, + inputStyle = inputStyle, fieldUiModel = fieldUiModel, intentHandler = intentHandler, focusManager = focusManager, @@ -137,6 +142,7 @@ fun FieldProvider( ValueType.PERCENTAGE -> { ProvidePercentage( modifier = modifierWithFocus, + inputStyle = inputStyle, fieldUiModel = fieldUiModel, intentHandler = intentHandler, focusManager = focusManager, @@ -148,6 +154,7 @@ fun FieldProvider( ValueType.NUMBER -> { ProvideNumber( modifier = modifierWithFocus, + inputStyle = inputStyle, fieldUiModel = fieldUiModel, intentHandler = intentHandler, focusManager = focusManager, @@ -159,6 +166,7 @@ fun FieldProvider( ValueType.INTEGER_NEGATIVE -> { ProvideIntegerNegative( modifier = modifierWithFocus, + inputStyle = inputStyle, fieldUiModel = fieldUiModel, intentHandler = intentHandler, focusManager = focusManager, @@ -170,6 +178,7 @@ fun FieldProvider( ValueType.LONG_TEXT -> { ProvideLongText( modifier = modifierWithFocus, + inputStyle = inputStyle, fieldUiModel = fieldUiModel, intentHandler = intentHandler, focusManager = focusManager, @@ -181,6 +190,7 @@ fun FieldProvider( ValueType.LETTER -> { ProvideLetter( modifier = modifierWithFocus, + inputStyle = inputStyle, fieldUiModel = fieldUiModel, intentHandler = intentHandler, focusManager = focusManager, @@ -192,6 +202,7 @@ fun FieldProvider( ValueType.INTEGER -> { ProvideInteger( modifier = modifierWithFocus, + inputStyle = inputStyle, fieldUiModel = fieldUiModel, intentHandler = intentHandler, focusManager = focusManager, @@ -203,6 +214,7 @@ fun FieldProvider( ValueType.ORGANISATION_UNIT -> { ProvideOrgUnitInput( modifier = modifierWithFocus, + inputStyle = inputStyle, fieldUiModel = fieldUiModel, uiEventHandler = uiEventHandler, intentHandler = intentHandler, @@ -213,6 +225,7 @@ fun FieldProvider( ValueType.UNIT_INTERVAL -> { ProvideUnitIntervalInput( modifier = modifierWithFocus, + inputStyle = inputStyle, fieldUiModel = fieldUiModel, intentHandler = intentHandler, onNextClicked = onNextClicked, @@ -222,6 +235,7 @@ fun FieldProvider( ValueType.EMAIL -> { ProvideEmail( modifier = modifierWithFocus, + inputStyle = inputStyle, fieldUiModel = fieldUiModel, intentHandler = intentHandler, uiEventHandler = uiEventHandler, @@ -243,6 +257,7 @@ fun FieldProvider( ValueType.URL -> { ProvideInputLink( modifier = modifierWithFocus, + inputStyle = inputStyle, fieldUiModel = fieldUiModel, intentHandler = intentHandler, uiEventHandler = uiEventHandler, @@ -259,6 +274,7 @@ fun FieldProvider( -> { ProvideYesNoCheckBoxInput( modifier = modifierWithFocus, + inputStyle = inputStyle, fieldUiModel = fieldUiModel, intentHandler = intentHandler, resources = resources, @@ -269,6 +285,7 @@ fun FieldProvider( else -> { ProvideYesNoRadioButtonInput( modifier = modifierWithFocus, + inputStyle = inputStyle, fieldUiModel = fieldUiModel, intentHandler = intentHandler, resources = resources, @@ -283,6 +300,7 @@ fun FieldProvider( UiRenderType.TOGGLE -> { ProvideYesOnlySwitchInput( modifier = modifierWithFocus, + inputStyle = inputStyle, fieldUiModel = fieldUiModel, intentHandler = intentHandler, ) @@ -291,6 +309,7 @@ fun FieldProvider( else -> { ProvideYesOnlyCheckBoxInput( modifier = modifierWithFocus, + inputStyle = inputStyle, fieldUiModel = fieldUiModel, intentHandler = intentHandler, focusRequester = focusRequester, @@ -302,6 +321,7 @@ fun FieldProvider( ValueType.PHONE_NUMBER -> { ProvideInputPhoneNumber( modifier = modifierWithFocus, + inputStyle = inputStyle, fieldUiModel = fieldUiModel, intentHandler = intentHandler, uiEventHandler = uiEventHandler, @@ -317,6 +337,7 @@ fun FieldProvider( -> { ProvideInputDate( modifier = modifierWithFocus, + inputStyle = inputStyle, fieldUiModel = fieldUiModel, intentHandler = intentHandler, uiEventHandler = uiEventHandler, @@ -370,6 +391,7 @@ fun FieldProvider( ValueType.AGE -> { ProvideInputAge( modifier = modifierWithFocus, + inputStyle = inputStyle, fieldUiModel = fieldUiModel, intentHandler = intentHandler, uiEventHandler = uiEventHandler, @@ -394,6 +416,7 @@ fun FieldProvider( -> { ProvideRadioButtonInput( modifier = modifierWithFocus, + inputStyle = inputStyle, fieldUiModel = fieldUiModel, intentHandler = intentHandler, focusRequester = focusRequester, @@ -405,6 +428,7 @@ fun FieldProvider( -> { ProvideCheckBoxInput( modifier = modifierWithFocus, + inputStyle = inputStyle, fieldUiModel = fieldUiModel, intentHandler = intentHandler, focusRequester = focusRequester, @@ -414,6 +438,7 @@ fun FieldProvider( UiRenderType.MATRIX -> { ProvideMatrixInput( modifier = modifierWithFocus, + inputStyle = inputStyle, fieldUiModel = fieldUiModel, intentHandler = intentHandler, context = context, @@ -423,6 +448,7 @@ fun FieldProvider( UiRenderType.SEQUENCIAL -> { ProvideSequentialInput( modifier = modifierWithFocus, + inputStyle = inputStyle, fieldUiModel = fieldUiModel, intentHandler = intentHandler, context = context, @@ -434,6 +460,7 @@ fun FieldProvider( else -> { ProvideDropdownInput( modifier = modifierWithFocus, + inputStyle = inputStyle, fieldUiModel = fieldUiModel, ) } @@ -444,6 +471,7 @@ fun FieldProvider( @Composable private fun ProvideIntegerPositive( modifier: Modifier, + inputStyle: InputStyle, fieldUiModel: FieldUiModel, intentHandler: (FormIntent) -> Unit, focusManager: FocusManager, @@ -457,6 +485,7 @@ private fun ProvideIntegerPositive( InputPositiveInteger( modifier = modifier.fillMaxWidth(), + inputStyle = inputStyle, title = fieldUiModel.label, state = fieldUiModel.inputState(), supportingText = fieldUiModel.supportingText(), @@ -484,6 +513,7 @@ private fun ProvideIntegerPositive( @Composable private fun ProvideIntegerPositiveOrZero( modifier: Modifier, + inputStyle: InputStyle, fieldUiModel: FieldUiModel, intentHandler: (FormIntent) -> Unit, focusManager: FocusManager, @@ -498,6 +528,7 @@ private fun ProvideIntegerPositiveOrZero( InputPositiveIntegerOrZero( modifier = modifier.fillMaxWidth(), + inputStyle = inputStyle, title = fieldUiModel.label, state = fieldUiModel.inputState(), supportingText = fieldUiModel.supportingText(), @@ -525,6 +556,7 @@ private fun ProvideIntegerPositiveOrZero( @Composable private fun ProvidePercentage( modifier: Modifier = Modifier, + inputStyle: InputStyle, fieldUiModel: FieldUiModel, intentHandler: (FormIntent) -> Unit, focusManager: FocusManager, @@ -539,6 +571,7 @@ private fun ProvidePercentage( InputPercentage( modifier = modifier.fillMaxWidth(), + inputStyle = inputStyle, title = fieldUiModel.label, state = fieldUiModel.inputState(), supportingText = fieldUiModel.supportingText(), @@ -566,6 +599,7 @@ private fun ProvidePercentage( @Composable private fun ProvideNumber( modifier: Modifier, + inputStyle: InputStyle, fieldUiModel: FieldUiModel, intentHandler: (FormIntent) -> Unit, focusManager: FocusManager, @@ -580,6 +614,7 @@ private fun ProvideNumber( InputNumber( modifier = modifier.fillMaxWidth(), + inputStyle = inputStyle, title = fieldUiModel.label, state = fieldUiModel.inputState(), supportingText = fieldUiModel.supportingText(), @@ -608,6 +643,7 @@ private fun ProvideNumber( @Composable private fun ProvideIntegerNegative( modifier: Modifier = Modifier, + inputStyle: InputStyle, fieldUiModel: FieldUiModel, intentHandler: (FormIntent) -> Unit, focusManager: FocusManager, @@ -621,6 +657,7 @@ private fun ProvideIntegerNegative( InputNegativeInteger( modifier = modifier.fillMaxWidth(), + inputStyle = inputStyle, title = fieldUiModel.label, state = fieldUiModel.inputState(), supportingText = fieldUiModel.supportingText(), @@ -648,6 +685,7 @@ private fun ProvideIntegerNegative( @Composable private fun ProvideLongText( modifier: Modifier, + inputStyle: InputStyle, fieldUiModel: FieldUiModel, intentHandler: (FormIntent) -> Unit, focusManager: FocusManager, @@ -662,6 +700,7 @@ private fun ProvideLongText( InputLongText( modifier = modifier.fillMaxWidth(), + inputStyle = inputStyle, title = fieldUiModel.label, state = fieldUiModel.inputState(), supportingText = fieldUiModel.supportingText(), @@ -690,6 +729,7 @@ private fun ProvideLongText( @Composable private fun ProvideLetter( modifier: Modifier, + inputStyle: InputStyle, fieldUiModel: FieldUiModel, intentHandler: (FormIntent) -> Unit, focusManager: FocusManager, @@ -703,6 +743,7 @@ private fun ProvideLetter( InputLetter( modifier = modifier.fillMaxWidth(), + inputStyle = inputStyle, title = fieldUiModel.label, state = fieldUiModel.inputState(), supportingText = fieldUiModel.supportingText(), @@ -730,6 +771,7 @@ private fun ProvideLetter( @Composable private fun ProvideInteger( modifier: Modifier, + inputStyle: InputStyle, fieldUiModel: FieldUiModel, intentHandler: (FormIntent) -> Unit, focusManager: FocusManager, @@ -743,6 +785,7 @@ private fun ProvideInteger( InputInteger( modifier = modifier.fillMaxWidth(), + inputStyle = inputStyle, title = fieldUiModel.label, state = fieldUiModel.inputState(), supportingText = fieldUiModel.supportingText(), @@ -770,6 +813,7 @@ private fun ProvideInteger( @Composable private fun ProvideEmail( modifier: Modifier, + inputStyle: InputStyle, fieldUiModel: FieldUiModel, uiEventHandler: (RecyclerViewUiEvents) -> Unit, intentHandler: (FormIntent) -> Unit, @@ -784,6 +828,7 @@ private fun ProvideEmail( InputEmail( modifier = modifier.fillMaxWidth(), + inputStyle = inputStyle, title = fieldUiModel.label, state = fieldUiModel.inputState(), supportingText = fieldUiModel.supportingText(), @@ -820,6 +865,7 @@ private fun ProvideEmail( @Composable private fun ProvideInputPhoneNumber( fieldUiModel: FieldUiModel, + inputStyle: InputStyle, intentHandler: (FormIntent) -> Unit, uiEventHandler: (RecyclerViewUiEvents) -> Unit, focusManager: FocusManager, @@ -835,6 +881,7 @@ private fun ProvideInputPhoneNumber( InputPhoneNumber( modifier = modifier.fillMaxWidth(), + inputStyle = inputStyle, title = fieldUiModel.label, state = fieldUiModel.inputState(), supportingText = fieldUiModel.supportingText(), @@ -871,6 +918,7 @@ private fun ProvideInputPhoneNumber( @Composable private fun ProvideInputLink( modifier: Modifier, + inputStyle: InputStyle, fieldUiModel: FieldUiModel, intentHandler: (FormIntent) -> Unit, uiEventHandler: (RecyclerViewUiEvents) -> Unit, @@ -886,6 +934,7 @@ private fun ProvideInputLink( InputLink( modifier = modifier.fillMaxWidth(), + inputStyle = inputStyle, title = fieldUiModel.label, state = fieldUiModel.inputState(), supportingText = fieldUiModel.supportingText(), @@ -922,6 +971,7 @@ private fun ProvideInputLink( @Composable private fun ProvideOrgUnitInput( modifier: Modifier, + inputStyle: InputStyle, fieldUiModel: FieldUiModel, uiEventHandler: (RecyclerViewUiEvents) -> Unit, intentHandler: (FormIntent) -> Unit, @@ -935,6 +985,7 @@ private fun ProvideOrgUnitInput( InputOrgUnit( modifier = modifier.fillMaxWidth(), + inputStyle = inputStyle, title = fieldUiModel.label, state = fieldUiModel.inputState(), supportingText = fieldUiModel.supportingText(), diff --git a/form/src/main/java/org/dhis2/form/ui/provider/inputfield/InputsForTextValueTypeProvider.kt b/form/src/main/java/org/dhis2/form/ui/provider/inputfield/InputsForTextValueTypeProvider.kt index 9d069fdea0..b877909e6e 100644 --- a/form/src/main/java/org/dhis2/form/ui/provider/inputfield/InputsForTextValueTypeProvider.kt +++ b/form/src/main/java/org/dhis2/form/ui/provider/inputfield/InputsForTextValueTypeProvider.kt @@ -20,11 +20,13 @@ import org.dhis2.form.ui.event.RecyclerViewUiEvents import org.dhis2.form.ui.intent.FormIntent import org.hisp.dhis.mobile.ui.designsystem.component.InputBarCode import org.hisp.dhis.mobile.ui.designsystem.component.InputQRCode +import org.hisp.dhis.mobile.ui.designsystem.component.InputStyle import org.hisp.dhis.mobile.ui.designsystem.component.InputText @Composable internal fun ProvideInputsForValueTypeText( modifier: Modifier = Modifier, + inputStyle: InputStyle, fieldUiModel: FieldUiModel, intentHandler: (FormIntent) -> Unit, uiEventHandler: (RecyclerViewUiEvents) -> Unit, @@ -35,6 +37,7 @@ internal fun ProvideInputsForValueTypeText( UiRenderType.QR_CODE, UiRenderType.GS1_DATAMATRIX -> { ProvideQRInput( modifier = modifier, + inputStyle = inputStyle, fieldUiModel = fieldUiModel, intentHandler = intentHandler, uiEventHandler = uiEventHandler, @@ -46,6 +49,7 @@ internal fun ProvideInputsForValueTypeText( UiRenderType.BAR_CODE -> { ProvideBarcodeInput( modifier = modifier, + inputStyle = inputStyle, fieldUiModel = fieldUiModel, intentHandler = intentHandler, uiEventHandler = uiEventHandler, @@ -55,6 +59,7 @@ internal fun ProvideInputsForValueTypeText( } else -> { ProvideDefaultTextInput( modifier = modifier, + inputStyle = inputStyle, fieldUiModel = fieldUiModel, intentHandler = intentHandler, focusManager = focusManager, @@ -67,6 +72,7 @@ internal fun ProvideInputsForValueTypeText( @Composable private fun ProvideQRInput( modifier: Modifier, + inputStyle: InputStyle, fieldUiModel: FieldUiModel, intentHandler: (FormIntent) -> Unit, uiEventHandler: (RecyclerViewUiEvents) -> Unit, @@ -85,6 +91,7 @@ private fun ProvideQRInput( supportingText = fieldUiModel.supportingText(), legendData = fieldUiModel.legend(), inputTextFieldValue = value, + inputStyle = inputStyle, isRequiredField = fieldUiModel.mandatory, onNextClicked = onNextClicked, onValueChanged = { @@ -129,6 +136,7 @@ private fun ProvideQRInput( @Composable private fun ProvideDefaultTextInput( modifier: Modifier, + inputStyle: InputStyle = InputStyle.DataInputStyle(), fieldUiModel: FieldUiModel, intentHandler: (FormIntent) -> Unit, focusManager: FocusManager, @@ -144,6 +152,7 @@ private fun ProvideDefaultTextInput( supportingText = fieldUiModel.supportingText(), legendData = fieldUiModel.legend(), inputTextFieldValue = value, + inputStyle = inputStyle, isRequiredField = fieldUiModel.mandatory, onNextClicked = onNextClicked, onValueChanged = { @@ -166,6 +175,7 @@ private fun ProvideDefaultTextInput( @Composable private fun ProvideBarcodeInput( modifier: Modifier, + inputStyle: InputStyle, fieldUiModel: FieldUiModel, intentHandler: (FormIntent) -> Unit, uiEventHandler: (RecyclerViewUiEvents) -> Unit, @@ -180,6 +190,7 @@ private fun ProvideBarcodeInput( InputBarCode( modifier = modifier.fillMaxWidth(), + inputStyle = inputStyle, title = fieldUiModel.label, state = fieldUiModel.inputState(), supportingText = fieldUiModel.supportingText(), diff --git a/form/src/main/java/org/dhis2/form/ui/provider/inputfield/MatrixInputProvider.kt b/form/src/main/java/org/dhis2/form/ui/provider/inputfield/MatrixInputProvider.kt index 9406a26405..51fc1113ee 100644 --- a/form/src/main/java/org/dhis2/form/ui/provider/inputfield/MatrixInputProvider.kt +++ b/form/src/main/java/org/dhis2/form/ui/provider/inputfield/MatrixInputProvider.kt @@ -16,11 +16,13 @@ import org.dhis2.form.extensions.supportingText import org.dhis2.form.model.FieldUiModel import org.dhis2.form.ui.intent.FormIntent import org.hisp.dhis.mobile.ui.designsystem.component.InputMatrix +import org.hisp.dhis.mobile.ui.designsystem.component.InputStyle import org.hisp.dhis.mobile.ui.designsystem.component.internal.IconCardData @Composable internal fun ProvideMatrixInput( modifier: Modifier, + inputStyle: InputStyle, fieldUiModel: FieldUiModel, context: Context, intentHandler: (FormIntent) -> Unit, @@ -76,5 +78,6 @@ internal fun ProvideMatrixInput( legendData = fieldUiModel.legend(), isRequired = fieldUiModel.mandatory, modifier = modifier, + inputStyle = inputStyle, ) } diff --git a/form/src/main/java/org/dhis2/form/ui/provider/inputfield/RadioButtonProvider.kt b/form/src/main/java/org/dhis2/form/ui/provider/inputfield/RadioButtonProvider.kt index 3bfe01c0d8..c74c48149c 100644 --- a/form/src/main/java/org/dhis2/form/ui/provider/inputfield/RadioButtonProvider.kt +++ b/form/src/main/java/org/dhis2/form/ui/provider/inputfield/RadioButtonProvider.kt @@ -12,11 +12,13 @@ import org.dhis2.form.extensions.supportingText import org.dhis2.form.model.FieldUiModel import org.dhis2.form.ui.intent.FormIntent import org.hisp.dhis.mobile.ui.designsystem.component.InputRadioButton +import org.hisp.dhis.mobile.ui.designsystem.component.InputStyle import org.hisp.dhis.mobile.ui.designsystem.component.RadioButtonData @Composable internal fun ProvideRadioButtonInput( modifier: Modifier, + inputStyle: InputStyle, fieldUiModel: FieldUiModel, intentHandler: (FormIntent) -> Unit, focusRequester: FocusRequester, @@ -32,6 +34,7 @@ internal fun ProvideRadioButtonInput( InputRadioButton( modifier = modifier, + inputStyle = inputStyle, title = fieldUiModel.label, radioButtonData = data, orientation = fieldUiModel.orientation(), @@ -57,6 +60,7 @@ internal fun ProvideRadioButtonInput( @Composable internal fun ProvideYesNoRadioButtonInput( modifier: Modifier, + inputStyle: InputStyle, fieldUiModel: FieldUiModel, intentHandler: (FormIntent) -> Unit, resources: ResourceManager, @@ -79,6 +83,7 @@ internal fun ProvideYesNoRadioButtonInput( InputRadioButton( modifier = modifier, + inputStyle = inputStyle, title = fieldUiModel.label, radioButtonData = data, orientation = fieldUiModel.orientation(), diff --git a/form/src/main/java/org/dhis2/form/ui/provider/inputfield/SequentialInputProvider.kt b/form/src/main/java/org/dhis2/form/ui/provider/inputfield/SequentialInputProvider.kt index 2d51a480d0..a55a5837cc 100644 --- a/form/src/main/java/org/dhis2/form/ui/provider/inputfield/SequentialInputProvider.kt +++ b/form/src/main/java/org/dhis2/form/ui/provider/inputfield/SequentialInputProvider.kt @@ -16,11 +16,13 @@ import org.dhis2.form.extensions.supportingText import org.dhis2.form.model.FieldUiModel import org.dhis2.form.ui.intent.FormIntent import org.hisp.dhis.mobile.ui.designsystem.component.InputSequential +import org.hisp.dhis.mobile.ui.designsystem.component.InputStyle import org.hisp.dhis.mobile.ui.designsystem.component.internal.IconCardData @Composable internal fun ProvideSequentialInput( modifier: Modifier, + inputStyle: InputStyle, fieldUiModel: FieldUiModel, context: Context, intentHandler: (FormIntent) -> Unit, @@ -76,5 +78,6 @@ internal fun ProvideSequentialInput( legendData = fieldUiModel.legend(), isRequired = fieldUiModel.mandatory, modifier = modifier, + inputStyle = inputStyle, ) } diff --git a/form/src/main/java/org/dhis2/form/ui/provider/inputfield/SwitchProvider.kt b/form/src/main/java/org/dhis2/form/ui/provider/inputfield/SwitchProvider.kt index d1a878189a..3b902ec4c8 100644 --- a/form/src/main/java/org/dhis2/form/ui/provider/inputfield/SwitchProvider.kt +++ b/form/src/main/java/org/dhis2/form/ui/provider/inputfield/SwitchProvider.kt @@ -7,16 +7,19 @@ import org.dhis2.form.extensions.legend import org.dhis2.form.extensions.supportingText import org.dhis2.form.model.FieldUiModel import org.dhis2.form.ui.intent.FormIntent +import org.hisp.dhis.mobile.ui.designsystem.component.InputStyle import org.hisp.dhis.mobile.ui.designsystem.component.InputYesOnlySwitch @Composable internal fun ProvideYesOnlySwitchInput( modifier: Modifier, + inputStyle: InputStyle, fieldUiModel: FieldUiModel, intentHandler: (FormIntent) -> Unit, ) { InputYesOnlySwitch( modifier = modifier, + inputStyle = inputStyle, title = fieldUiModel.label, state = fieldUiModel.inputState(), supportingText = fieldUiModel.supportingText(), diff --git a/form/src/main/java/org/dhis2/form/ui/provider/inputfield/UnitIntervalInputProvider.kt b/form/src/main/java/org/dhis2/form/ui/provider/inputfield/UnitIntervalInputProvider.kt index eb7dfa93f8..b2e317aa41 100644 --- a/form/src/main/java/org/dhis2/form/ui/provider/inputfield/UnitIntervalInputProvider.kt +++ b/form/src/main/java/org/dhis2/form/ui/provider/inputfield/UnitIntervalInputProvider.kt @@ -14,11 +14,13 @@ import org.dhis2.form.extensions.legend import org.dhis2.form.extensions.supportingText import org.dhis2.form.model.FieldUiModel import org.dhis2.form.ui.intent.FormIntent +import org.hisp.dhis.mobile.ui.designsystem.component.InputStyle import org.hisp.dhis.mobile.ui.designsystem.component.InputUnitInterval @Composable fun ProvideUnitIntervalInput( modifier: Modifier, + inputStyle: InputStyle, fieldUiModel: FieldUiModel, intentHandler: (FormIntent) -> Unit, onNextClicked: () -> Unit, @@ -30,6 +32,7 @@ fun ProvideUnitIntervalInput( } InputUnitInterval( modifier = modifier.fillMaxWidth(), + inputStyle = inputStyle, title = fieldUiModel.label, state = fieldUiModel.inputState(), supportingText = fieldUiModel.supportingText(), From 43ab28a10e4442a18083f41802aff89499e29a6f Mon Sep 17 00:00:00 2001 From: andresmr Date: Mon, 12 Feb 2024 12:09:11 +0100 Subject: [PATCH 16/47] [ANDROAPP-5800] text range to textInput model --- .../searchTrackEntity/SearchTEIViewModel.kt | 44 ++++++++++--------- .../SearchParametersScreen.kt | 1 + .../provider/ParameterSelectorItemProvider.kt | 12 ++--- .../InputsForTextValueTypeProvider.kt | 3 +- 4 files changed, 30 insertions(+), 30 deletions(-) diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt index 1877c987da..74f471ab24 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt @@ -741,29 +741,33 @@ class SearchTEIViewModel( } } - fun onParameterIntent(formIntent: FormIntent) { - when (formIntent) { - is FormIntent.OnTextChange -> { - updateQuery( - formIntent.uid, - formIntent.value, - ) - } - is FormIntent.OnFocus -> { - /*val updatedItems = uiState.items.map { - if (it.uid == formIntent.uid) { - (it as FieldUiModelImpl).copy(focused = true) - } else { - it - } + fun onParameterIntent(formIntent: FormIntent) = when (formIntent) { + is FormIntent.OnTextChange -> { + updateQuery( + formIntent.uid, + formIntent.value, + ) + } + is FormIntent.OnFocus -> { + val updatedItems = uiState.items.map { + if (it.focused && it.uid != formIntent.uid) { + (it as FieldUiModelImpl).copy(focused = false) + } else if (it.uid == formIntent.uid) { + (it as FieldUiModelImpl).copy(focused = true) + } else { + it } - uiState = uiState.copy(items = updatedItems)*/ - } - - is FormIntent.ClearValue -> { } + uiState = uiState.copy(items = updatedItems) + } - else -> {} + is FormIntent.ClearValue -> { + updateQuery( + formIntent.uid, + null, + ) } + + else -> {} } } diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt index 3490ae5b2a..9a32f67377 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt @@ -127,6 +127,7 @@ fun SearchParametersScreen( ) }, ) { + focusManager.clearFocus() onClear() } } diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt index 5e860910e9..56146606e8 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt @@ -6,12 +6,11 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusDirection import androidx.compose.ui.focus.FocusManager import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.onFocusChanged -import androidx.compose.ui.text.TextRange -import androidx.compose.ui.text.input.TextFieldValue import org.dhis2.commons.resources.ResourceManager import org.dhis2.form.model.FieldUiModel import org.dhis2.form.ui.provider.inputfield.FieldProvider @@ -27,14 +26,9 @@ fun provideParameterSelectorItem( ): ParameterSelectorItemModel { val focusRequester = remember { FocusRequester() } - val textSelection = TextRange(fieldUiModel.value?.length ?: 0) - val value by remember(fieldUiModel.value) { - mutableStateOf(TextFieldValue(fieldUiModel.value ?: "", textSelection)) - } - var status by remember { mutableStateOf( - if (value.text.isEmpty()) { + if (fieldUiModel.value.isNullOrEmpty()) { ParameterSelectorItemModel.Status.CLOSED } else { ParameterSelectorItemModel.Status.UNFOCUSED @@ -64,7 +58,7 @@ fun provideParameterSelectorItem( intentHandler = callback::intent, resources = resources, focusManager = focusManager, - onNextClicked = { }, + onNextClicked = { focusManager.moveFocus(FocusDirection.Down) }, ) }, status = status, diff --git a/form/src/main/java/org/dhis2/form/ui/provider/inputfield/InputsForTextValueTypeProvider.kt b/form/src/main/java/org/dhis2/form/ui/provider/inputfield/InputsForTextValueTypeProvider.kt index b877909e6e..8b54a50bd4 100644 --- a/form/src/main/java/org/dhis2/form/ui/provider/inputfield/InputsForTextValueTypeProvider.kt +++ b/form/src/main/java/org/dhis2/form/ui/provider/inputfield/InputsForTextValueTypeProvider.kt @@ -142,8 +142,9 @@ private fun ProvideDefaultTextInput( focusManager: FocusManager, onNextClicked: () -> Unit, ) { + val textSelection = TextRange(fieldUiModel.value?.length ?: 0) var value by remember(fieldUiModel.value) { - mutableStateOf(TextFieldValue(fieldUiModel.value ?: "")) + mutableStateOf(TextFieldValue(fieldUiModel.value ?: "", textSelection)) } InputText( modifier = modifier.fillMaxWidth(), From 21ee9ed1ecbe233eb4e90e1a74dac598f070836f Mon Sep 17 00:00:00 2001 From: andresmr Date: Mon, 12 Feb 2024 13:57:24 +0100 Subject: [PATCH 17/47] [ANDROAPP-5800] update display name values --- .../searchTrackEntity/SearchTEIViewModel.kt | 21 +++++++++++++++++-- .../searchTrackEntity/SearchTEModule.java | 18 +++++++++++++--- .../SearchTeiViewModelFactory.kt | 3 +++ .../searchparameters/model/SearchParameter.kt | 11 ---------- .../form/ui/FieldViewModelFactoryImpl.kt | 3 +-- 5 files changed, 38 insertions(+), 18 deletions(-) delete mode 100644 app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParameter.kt diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt index 74f471ab24..2acff29514 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt @@ -26,6 +26,7 @@ import org.dhis2.commons.viewmodel.DispatcherProvider import org.dhis2.data.search.SearchParametersModel import org.dhis2.form.model.FieldUiModelImpl import org.dhis2.form.ui.intent.FormIntent +import org.dhis2.form.ui.provider.DisplayNameProvider import org.dhis2.maps.layer.basemaps.BaseMapStyle import org.dhis2.maps.usecases.MapStyleConfiguration import org.dhis2.usescases.searchTrackEntity.listView.SearchResult @@ -50,6 +51,7 @@ class SearchTEIViewModel( private val mapStyleConfig: MapStyleConfiguration, private val searchParametersRepository: SearchParametersRepository, private val resourceManager: ResourceManager, + private val displayNameProvider: DisplayNameProvider, ) : ViewModel() { private val _pageConfiguration = MutableLiveData() @@ -230,7 +232,14 @@ class SearchTEIViewModel( private fun updateSearchParameters(uid: String, value: String?) { val updatedItems = uiState.items.map { if (it.uid == uid) { - (it as FieldUiModelImpl).copy(value = value) + (it as FieldUiModelImpl).copy( + value = value, + displayName = displayNameProvider.provideDisplayName( + valueType = it.valueType, + value = value, + optionSet = it.optionSet, + ), + ) } else { it } @@ -247,7 +256,7 @@ class SearchTEIViewModel( private fun clearSearchParameters() { val updatedItems = uiState.items.map { - (it as FieldUiModelImpl).copy(value = null) + (it as FieldUiModelImpl).copy(value = null, displayName = null) } uiState = uiState.copy(items = updatedItems) } @@ -748,6 +757,14 @@ class SearchTEIViewModel( formIntent.value, ) } + + is FormIntent.OnSave -> { + updateQuery( + formIntent.uid, + formIntent.value, + ) + } + is FormIntent.OnFocus -> { val updatedItems = uiState.items.map { if (it.focused && it.uid != formIntent.uid) { diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEModule.java b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEModule.java index 435697699b..cea884988b 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEModule.java +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEModule.java @@ -36,6 +36,7 @@ import org.dhis2.form.ui.FieldViewModelFactoryImpl; import org.dhis2.form.ui.LayoutProviderImpl; import org.dhis2.form.ui.provider.AutoCompleteProviderImpl; +import org.dhis2.form.ui.provider.DisplayNameProvider; import org.dhis2.form.ui.provider.DisplayNameProviderImpl; import org.dhis2.form.ui.provider.HintProviderImpl; import org.dhis2.form.ui.provider.KeyboardActionProviderImpl; @@ -274,9 +275,9 @@ SearchTeiViewModelFactory providesViewModelFactory( MapDataRepository mapDataRepository, NetworkUtils networkUtils, D2 d2, - FilterRepository filterRepository, SearchParametersRepository searchParametersRepository, - ResourceManager resourceManager + ResourceManager resourceManager, + DisplayNameProvider displayNameProvider ) { return new SearchTeiViewModelFactory( searchRepository, @@ -289,7 +290,18 @@ SearchTeiViewModelFactory providesViewModelFactory( new SearchDispatchers(), new MapStyleConfiguration(d2), searchParametersRepository, - resourceManager + resourceManager, + displayNameProvider + ); + } + + @Provides + @PerActivity + DisplayNameProvider provideDisplayNameProvider(D2 d2) { + return new DisplayNameProviderImpl( + new OptionSetConfiguration(d2), + new OrgUnitConfiguration(d2), + new FileResourceConfiguration(d2) ); } diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTeiViewModelFactory.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTeiViewModelFactory.kt index 2e5deeb5da..bcbd8ff8e4 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTeiViewModelFactory.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTeiViewModelFactory.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.ViewModelProvider import org.dhis2.commons.network.NetworkUtils import org.dhis2.commons.resources.ResourceManager import org.dhis2.commons.viewmodel.DispatcherProvider +import org.dhis2.form.ui.provider.DisplayNameProvider import org.dhis2.maps.usecases.MapStyleConfiguration import org.dhis2.usescases.searchTrackEntity.searchparameters.SearchParametersRepository @@ -21,6 +22,7 @@ class SearchTeiViewModelFactory( private val mapStyleConfig: MapStyleConfiguration, private val searchParameterRepository: SearchParametersRepository, private val resourceManager: ResourceManager, + private val displayNameProvider: DisplayNameProvider, ) : ViewModelProvider.Factory { override fun create(modelClass: Class): T { return SearchTEIViewModel( @@ -35,6 +37,7 @@ class SearchTeiViewModelFactory( mapStyleConfig, searchParameterRepository, resourceManager, + displayNameProvider, ) as T } } diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParameter.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParameter.kt deleted file mode 100644 index 9b971dd61d..0000000000 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParameter.kt +++ /dev/null @@ -1,11 +0,0 @@ -package org.dhis2.usescases.searchTrackEntity.searchparameters.model - -import org.hisp.dhis.android.core.common.ValueType - -data class SearchParameter( - val uid: String, - val label: String, - val valueType: ValueType, - val helper: String = "Optional", - val value: String? = null, -) diff --git a/form/src/main/java/org/dhis2/form/ui/FieldViewModelFactoryImpl.kt b/form/src/main/java/org/dhis2/form/ui/FieldViewModelFactoryImpl.kt index 83eeb7daad..8b8ce80bc0 100644 --- a/form/src/main/java/org/dhis2/form/ui/FieldViewModelFactoryImpl.kt +++ b/form/src/main/java/org/dhis2/form/ui/FieldViewModelFactoryImpl.kt @@ -124,8 +124,7 @@ class FieldViewModelFactoryImpl( allowFutureDates = programTrackedEntityAttribute?.allowFutureDate() ?: true, editable = editable, renderingType = SectionRenderingType.LISTING, - description = programTrackedEntityAttribute?.displayDescription() - ?: trackedEntityAttribute.displayDescription(), + description = null, fieldRendering = programTrackedEntityAttribute?.renderType()?.mobile(), objectStyle = trackedEntityAttribute.style() ?: ObjectStyle.builder().build(), fieldMask = trackedEntityAttribute.fieldMask(), From 6c62fdc7a9c4b90ca1a6d600f1a4e0bb30fb0f8a Mon Sep 17 00:00:00 2001 From: andresmr Date: Tue, 13 Feb 2024 10:34:25 +0100 Subject: [PATCH 18/47] [ANDROAPP-5800] show org unit selector --- .../searchTrackEntity/SearchTEActivity.java | 28 +++++++++ .../SearchParametersScreen.kt | 57 +++++++++++++++---- 2 files changed, 73 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java index 1f3d95e304..befd2f9ef4 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java @@ -36,6 +36,8 @@ import org.dhis2.data.forms.dataentry.ProgramAdapter; import org.dhis2.databinding.ActivitySearchBinding; import org.dhis2.databinding.SnackbarMinAttrBinding; +import org.dhis2.form.ui.event.RecyclerViewUiEvents; +import org.dhis2.form.ui.intent.FormIntent; import org.dhis2.ui.ThemeManager; import org.dhis2.usescases.general.ActivityGlobalAbstract; import org.dhis2.usescases.searchTrackEntity.listView.SearchTEList; @@ -47,6 +49,7 @@ import org.dhis2.utils.granularsync.SyncStatusDialog; import org.dhis2.utils.granularsync.SyncStatusDialogNavigatorKt; import org.hisp.dhis.android.core.arch.call.D2Progress; +import org.hisp.dhis.android.core.common.ValueType; import org.hisp.dhis.android.core.organisationunit.OrganisationUnit; import java.io.Serializable; @@ -353,6 +356,31 @@ private void initSearchParameters() { initialProgram, tEType, resourceManager, + (uid, preselectedOrgUnits, orgUnitScope) -> { + new OUTreeFragment.Builder() + .showAsDialog() + .withPreselectedOrgUnits(preselectedOrgUnits) + .singleSelection() + .onSelection(selectedOrgUnits -> { + String selecteOrgUnit = null; + if(!selectedOrgUnits.isEmpty()) { + selecteOrgUnit = selectedOrgUnits.get(0).uid(); + } + viewModel.onParameterIntent( + new FormIntent.OnSave( + uid, + selecteOrgUnit, + ValueType.ORGANISATION_UNIT, + null + ) + ); + return Unit.INSTANCE; + }) + .orgUnitScope(orgUnitScope) + .build() + .show(getSupportFragmentManager(), "OUTreeFragment"); + return Unit.INSTANCE; + }, () -> { presenter.onClearClick(); return Unit.INSTANCE; diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt index 9a32f67377..b9dc6a1013 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt @@ -21,18 +21,23 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch +import org.dhis2.commons.orgunitselector.OrgUnitSelectorScope +import org.dhis2.commons.resources.ColorUtils import org.dhis2.commons.resources.ResourceManager import org.dhis2.form.model.FieldUiModel +import org.dhis2.form.model.FieldUiModelImpl import org.dhis2.form.ui.event.RecyclerViewUiEvents import org.dhis2.form.ui.intent.FormIntent import org.dhis2.usescases.searchTrackEntity.SearchTEIViewModel import org.dhis2.usescases.searchTrackEntity.searchparameters.model.SearchParametersUiState import org.dhis2.usescases.searchTrackEntity.searchparameters.provider.provideParameterSelectorItem +import org.hisp.dhis.android.core.common.ValueType import org.hisp.dhis.mobile.ui.designsystem.component.Button import org.hisp.dhis.mobile.ui.designsystem.component.ButtonStyle import org.hisp.dhis.mobile.ui.designsystem.component.parameter.ParameterSelectorItem @@ -44,6 +49,11 @@ fun SearchParametersScreen( resourceManager: ResourceManager, uiState: SearchParametersUiState, intentHandler: (FormIntent) -> Unit, + showOrgUnit: ( + uid: String, + preselectedOrgUnits: List, + orgUnitScope: OrgUnitSelectorScope, + ) -> Unit, onSearchClick: () -> Unit, onClear: () -> Unit, ) { @@ -58,7 +68,16 @@ fun SearchParametersScreen( } override fun recyclerViewUiEvents(uiEvent: RecyclerViewUiEvents) { -// uiEventHandler(uiEvent) + when (uiEvent) { + is RecyclerViewUiEvents.OpenOrgUnitDialog -> + showOrgUnit( + uiEvent.uid, + uiEvent.value?.let { listOf(it) } ?: emptyList(), + uiEvent.orgUnitSelectorScope ?: OrgUnitSelectorScope.UserSearchScope(), + ) + + else -> {} + } } } } @@ -148,25 +167,33 @@ fun SearchParametersScreen( @Preview(showBackground = true) @Composable fun SearchFormPreview() { - /*SearchParametersScreen( + SearchParametersScreen( + resourceManager = ResourceManager(LocalContext.current, ColorUtils()), uiState = SearchParametersUiState( items = listOf( - SearchParameter( - "uid1", - "Label 1", - ValueType.TEXT, + FieldUiModelImpl( + uid = "uid1", + layoutId = 1, + label = "Label 1", + autocompleteList = emptyList(), + optionSetConfiguration = null, + valueType = ValueType.TEXT, ), - SearchParameter( - "uid2", - "Label 2", - ValueType.TEXT, + FieldUiModelImpl( + uid = "uid2", + layoutId = 2, + label = "Label 2", + autocompleteList = emptyList(), + optionSetConfiguration = null, + valueType = ValueType.TEXT, ), ), ), - onValueChange = { _, _ -> }, + intentHandler = {}, + showOrgUnit = { _, _, _ -> }, onSearchClick = {}, onClear = {}, - )*/ + ) } fun initSearchScreen( @@ -175,6 +202,11 @@ fun initSearchScreen( program: String?, teiType: String, resources: ResourceManager, + showOrgUnit: ( + uid: String, + preselectedOrgUnits: List, + orgUnitScope: OrgUnitSelectorScope, + ) -> Unit, onClear: () -> Unit, ) { viewModel.fetchSearchParameters( @@ -187,6 +219,7 @@ fun initSearchScreen( uiState = viewModel.uiState, onSearchClick = viewModel::onSearchClick, intentHandler = viewModel::onParameterIntent, + showOrgUnit = showOrgUnit, onClear = { onClear() viewModel.clearQueryData() From 5b9e2abed344bbd821576e1684e26b407fc99e39 Mon Sep 17 00:00:00 2001 From: andresmr Date: Tue, 13 Feb 2024 12:00:40 +0100 Subject: [PATCH 19/47] [ANDROAPP-5800] remove SearchRepository.kt --- .../usescases/enrollment/EnrollmentModule.kt | 5 +- .../injection/EventDetailsModule.kt | 7 +- .../eventInitial/EventInitialModule.java | 5 +- .../searchTrackEntity/SearchTEModule.java | 5 +- .../SearchParametersRepository.kt | 45 +++++-- .../org/dhis2/form/data/SearchRepository.kt | 117 ------------------ .../main/java/org/dhis2/form/di/Injector.kt | 48 +------ .../dhis2/form/model/FormRepositoryRecords.kt | 14 +-- .../dhis2/form/ui/FieldViewModelFactory.kt | 8 -- .../form/ui/FieldViewModelFactoryImpl.kt | 39 +----- .../ui/style/FormUiModelColorFactoryImpl.kt | 29 +---- .../ui/style/LongTextUiColorFactoryImpl.kt | 29 +---- 12 files changed, 62 insertions(+), 289 deletions(-) delete mode 100644 form/src/main/java/org/dhis2/form/data/SearchRepository.kt diff --git a/app/src/main/java/org/dhis2/usescases/enrollment/EnrollmentModule.kt b/app/src/main/java/org/dhis2/usescases/enrollment/EnrollmentModule.kt index 0bc4ef3015..dcad2d5437 100644 --- a/app/src/main/java/org/dhis2/usescases/enrollment/EnrollmentModule.kt +++ b/app/src/main/java/org/dhis2/usescases/enrollment/EnrollmentModule.kt @@ -117,10 +117,9 @@ class EnrollmentModule( colorUtils: ColorUtils, ): FieldViewModelFactory { return FieldViewModelFactoryImpl( - false, UiStyleProviderImpl( - FormUiModelColorFactoryImpl(activityContext, true, colorUtils), - LongTextUiColorFactoryImpl(activityContext, true, colorUtils), + FormUiModelColorFactoryImpl(activityContext, colorUtils), + LongTextUiColorFactoryImpl(activityContext, colorUtils), true, ), LayoutProviderImpl(), diff --git a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventDetails/injection/EventDetailsModule.kt b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventDetails/injection/EventDetailsModule.kt index 898ab56413..fd6b8f9f0e 100644 --- a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventDetails/injection/EventDetailsModule.kt +++ b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventDetails/injection/EventDetailsModule.kt @@ -6,7 +6,6 @@ import dagger.Provides import org.dhis2.commons.data.EventCreationType import org.dhis2.commons.di.dagger.PerFragment import org.dhis2.commons.locationprovider.LocationProvider -import org.dhis2.commons.network.NetworkUtils import org.dhis2.commons.prefs.PreferenceProvider import org.dhis2.commons.prefs.PreferenceProviderImpl import org.dhis2.commons.resources.ColorUtils @@ -75,7 +74,6 @@ class EventDetailsModule( fun provideEventDetailsRepository( d2: D2, resourceManager: ResourceManager, - networkUtils: NetworkUtils, colorUtils: ColorUtils, ): EventDetailsRepository { return EventDetailsRepository( @@ -84,10 +82,9 @@ class EventDetailsModule( eventUid = eventUid, programStageUid = programStageUid, fieldFactory = FieldViewModelFactoryImpl( - false, UiStyleProviderImpl( - FormUiModelColorFactoryImpl(context, true, colorUtils), - LongTextUiColorFactoryImpl(context, true, colorUtils), + FormUiModelColorFactoryImpl(context, colorUtils), + LongTextUiColorFactoryImpl(context, colorUtils), true, ), LayoutProviderImpl(), diff --git a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventInitial/EventInitialModule.java b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventInitial/EventInitialModule.java index 038527c669..78fc55ffa8 100644 --- a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventInitial/EventInitialModule.java +++ b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventInitial/EventInitialModule.java @@ -90,10 +90,9 @@ EventFieldMapper provideFieldMapper(Context context, FieldViewModelFactory field @PerActivity FieldViewModelFactory fieldFactory(Context context, D2 d2, ResourceManager resourceManager, ColorUtils colorUtils) { return new FieldViewModelFactoryImpl( - false, new UiStyleProviderImpl( - new FormUiModelColorFactoryImpl(activityContext, true, colorUtils), - new LongTextUiColorFactoryImpl(activityContext, true, colorUtils), + new FormUiModelColorFactoryImpl(activityContext, colorUtils), + new LongTextUiColorFactoryImpl(activityContext, colorUtils), true ), new LayoutProviderImpl(), diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEModule.java b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEModule.java index cea884988b..f5a0daf241 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEModule.java +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEModule.java @@ -182,10 +182,9 @@ FieldViewModelFactory fieldViewModelFactory( ColorUtils colorUtils ) { return new FieldViewModelFactoryImpl( - true, new UiStyleProviderImpl( - new FormUiModelColorFactoryImpl(moduleContext, false, colorUtils), - new LongTextUiColorFactoryImpl(moduleContext, false, colorUtils), + new FormUiModelColorFactoryImpl(moduleContext, colorUtils), + new LongTextUiColorFactoryImpl(moduleContext, colorUtils), false ), new LayoutProviderImpl(), diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersRepository.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersRepository.kt index 8392dd8c19..db2012a35b 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersRepository.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersRepository.kt @@ -7,7 +7,11 @@ import org.dhis2.form.model.OptionSetConfiguration import org.dhis2.form.ui.FieldViewModelFactory import org.hisp.dhis.android.core.D2 import org.hisp.dhis.android.core.arch.repositories.scope.RepositoryScope +import org.hisp.dhis.android.core.common.ObjectStyle import org.hisp.dhis.android.core.common.ValueType +import org.hisp.dhis.android.core.program.ProgramTrackedEntityAttribute +import org.hisp.dhis.android.core.program.SectionRenderingType +import org.hisp.dhis.android.core.trackedentity.TrackedEntityAttribute class SearchParametersRepository( private val d2: D2, @@ -51,18 +55,16 @@ class SearchParametersRepository( .blockingGet() } } - fieldViewModelFactory.createForAttribute( + createField( trackedEntityAttribute = attribute, programTrackedEntityAttribute = programAttribute, - value = null, - editable = true, optionSetConfiguration = optionSetConfiguration, ) } }.filter { parameter -> parameter.valueType !== ValueType.IMAGE && - parameter.valueType !== ValueType.COORDINATE && - parameter.valueType !== ValueType.FILE_RESOURCE + parameter.valueType !== ValueType.COORDINATE && + parameter.valueType !== ValueType.FILE_RESOURCE } } @@ -90,18 +92,41 @@ class SearchParametersRepository( } } - fieldViewModelFactory.createForAttribute( + createField( trackedEntityAttribute = attribute, programTrackedEntityAttribute = null, - value = null, - editable = true, optionSetConfiguration = optionSetConfiguration, ) } }.filter { parameter -> parameter.valueType !== ValueType.IMAGE && - parameter.valueType !== ValueType.COORDINATE && - parameter.valueType !== ValueType.FILE_RESOURCE + parameter.valueType !== ValueType.COORDINATE && + parameter.valueType !== ValueType.FILE_RESOURCE } } + + private fun createField( + trackedEntityAttribute: TrackedEntityAttribute, + programTrackedEntityAttribute: ProgramTrackedEntityAttribute?, + optionSetConfiguration: OptionSetConfiguration?, + ): FieldUiModel { + return fieldViewModelFactory.create( + id = trackedEntityAttribute.uid(), + label = trackedEntityAttribute.displayFormName() ?: "", + valueType = trackedEntityAttribute.valueType()!!, + mandatory = false, + optionSet = trackedEntityAttribute.optionSet()?.uid(), + value = null, + programStageSection = null, + allowFutureDates = programTrackedEntityAttribute?.allowFutureDate() ?: true, + editable = true, + renderingType = SectionRenderingType.LISTING, + description = null, + fieldRendering = programTrackedEntityAttribute?.renderType()?.mobile(), + objectStyle = trackedEntityAttribute.style() ?: ObjectStyle.builder().build(), + fieldMask = trackedEntityAttribute.fieldMask(), + optionSetConfiguration = optionSetConfiguration, + featureType = null, + ) + } } diff --git a/form/src/main/java/org/dhis2/form/data/SearchRepository.kt b/form/src/main/java/org/dhis2/form/data/SearchRepository.kt deleted file mode 100644 index 3296fddb82..0000000000 --- a/form/src/main/java/org/dhis2/form/data/SearchRepository.kt +++ /dev/null @@ -1,117 +0,0 @@ -package org.dhis2.form.data - -import io.reactivex.Flowable -import org.dhis2.form.data.metadata.FormBaseConfiguration -import org.dhis2.form.model.FieldUiModel -import org.dhis2.form.model.OptionSetConfiguration -import org.dhis2.form.ui.FieldViewModelFactory -import org.hisp.dhis.android.core.D2 -import org.hisp.dhis.android.core.arch.repositories.scope.RepositoryScope -import org.hisp.dhis.android.core.common.ValueType - -class SearchRepository( - private val d2: D2, - private val fieldViewModelFactory: FieldViewModelFactory, - override val programUid: String?, - private val teiTypeUid: String, - private val currentSearchValues: Map, -) : DataEntryBaseRepository(FormBaseConfiguration(d2), fieldViewModelFactory) { - - override fun list(): Flowable> { - return programUid?.let { - programTrackedEntityAttributes() - } ?: trackedEntitySearchFields() - } - - override fun sectionUids(): Flowable> { - return Flowable.just(mutableListOf()) - } - - override fun isEvent(): Boolean { - return false - } - - private fun trackedEntitySearchFields(): Flowable> { - return Flowable.fromCallable { - val teTypeAttributes = d2.trackedEntityModule().trackedEntityTypeAttributes() - .byTrackedEntityTypeUid().eq(teiTypeUid) - .bySearchable().isTrue - .blockingGet() - - return@fromCallable teTypeAttributes.mapNotNull { typeAttribute -> - d2.trackedEntityModule().trackedEntityAttributes() - .uid(typeAttribute.trackedEntityAttribute()!!.uid()) - .blockingGet()?.let { attribute -> - - val optionSetConfiguration = attribute.optionSet()?.let { - OptionSetConfiguration.config( - d2.optionModule().options() - .byOptionSetUid().eq(attribute.optionSet()!!.uid()) - .blockingCount(), - ) { - d2.optionModule().options() - .byOptionSetUid().eq(attribute.optionSet()!!.uid()) - .orderBySortOrder(RepositoryScope.OrderByDirection.ASC) - .blockingGet() - } - } - - fieldViewModelFactory.createForAttribute( - attribute, - null, - currentSearchValues[attribute.uid()], - true, - optionSetConfiguration, - ) - } - }.filter { item: FieldUiModel -> - item.valueType !== ValueType.IMAGE && - item.valueType !== ValueType.COORDINATE - } - } - } - - private fun programTrackedEntityAttributes(): Flowable> { - return Flowable.fromCallable { - val searchableAttributes = d2.programModule().programTrackedEntityAttributes() - .withRenderType() - .byProgram().eq(programUid) - .blockingGet().filter { programAttribute -> - val isSearchable = programAttribute.searchable()!! - val isUnique = d2.trackedEntityModule().trackedEntityAttributes() - .uid(programAttribute.trackedEntityAttribute()!!.uid()) - .blockingGet()?.unique() === java.lang.Boolean.TRUE - isSearchable || isUnique - } - searchableAttributes.mapNotNull { programAttribute -> - d2.trackedEntityModule().trackedEntityAttributes() - .uid(programAttribute.trackedEntityAttribute()!!.uid()) - .blockingGet()?.let { attribute -> - - val optionSetConfiguration = attribute.optionSet()?.let { - OptionSetConfiguration.config( - d2.optionModule().options() - .byOptionSetUid().eq(attribute.optionSet()!!.uid()) - .blockingCount(), - ) { - d2.optionModule().options() - .byOptionSetUid().eq(attribute.optionSet()!!.uid()) - .orderBySortOrder(RepositoryScope.OrderByDirection.ASC) - .blockingGet() - } - } - fieldViewModelFactory.createForAttribute( - attribute, - programAttribute, - currentSearchValues[attribute.uid()], - true, - optionSetConfiguration, - ) - } - }.filter { item: FieldUiModel? -> - item!!.valueType !== ValueType.IMAGE && - item!!.valueType !== ValueType.COORDINATE - } - } - } -} diff --git a/form/src/main/java/org/dhis2/form/di/Injector.kt b/form/src/main/java/org/dhis2/form/di/Injector.kt index b2fec9542f..e30fa4950d 100644 --- a/form/src/main/java/org/dhis2/form/di/Injector.kt +++ b/form/src/main/java/org/dhis2/form/di/Injector.kt @@ -20,7 +20,6 @@ import org.dhis2.form.data.OptionsRepository import org.dhis2.form.data.RuleEngineRepository import org.dhis2.form.data.RulesUtilsProviderImpl import org.dhis2.form.data.SearchOptionSetOption -import org.dhis2.form.data.SearchRepository import org.dhis2.form.data.metadata.EnrollmentConfiguration import org.dhis2.form.data.metadata.FileResourceConfiguration import org.dhis2.form.data.metadata.OptionSetConfiguration @@ -28,7 +27,6 @@ import org.dhis2.form.data.metadata.OrgUnitConfiguration import org.dhis2.form.model.EnrollmentRecords import org.dhis2.form.model.EventRecords import org.dhis2.form.model.FormRepositoryRecords -import org.dhis2.form.model.SearchRecords import org.dhis2.form.model.coroutine.FormDispatcher import org.dhis2.form.ui.FieldViewModelFactory import org.dhis2.form.ui.FieldViewModelFactoryImpl @@ -119,45 +117,19 @@ object Injector { repositoryRecords as EnrollmentRecords, ) - EntryMode.DE -> provideEventRepository( + else -> provideEventRepository( context, repositoryRecords as EventRecords, ) - - else -> provideSearchRepository( - context, - repositoryRecords as SearchRecords, - ) } } - private fun provideSearchRepository( - context: Context, - searchRecords: SearchRecords, - ): DataEntryRepository { - return SearchRepository( - d2 = provideD2(), - fieldViewModelFactory = provideFieldFactory( - context, - searchRecords.allowMandatoryFields, - searchRecords.isBackgroundTransparent, - ), - programUid = searchRecords.programUid, - teiTypeUid = searchRecords.teiTypeUid, - currentSearchValues = searchRecords.currentSearchValues, - ) - } - private fun provideEnrollmentRepository( context: Context, enrollmentRecords: EnrollmentRecords, ): DataEntryRepository { return EnrollmentRepository( - fieldFactory = provideFieldFactory( - context, - enrollmentRecords.allowMandatoryFields, - enrollmentRecords.isBackgroundTransparent, - ), + fieldFactory = provideFieldFactory(context), conf = EnrollmentConfiguration(provideD2(), enrollmentRecords.enrollmentUid), enrollmentMode = enrollmentRecords.enrollmentMode, enrollmentFormLabelsProvider = provideEnrollmentFormLabelsProvider(context), @@ -169,11 +141,7 @@ object Injector { eventRecords: EventRecords, ): DataEntryRepository { return EventRepository( - fieldFactory = provideFieldFactory( - context, - eventRecords.allowMandatoryFields, - eventRecords.isBackgroundTransparent, - ), + fieldFactory = provideFieldFactory(context), eventUid = eventRecords.eventUid, d2 = provideD2(), ) @@ -184,11 +152,8 @@ object Injector { private fun provideFieldFactory( context: Context, - allowMandatoryFields: Boolean, - isBackgroundTransparent: Boolean, ): FieldViewModelFactory = FieldViewModelFactoryImpl( - noMandatoryFields = !allowMandatoryFields, - uiStyleProvider = provideUiStyleProvider(context, isBackgroundTransparent), + uiStyleProvider = provideUiStyleProvider(context), layoutProvider = provideLayoutProvider(), hintProvider = provideHintProvider(context), displayNameProvider = provideDisplayNameProvider(), @@ -208,19 +173,16 @@ object Injector { private fun provideUiStyleProvider( context: Context, - isBackgroundTransparent: Boolean, ): UiStyleProvider = UiStyleProviderImpl( colorFactory = FormUiModelColorFactoryImpl( context, - isBackgroundTransparent, provideColorUtils(), ), longTextColorFactory = LongTextUiColorFactoryImpl( context, - isBackgroundTransparent, provideColorUtils(), ), - actionIconClickable = isBackgroundTransparent, + actionIconClickable = true, ) private fun provideFormValueStore( diff --git a/form/src/main/java/org/dhis2/form/model/FormRepositoryRecords.kt b/form/src/main/java/org/dhis2/form/model/FormRepositoryRecords.kt index 9a82235d99..ad8cab5b2b 100644 --- a/form/src/main/java/org/dhis2/form/model/FormRepositoryRecords.kt +++ b/form/src/main/java/org/dhis2/form/model/FormRepositoryRecords.kt @@ -4,9 +4,7 @@ import org.dhis2.commons.data.EntryMode sealed class FormRepositoryRecords( val recordUid: String?, - val entryMode: EntryMode? = null, - val allowMandatoryFields: Boolean = true, - val isBackgroundTransparent: Boolean = true, + val entryMode: EntryMode, ) : java.io.Serializable class EnrollmentRecords( @@ -23,13 +21,3 @@ class EventRecords( recordUid = eventUid, entryMode = EntryMode.DE, ) - -class SearchRecords( - val programUid: String?, - val teiTypeUid: String, - val currentSearchValues: Map, -) : FormRepositoryRecords( - recordUid = programUid, - allowMandatoryFields = false, - isBackgroundTransparent = false, -) diff --git a/form/src/main/java/org/dhis2/form/ui/FieldViewModelFactory.kt b/form/src/main/java/org/dhis2/form/ui/FieldViewModelFactory.kt index a6563353e7..2e5462ed86 100644 --- a/form/src/main/java/org/dhis2/form/ui/FieldViewModelFactory.kt +++ b/form/src/main/java/org/dhis2/form/ui/FieldViewModelFactory.kt @@ -33,14 +33,6 @@ interface FieldViewModelFactory { orgUnitSelectorScope: OrgUnitSelectorScope? = null, ): FieldUiModel - fun createForAttribute( - trackedEntityAttribute: TrackedEntityAttribute, - programTrackedEntityAttribute: ProgramTrackedEntityAttribute?, - value: String?, - editable: Boolean, - optionSetConfiguration: OptionSetConfiguration?, - ): FieldUiModel - fun createSingleSection(singleSectionName: String): FieldUiModel fun createSection( diff --git a/form/src/main/java/org/dhis2/form/ui/FieldViewModelFactoryImpl.kt b/form/src/main/java/org/dhis2/form/ui/FieldViewModelFactoryImpl.kt index 8b8ce80bc0..aefb3f6239 100644 --- a/form/src/main/java/org/dhis2/form/ui/FieldViewModelFactoryImpl.kt +++ b/form/src/main/java/org/dhis2/form/ui/FieldViewModelFactoryImpl.kt @@ -20,12 +20,9 @@ import org.hisp.dhis.android.core.common.FeatureType import org.hisp.dhis.android.core.common.ObjectStyle import org.hisp.dhis.android.core.common.ValueType import org.hisp.dhis.android.core.common.ValueTypeDeviceRendering -import org.hisp.dhis.android.core.program.ProgramTrackedEntityAttribute import org.hisp.dhis.android.core.program.SectionRenderingType -import org.hisp.dhis.android.core.trackedentity.TrackedEntityAttribute class FieldViewModelFactoryImpl( - private val noMandatoryFields: Boolean, private val uiStyleProvider: UiStyleProvider, private val layoutProvider: LayoutProvider, private val hintProvider: HintProvider, @@ -57,9 +54,7 @@ class FieldViewModelFactoryImpl( autoCompleteList: List?, orgUnitSelectorScope: OrgUnitSelectorScope?, ): FieldUiModel { - var isMandatory = mandatory isNull(valueType, "type must be supplied") - if (noMandatoryFields) isMandatory = false return FieldUiModelImpl( uid = id, layoutId = layoutProvider.getLayoutByType( @@ -73,7 +68,7 @@ class FieldViewModelFactoryImpl( error = null, editable = editable, warning = null, - mandatory = isMandatory, + mandatory = mandatory, label = label, programStageSection = programStageSection, style = uiStyleProvider.provideStyle(valueType), @@ -105,38 +100,6 @@ class FieldViewModelFactoryImpl( ) } - override fun createForAttribute( - trackedEntityAttribute: TrackedEntityAttribute, - programTrackedEntityAttribute: ProgramTrackedEntityAttribute?, - value: String?, - editable: Boolean, - optionSetConfiguration: OptionSetConfiguration?, - ): FieldUiModel { - isNull(trackedEntityAttribute.valueType(), "type must be supplied") - return create( - id = trackedEntityAttribute.uid(), - label = trackedEntityAttribute.displayFormName() ?: "", - valueType = trackedEntityAttribute.valueType()!!, - mandatory = programTrackedEntityAttribute?.mandatory() == true, - optionSet = trackedEntityAttribute.optionSet()?.uid(), - value = value, - programStageSection = null, - allowFutureDates = programTrackedEntityAttribute?.allowFutureDate() ?: true, - editable = editable, - renderingType = SectionRenderingType.LISTING, - description = null, - fieldRendering = programTrackedEntityAttribute?.renderType()?.mobile(), - objectStyle = trackedEntityAttribute.style() ?: ObjectStyle.builder().build(), - fieldMask = trackedEntityAttribute.fieldMask(), - optionSetConfiguration = optionSetConfiguration, - featureType = if (trackedEntityAttribute.valueType() === ValueType.COORDINATE) { - FeatureType.POINT - } else { - null - }, - ) - } - override fun createSingleSection(singleSectionName: String): FieldUiModel { return SectionUiModelImpl( SectionUiModelImpl.SINGLE_SECTION_UID, diff --git a/form/src/main/java/org/dhis2/form/ui/style/FormUiModelColorFactoryImpl.kt b/form/src/main/java/org/dhis2/form/ui/style/FormUiModelColorFactoryImpl.kt index 72782ebc58..5f7e8dbdc0 100644 --- a/form/src/main/java/org/dhis2/form/ui/style/FormUiModelColorFactoryImpl.kt +++ b/form/src/main/java/org/dhis2/form/ui/style/FormUiModelColorFactoryImpl.kt @@ -8,39 +8,22 @@ import org.dhis2.form.R class FormUiModelColorFactoryImpl( val context: Context, - val isBackgroundTransparent: Boolean = false, val colorUtils: ColorUtils, ) : FormUiColorFactory { override fun getBasicColors(): Map { - if (isBackgroundTransparent) { - return mapOf( - FormUiColorType.PRIMARY to - colorUtils.getPrimaryColor(context, ColorType.PRIMARY), - FormUiColorType.TEXT_PRIMARY to - ContextCompat.getColor(context, R.color.textPrimary), - FormUiColorType.FIELD_LABEL_TEXT to - ContextCompat.getColor(context, R.color.text_black_A63), - FormUiColorType.WARNING to - ContextCompat.getColor(context, R.color.warning_color), - FormUiColorType.ERROR to - ContextCompat.getColor(context, R.color.error_color), - FormUiColorType.ACTION_ICON to - ContextCompat.getColor(context, R.color.colorGreyDefault), - ) - } return mapOf( FormUiColorType.PRIMARY to - colorUtils.getPrimaryColor(context, ColorType.ACCENT), + colorUtils.getPrimaryColor(context, ColorType.PRIMARY), FormUiColorType.TEXT_PRIMARY to - colorUtils.getPrimaryColor(context, ColorType.ACCENT), + ContextCompat.getColor(context, R.color.textPrimary), FormUiColorType.FIELD_LABEL_TEXT to - colorUtils.getPrimaryColor(context, ColorType.ACCENT), + ContextCompat.getColor(context, R.color.text_black_A63), FormUiColorType.WARNING to - ContextCompat.getColor(context, R.color.warning_color), + ContextCompat.getColor(context, R.color.warning_color), FormUiColorType.ERROR to - ContextCompat.getColor(context, R.color.error_color), + ContextCompat.getColor(context, R.color.error_color), FormUiColorType.ACTION_ICON to - colorUtils.getPrimaryColor(context, ColorType.ACCENT), + ContextCompat.getColor(context, R.color.colorGreyDefault), ) } } diff --git a/form/src/main/java/org/dhis2/form/ui/style/LongTextUiColorFactoryImpl.kt b/form/src/main/java/org/dhis2/form/ui/style/LongTextUiColorFactoryImpl.kt index 3eaf5d67b9..05a525e852 100644 --- a/form/src/main/java/org/dhis2/form/ui/style/LongTextUiColorFactoryImpl.kt +++ b/form/src/main/java/org/dhis2/form/ui/style/LongTextUiColorFactoryImpl.kt @@ -8,40 +8,23 @@ import org.dhis2.form.R class LongTextUiColorFactoryImpl( val context: Context, - val isBackgroundTransparent: Boolean, val colorUtils: ColorUtils, ) : FormUiColorFactory { override fun getBasicColors(): Map { - if (isBackgroundTransparent) { - return mapOf( - FormUiColorType.PRIMARY to - colorUtils.getPrimaryColor(context, ColorType.PRIMARY), - FormUiColorType.TEXT_PRIMARY to - ContextCompat.getColor(context, R.color.textPrimary), - FormUiColorType.FIELD_LABEL_TEXT to - ContextCompat.getColor(context, R.color.text_black_A63), - FormUiColorType.WARNING to - ContextCompat.getColor(context, R.color.warning_color), - FormUiColorType.ERROR to - ContextCompat.getColor(context, R.color.error_color), - FormUiColorType.ACTION_ICON to - ContextCompat.getColor(context, R.color.colorGreyDefault), - ) - } return mapOf( FormUiColorType.PRIMARY to - colorUtils.getPrimaryColor(context, ColorType.PRIMARY), + colorUtils.getPrimaryColor(context, ColorType.PRIMARY), FormUiColorType.TEXT_PRIMARY to - ContextCompat.getColor(context, R.color.textPrimary), + ContextCompat.getColor(context, R.color.textPrimary), FormUiColorType.FIELD_LABEL_TEXT to - colorUtils.getPrimaryColor(context, ColorType.PRIMARY), + ContextCompat.getColor(context, R.color.text_black_A63), FormUiColorType.WARNING to - ContextCompat.getColor(context, R.color.warning_color), + ContextCompat.getColor(context, R.color.warning_color), FormUiColorType.ERROR to - ContextCompat.getColor(context, R.color.error_color), + ContextCompat.getColor(context, R.color.error_color), FormUiColorType.ACTION_ICON to - colorUtils.getPrimaryColor(context, ColorType.ACCENT), + ContextCompat.getColor(context, R.color.colorGreyDefault), ) } } From 440e7d9adea59cca73ad26432bdc03394da99bce Mon Sep 17 00:00:00 2001 From: andresmr Date: Tue, 13 Feb 2024 14:35:27 +0100 Subject: [PATCH 20/47] [ANDROAPP-5800] fix tests --- .../SearchParametersRepository.kt | 8 +-- .../SearchTEIViewModelTest.kt | 64 +++++++++++-------- .../dhis2/form/ui/FieldViewModelFactory.kt | 2 - .../ui/style/FormUiModelColorFactoryImpl.kt | 12 ++-- .../ui/style/LongTextUiColorFactoryImpl.kt | 12 ++-- .../data/FormRepositoryIntegrationTest.kt | 1 - .../form/data/RulesUtilsProviderImplTest.kt | 1 - .../form/ui/FieldViewModelFactoryImplTest.kt | 29 ++++++--- 8 files changed, 73 insertions(+), 56 deletions(-) diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersRepository.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersRepository.kt index db2012a35b..eb6eb4db65 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersRepository.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersRepository.kt @@ -63,8 +63,8 @@ class SearchParametersRepository( } }.filter { parameter -> parameter.valueType !== ValueType.IMAGE && - parameter.valueType !== ValueType.COORDINATE && - parameter.valueType !== ValueType.FILE_RESOURCE + parameter.valueType !== ValueType.COORDINATE && + parameter.valueType !== ValueType.FILE_RESOURCE } } @@ -100,8 +100,8 @@ class SearchParametersRepository( } }.filter { parameter -> parameter.valueType !== ValueType.IMAGE && - parameter.valueType !== ValueType.COORDINATE && - parameter.valueType !== ValueType.FILE_RESOURCE + parameter.valueType !== ValueType.COORDINATE && + parameter.valueType !== ValueType.FILE_RESOURCE } } diff --git a/app/src/test/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModelTest.kt b/app/src/test/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModelTest.kt index be5250af38..2409149871 100644 --- a/app/src/test/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModelTest.kt +++ b/app/src/test/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModelTest.kt @@ -1,21 +1,26 @@ package org.dhis2.usescases.searchTrackEntity import androidx.arch.core.executor.testing.InstantTaskExecutorRule +import app.cash.turbine.test import com.mapbox.geojson.BoundingBox import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.resetMain +import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.setMain import org.dhis2.commons.network.NetworkUtils +import org.dhis2.commons.resources.ResourceManager import org.dhis2.commons.viewmodel.DispatcherProvider import org.dhis2.data.search.SearchParametersModel -import org.dhis2.form.model.ActionType -import org.dhis2.form.model.RowAction +import org.dhis2.form.ui.intent.FormIntent +import org.dhis2.form.ui.provider.DisplayNameProvider import org.dhis2.maps.geometry.mapper.EventsByProgramStage import org.dhis2.maps.usecases.MapStyleConfiguration import org.dhis2.usescases.searchTrackEntity.listView.SearchResult.SearchResultType +import org.dhis2.usescases.searchTrackEntity.searchparameters.SearchParametersRepository +import org.hisp.dhis.android.core.common.ValueType import org.hisp.dhis.android.core.program.Program import org.hisp.dhis.android.core.trackedentity.TrackedEntityType import org.junit.After @@ -43,6 +48,9 @@ class SearchTEIViewModelTest { private val mapDataRepository: MapDataRepository = mock() private val networkUtils: NetworkUtils = mock() private val mapStyleConfiguration: MapStyleConfiguration = mock() + private val searchParametersRepository: SearchParametersRepository = mock() + private val resourceManager: ResourceManager = mock() + private val displayNameProvider: DisplayNameProvider = mock() @ExperimentalCoroutinesApi private val testingDispatcher = StandardTestDispatcher() @@ -78,6 +86,9 @@ class SearchTEIViewModelTest { } }, mapStyleConfiguration, + searchParametersRepository = searchParametersRepository, + resourceManager = resourceManager, + displayNameProvider = displayNameProvider, ) testingDispatcher.scheduler.advanceUntilIdle() } @@ -169,11 +180,11 @@ class SearchTEIViewModelTest { @Test fun `Should update query data`() { - viewModel.updateQueryData( - RowAction( - id = "testingUid", + viewModel.onParameterIntent( + FormIntent.OnSave( + uid = "testingUid", value = "testingValue", - type = ActionType.ON_SAVE, + valueType = ValueType.TEXT, ), ) @@ -257,10 +268,11 @@ class SearchTEIViewModelTest { } @Test - fun `Should use callback to perform min attributes warning`() { - setCurrentProgram(testingProgram()) - viewModel.onSearchClick { - assertTrue(true) + fun `Should use callback to perform min attributes warning`() = runTest { + setCurrentProgram(testingProgram(displayFrontPageList = false)) + viewModel.onSearchClick() + viewModel.uiState.shouldShowMinAttributeWarning.test { + assertTrue(awaitItem()) } } @@ -269,16 +281,14 @@ class SearchTEIViewModelTest { setCurrentProgram(testingProgram()) viewModel.setListScreen() viewModel.setSearchScreen() - viewModel.updateQueryData( - RowAction( - id = "testingUid", + viewModel.onParameterIntent( + FormIntent.OnSave( + uid = "testingUid", value = "testingValue", - type = ActionType.ON_SAVE, + valueType = ValueType.TEXT, ), ) - viewModel.onSearchClick { - assertTrue(false) - } + viewModel.onSearchClick() assertTrue(viewModel.refreshData.value != null) } @@ -307,16 +317,14 @@ class SearchTEIViewModelTest { setCurrentProgram(testingProgram()) viewModel.setMapScreen() viewModel.setSearchScreen() - viewModel.updateQueryData( - RowAction( - id = "testingUid", + viewModel.onParameterIntent( + FormIntent.OnSave( + uid = "testingUid", value = "testingValue", - type = ActionType.ON_SAVE, + valueType = ValueType.TEXT, ), ) - viewModel.onSearchClick { - assertTrue(false) - } + viewModel.onSearchClick() testingDispatcher.scheduler.advanceUntilIdle() @@ -649,11 +657,11 @@ class SearchTEIViewModelTest { @ExperimentalCoroutinesApi private fun performSearch() { - viewModel.updateQueryData( - RowAction( - id = "testingUid", + viewModel.onParameterIntent( + FormIntent.OnSave( + uid = "testingUid", value = "testingValue", - type = ActionType.ON_SAVE, + valueType = ValueType.TEXT, ), ) viewModel.setListScreen() diff --git a/form/src/main/java/org/dhis2/form/ui/FieldViewModelFactory.kt b/form/src/main/java/org/dhis2/form/ui/FieldViewModelFactory.kt index 2e5462ed86..083a9e8a89 100644 --- a/form/src/main/java/org/dhis2/form/ui/FieldViewModelFactory.kt +++ b/form/src/main/java/org/dhis2/form/ui/FieldViewModelFactory.kt @@ -7,9 +7,7 @@ import org.hisp.dhis.android.core.common.FeatureType import org.hisp.dhis.android.core.common.ObjectStyle import org.hisp.dhis.android.core.common.ValueType import org.hisp.dhis.android.core.common.ValueTypeDeviceRendering -import org.hisp.dhis.android.core.program.ProgramTrackedEntityAttribute import org.hisp.dhis.android.core.program.SectionRenderingType -import org.hisp.dhis.android.core.trackedentity.TrackedEntityAttribute interface FieldViewModelFactory { fun create( diff --git a/form/src/main/java/org/dhis2/form/ui/style/FormUiModelColorFactoryImpl.kt b/form/src/main/java/org/dhis2/form/ui/style/FormUiModelColorFactoryImpl.kt index 5f7e8dbdc0..b658fdc872 100644 --- a/form/src/main/java/org/dhis2/form/ui/style/FormUiModelColorFactoryImpl.kt +++ b/form/src/main/java/org/dhis2/form/ui/style/FormUiModelColorFactoryImpl.kt @@ -13,17 +13,17 @@ class FormUiModelColorFactoryImpl( override fun getBasicColors(): Map { return mapOf( FormUiColorType.PRIMARY to - colorUtils.getPrimaryColor(context, ColorType.PRIMARY), + colorUtils.getPrimaryColor(context, ColorType.PRIMARY), FormUiColorType.TEXT_PRIMARY to - ContextCompat.getColor(context, R.color.textPrimary), + ContextCompat.getColor(context, R.color.textPrimary), FormUiColorType.FIELD_LABEL_TEXT to - ContextCompat.getColor(context, R.color.text_black_A63), + ContextCompat.getColor(context, R.color.text_black_A63), FormUiColorType.WARNING to - ContextCompat.getColor(context, R.color.warning_color), + ContextCompat.getColor(context, R.color.warning_color), FormUiColorType.ERROR to - ContextCompat.getColor(context, R.color.error_color), + ContextCompat.getColor(context, R.color.error_color), FormUiColorType.ACTION_ICON to - ContextCompat.getColor(context, R.color.colorGreyDefault), + ContextCompat.getColor(context, R.color.colorGreyDefault), ) } } diff --git a/form/src/main/java/org/dhis2/form/ui/style/LongTextUiColorFactoryImpl.kt b/form/src/main/java/org/dhis2/form/ui/style/LongTextUiColorFactoryImpl.kt index 05a525e852..39964342f9 100644 --- a/form/src/main/java/org/dhis2/form/ui/style/LongTextUiColorFactoryImpl.kt +++ b/form/src/main/java/org/dhis2/form/ui/style/LongTextUiColorFactoryImpl.kt @@ -14,17 +14,17 @@ class LongTextUiColorFactoryImpl( override fun getBasicColors(): Map { return mapOf( FormUiColorType.PRIMARY to - colorUtils.getPrimaryColor(context, ColorType.PRIMARY), + colorUtils.getPrimaryColor(context, ColorType.PRIMARY), FormUiColorType.TEXT_PRIMARY to - ContextCompat.getColor(context, R.color.textPrimary), + ContextCompat.getColor(context, R.color.textPrimary), FormUiColorType.FIELD_LABEL_TEXT to - ContextCompat.getColor(context, R.color.text_black_A63), + ContextCompat.getColor(context, R.color.text_black_A63), FormUiColorType.WARNING to - ContextCompat.getColor(context, R.color.warning_color), + ContextCompat.getColor(context, R.color.warning_color), FormUiColorType.ERROR to - ContextCompat.getColor(context, R.color.error_color), + ContextCompat.getColor(context, R.color.error_color), FormUiColorType.ACTION_ICON to - ContextCompat.getColor(context, R.color.colorGreyDefault), + ContextCompat.getColor(context, R.color.colorGreyDefault), ) } } diff --git a/form/src/test/java/org/dhis2/form/data/FormRepositoryIntegrationTest.kt b/form/src/test/java/org/dhis2/form/data/FormRepositoryIntegrationTest.kt index ebc64ee083..2dfa5ebe41 100644 --- a/form/src/test/java/org/dhis2/form/data/FormRepositoryIntegrationTest.kt +++ b/form/src/test/java/org/dhis2/form/data/FormRepositoryIntegrationTest.kt @@ -161,7 +161,6 @@ class FormRepositoryIntegrationTest { val autoCompleteProvider: AutoCompleteProvider = mock() val fieldFactory = FieldViewModelFactoryImpl( - false, styleProvider, layoutProvider, hintProvider, diff --git a/form/src/test/java/org/dhis2/form/data/RulesUtilsProviderImplTest.kt b/form/src/test/java/org/dhis2/form/data/RulesUtilsProviderImplTest.kt index 7cd4d02224..c16c018c84 100644 --- a/form/src/test/java/org/dhis2/form/data/RulesUtilsProviderImplTest.kt +++ b/form/src/test/java/org/dhis2/form/data/RulesUtilsProviderImplTest.kt @@ -80,7 +80,6 @@ class RulesUtilsProviderImplTest { fun setUp() { ruleUtils = RulesUtilsProviderImpl(d2, optionsRepository) fieldFactory = FieldViewModelFactoryImpl( - false, uiStyleProvider, layoutProvider, hintProvider, diff --git a/form/src/test/java/org/dhis2/form/ui/FieldViewModelFactoryImplTest.kt b/form/src/test/java/org/dhis2/form/ui/FieldViewModelFactoryImplTest.kt index e9cd40695b..f012d362a4 100644 --- a/form/src/test/java/org/dhis2/form/ui/FieldViewModelFactoryImplTest.kt +++ b/form/src/test/java/org/dhis2/form/ui/FieldViewModelFactoryImplTest.kt @@ -8,8 +8,10 @@ import org.dhis2.form.ui.provider.LayoutProvider import org.dhis2.form.ui.provider.LegendValueProvider import org.dhis2.form.ui.provider.UiEventTypesProvider import org.dhis2.form.ui.provider.UiStyleProvider +import org.hisp.dhis.android.core.common.ObjectStyle import org.hisp.dhis.android.core.common.ValueType import org.hisp.dhis.android.core.program.ProgramTrackedEntityAttribute +import org.hisp.dhis.android.core.program.SectionRenderingType import org.hisp.dhis.android.core.trackedentity.TrackedEntityAttribute import org.junit.Before import org.junit.Test @@ -21,7 +23,6 @@ import org.mockito.kotlin.verify class FieldViewModelFactoryImplTest { private val valueTypeHintMap = HashMap() - private val searchMode = true private lateinit var fieldViewModelFactoryImpl: FieldViewModelFactoryImpl private val programTrackedEntityAttribute: ProgramTrackedEntityAttribute = mock() private val uiStyleProvider: UiStyleProvider = mock() @@ -42,7 +43,6 @@ class FieldViewModelFactoryImplTest { fun setUp() { valueTypeHintMap[ValueType.TEXT] = "Enter text" fieldViewModelFactoryImpl = FieldViewModelFactoryImpl( - searchMode, uiStyleProvider, layoutProvider, hintProvider, @@ -56,12 +56,25 @@ class FieldViewModelFactoryImplTest { @Test fun `should display trackedEntityInstanceAttribute as name rather than program attribute`() { - fieldViewModelFactoryImpl.createForAttribute( - trackedEntityAttribute, - programTrackedEntityAttribute, - "Peter", - true, - null, + fieldViewModelFactoryImpl.create( + id = trackedEntityAttribute.uid(), + label = trackedEntityAttribute.displayFormName() ?: "", + valueType = trackedEntityAttribute.valueType()!!, + mandatory = false, + optionSet = trackedEntityAttribute.optionSet()?.uid(), + value = null, + programStageSection = null, + allowFutureDates = programTrackedEntityAttribute.allowFutureDate() ?: true, + editable = true, + renderingType = SectionRenderingType.LISTING, + description = null, + fieldRendering = programTrackedEntityAttribute.renderType()?.mobile(), + objectStyle = trackedEntityAttribute.style() ?: ObjectStyle.builder().build(), + fieldMask = trackedEntityAttribute.fieldMask(), + optionSetConfiguration = null, + featureType = null, + autoCompleteList = null, + orgUnitSelectorScope = null, ) verify(trackedEntityAttribute).displayFormName() verify(programTrackedEntityAttribute, never()).displayName() From 464b2be8a57fa6685b3e85020d41f964873242f3 Mon Sep 17 00:00:00 2001 From: andresmr Date: Tue, 13 Feb 2024 16:00:56 +0100 Subject: [PATCH 21/47] [ANDROAPP-5800] fix tests --- .../flow/searchFlow/SearchFlowTest.kt | 6 ++- .../usescases/flow/syncFlow/SyncFlowTest.kt | 2 +- .../usescases/flow/teiFlow/TeiFlowRobot.kt | 4 +- .../usescases/flow/teiFlow/TeiFlowTest.kt | 2 +- .../java/org/dhis2/usescases/form/FormTest.kt | 5 +-- .../dhis2/usescases/searchte/SearchTETest.kt | 14 +++---- .../searchte/robot/SearchTeiRobot.kt | 5 ++- .../TeiDashboardTestNoComposable.kt | 10 +++-- .../SearchParametersScreen.kt | 4 +- .../provider/inputfield/DateProviderTest.kt | 37 +++++++++++++------ 10 files changed, 57 insertions(+), 32 deletions(-) diff --git a/app/src/androidTest/java/org/dhis2/usescases/flow/searchFlow/SearchFlowTest.kt b/app/src/androidTest/java/org/dhis2/usescases/flow/searchFlow/SearchFlowTest.kt index 45730cfcb7..5e82eddb97 100644 --- a/app/src/androidTest/java/org/dhis2/usescases/flow/searchFlow/SearchFlowTest.kt +++ b/app/src/androidTest/java/org/dhis2/usescases/flow/searchFlow/SearchFlowTest.kt @@ -3,6 +3,7 @@ package org.dhis2.usescases.flow.searchFlow import android.content.Intent import androidx.compose.ui.text.capitalize import androidx.compose.ui.text.intl.Locale +import androidx.compose.ui.test.junit4.createComposeRule import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.rule.ActivityTestRule import org.dhis2.R @@ -21,6 +22,9 @@ class SearchFlowTest : BaseTest() { @get:Rule val rule = ActivityTestRule(SearchTEActivity::class.java, false, false) + @get:Rule + val composeTestRule = createComposeRule() + private val dateRegistration = createFirstSpecificDate() private val dateEnrollment = createEnrollmentDate() @@ -35,7 +39,7 @@ class SearchFlowTest : BaseTest() { prepareWomanProgrammeIntentAndLaunchActivity(rule) teiFlowRobot { - registerTEI(registerTEIDetails) + registerTEI(registerTEIDetails, composeTestRule) pressBack() } diff --git a/app/src/androidTest/java/org/dhis2/usescases/flow/syncFlow/SyncFlowTest.kt b/app/src/androidTest/java/org/dhis2/usescases/flow/syncFlow/SyncFlowTest.kt index c1e199892b..f92dda2703 100644 --- a/app/src/androidTest/java/org/dhis2/usescases/flow/syncFlow/SyncFlowTest.kt +++ b/app/src/androidTest/java/org/dhis2/usescases/flow/syncFlow/SyncFlowTest.kt @@ -62,7 +62,7 @@ class SyncFlowTest : BaseTest() { clickOnOpenSearch() typeAttributeAtPosition(teiName, 0) typeAttributeAtPosition(teiLastName, 1) - clickOnSearch() + clickOnSearch(composeTestRule) clickOnTEI(teiName, teiLastName) } diff --git a/app/src/androidTest/java/org/dhis2/usescases/flow/teiFlow/TeiFlowRobot.kt b/app/src/androidTest/java/org/dhis2/usescases/flow/teiFlow/TeiFlowRobot.kt index f4a7ddc5c7..8f4e1b2549 100644 --- a/app/src/androidTest/java/org/dhis2/usescases/flow/teiFlow/TeiFlowRobot.kt +++ b/app/src/androidTest/java/org/dhis2/usescases/flow/teiFlow/TeiFlowRobot.kt @@ -19,7 +19,7 @@ fun teiFlowRobot(teiFlowRobot: TeiFlowRobot.() -> Unit) { class TeiFlowRobot : BaseRobot() { - fun registerTEI(registrationModel: RegisterTEIUIModel) { + fun registerTEI(registrationModel: RegisterTEIUIModel, composeTestRule: ComposeContentTestRule) { val registrationDate = registrationModel.firstSpecificDate val enrollmentDate = registrationModel.enrollmentDate @@ -29,7 +29,7 @@ class TeiFlowRobot : BaseRobot() { clickOnDateField() selectSpecificDate(registrationDate.year, registrationDate.month, registrationDate.day) acceptDate() - clickOnSearch() + clickOnSearch(composeTestRule) clickOnEnroll() selectSpecificDate(enrollmentDate.year, enrollmentDate.month, enrollmentDate.day) acceptDate() diff --git a/app/src/androidTest/java/org/dhis2/usescases/flow/teiFlow/TeiFlowTest.kt b/app/src/androidTest/java/org/dhis2/usescases/flow/teiFlow/TeiFlowTest.kt index 14e3efdb69..83ec08eb93 100644 --- a/app/src/androidTest/java/org/dhis2/usescases/flow/teiFlow/TeiFlowTest.kt +++ b/app/src/androidTest/java/org/dhis2/usescases/flow/teiFlow/TeiFlowTest.kt @@ -45,7 +45,7 @@ class TeiFlowTest: BaseTest() { prepareWomanProgrammeIntentAndLaunchActivity(ruleSearch) teiFlowRobot { - registerTEI(registerTeiDetails) + registerTEI(registerTeiDetails, composeTestRule) closeEnrollmentAndCheckEvents(composeTestRule,totalEventsPerEnrollment) enrollToProgram(composeTestRule, ADULT_WOMAN_PROGRAM) checkActiveAndPastEnrollmentDetails(enrollmentListDetails) diff --git a/app/src/androidTest/java/org/dhis2/usescases/form/FormTest.kt b/app/src/androidTest/java/org/dhis2/usescases/form/FormTest.kt index 0047e6f375..20e1e26a55 100644 --- a/app/src/androidTest/java/org/dhis2/usescases/form/FormTest.kt +++ b/app/src/androidTest/java/org/dhis2/usescases/form/FormTest.kt @@ -11,7 +11,6 @@ import org.dhis2.usescases.searchte.robot.searchTeiRobot import org.dhis2.usescases.teiDashboard.TeiDashboardMobileActivity import org.dhis2.usescases.teidashboard.robot.enrollmentRobot import org.dhis2.usescases.teidashboard.robot.eventRobot -import org.dhis2.usescases.teidashboard.robot.teiDashboardRobot import org.junit.After import org.junit.Ignore import org.junit.Rule @@ -244,7 +243,7 @@ class FormTest : BaseTest() { searchTeiRobot { clickOnOpenSearch() typeAttributeAtPosition("optionGroup", 1) - clickOnSearch() + clickOnSearch(composeTestRule) clickOnEnroll() orgUnitSelectorRobot(composeTestRule) { selectTreeOrgUnit("Ngelehun CHC") @@ -288,7 +287,7 @@ class FormTest : BaseTest() { searchTeiRobot { clickOnOpenSearch() typeAttributeAtPosition("abc", 1) - clickOnSearch() + clickOnSearch(composeTestRule) clickOnEnroll() orgUnitSelectorRobot(composeTestRule) { selectTreeOrgUnit("Ngelehun CHC") diff --git a/app/src/androidTest/java/org/dhis2/usescases/searchte/SearchTETest.kt b/app/src/androidTest/java/org/dhis2/usescases/searchte/SearchTETest.kt index b21921ed97..3b5a28634f 100644 --- a/app/src/androidTest/java/org/dhis2/usescases/searchte/SearchTETest.kt +++ b/app/src/androidTest/java/org/dhis2/usescases/searchte/SearchTETest.kt @@ -68,7 +68,7 @@ class SearchTETest : BaseTest() { searchTeiRobot { clickOnOpenSearch() typeAttributeAtPosition(firstName, firstNamePosition) - clickOnSearch() + clickOnSearch(composeTestRule) checkListOfSearchTEI(firstName, orgUnit) } } @@ -83,7 +83,7 @@ class SearchTETest : BaseTest() { searchTeiRobot { clickOnOpenSearch() typeAttributeAtPosition(firstName, firstNamePosition) - clickOnSearch() + clickOnSearch(composeTestRule) checkNoSearchResult() } } @@ -101,7 +101,7 @@ class SearchTETest : BaseTest() { clickOnOpenSearch() typeAttributeAtPosition(firstName, firstNamePosition) typeAttributeAtPosition(lastName, lastNamePosition) - clickOnSearch() + clickOnSearch(composeTestRule) checkListOfSearchTEI(firstName, lastName) } } @@ -136,7 +136,7 @@ class SearchTETest : BaseTest() { clickOnDateField() selectSpecificDate(birthdaySearch.year, birthdaySearch.month, birthdaySearch.day) acceptDate() - clickOnSearch() + clickOnSearch(composeTestRule) checkFieldsFromDisplayList( composeTestRule, displayInListData, @@ -179,7 +179,7 @@ class SearchTETest : BaseTest() { prepareTestAdultWomanProgrammeIntentAndLaunchActivity(rule) teiFlowRobot { - registerTEI(registerTeiDetails) + registerTEI(registerTeiDetails, composeTestRule) changeDueDate(overdueDate, programStage, orgUnit, composeTestRule) pressBack() composeTestRule.onNodeWithTag(SECONDARY_BUTTON_TAG).performClick() @@ -286,7 +286,7 @@ class SearchTETest : BaseTest() { clickOnOpenSearch() typeAttributeAtPosition(teiName, firstNamePosition) typeAttributeAtPosition(teiLastName, lastNamePosition) - clickOnSearch() + clickOnSearch(composeTestRule) clickOnTEI(teiName, teiLastName) } @@ -322,7 +322,7 @@ class SearchTETest : BaseTest() { searchTeiRobot { clickOnOpenSearch() typeAttributeAtPosition(name, namePosition) - clickOnSearch() + clickOnSearch(composeTestRule) } filterRobot { diff --git a/app/src/androidTest/java/org/dhis2/usescases/searchte/robot/SearchTeiRobot.kt b/app/src/androidTest/java/org/dhis2/usescases/searchte/robot/SearchTeiRobot.kt index 343db8ad21..5bb8fadb89 100644 --- a/app/src/androidTest/java/org/dhis2/usescases/searchte/robot/SearchTeiRobot.kt +++ b/app/src/androidTest/java/org/dhis2/usescases/searchte/robot/SearchTeiRobot.kt @@ -2,6 +2,7 @@ package org.dhis2.usescases.searchte.robot import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.junit4.ComposeContentTestRule +import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.recyclerview.widget.RecyclerView @@ -130,9 +131,9 @@ class SearchTeiRobot : BaseRobot() { onView(withId(R.id.acceptBtn)).perform(click()) } - fun clickOnSearch() { + fun clickOnSearch(composeTestRule: ComposeContentTestRule) { closeKeyboard() - onView(withId(R.id.searchButton)).perform(click()) + composeTestRule.onNodeWithTag("SEARCH_BUTTON").performClick() } fun checkListOfSearchTEI(firstSearchWord: String, secondSearchWord: String) { diff --git a/app/src/androidTest/java/org/dhis2/usescases/teidashboard/TeiDashboardTestNoComposable.kt b/app/src/androidTest/java/org/dhis2/usescases/teidashboard/TeiDashboardTestNoComposable.kt index eb75346c45..aa305567a9 100644 --- a/app/src/androidTest/java/org/dhis2/usescases/teidashboard/TeiDashboardTestNoComposable.kt +++ b/app/src/androidTest/java/org/dhis2/usescases/teidashboard/TeiDashboardTestNoComposable.kt @@ -1,5 +1,6 @@ package org.dhis2.usescases.teidashboard +import androidx.compose.ui.test.junit4.createComposeRule import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.rule.ActivityTestRule import org.dhis2.usescases.BaseTest @@ -18,6 +19,9 @@ class TeiDashboardTestNoComposable : BaseTest() { @get:Rule val ruleSearch = ActivityTestRule(SearchTEActivity::class.java, false, false) + @get:Rule + val composeTestRule = createComposeRule() + @Ignore @Test fun shouldSuccessfullyCreateRelationshipWhenClickAdd() { @@ -49,7 +53,7 @@ class TeiDashboardTestNoComposable : BaseTest() { clickOnOpenSearch() typeAttributeAtPosition(relationshipName, 0) typeAttributeAtPosition(relationshipLastName, 1) - clickOnSearch() + clickOnSearch(composeTestRule) waitToDebounce(5000) clickOnTEI(relationshipName, relationshipLastName) } @@ -73,7 +77,7 @@ class TeiDashboardTestNoComposable : BaseTest() { clickOnOpenSearch() typeAttributeAtPosition(teiName, firstNamePosition) typeAttributeAtPosition(teiLastName, lastNamePosition) - clickOnSearch() + clickOnSearch(composeTestRule) clickOnTEI(teiName, teiLastName) //scrollToTEIandClick() } @@ -102,7 +106,7 @@ class TeiDashboardTestNoComposable : BaseTest() { clickOnOpenSearch() typeAttributeAtPosition(teiName, firstNamePosition) typeAttributeAtPosition(teiLastName, lastNamePosition) - clickOnSearch() + clickOnSearch(composeTestRule) // waitToDebounce(400) clickOnTEI(teiName, teiLastName) } diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt index b9dc6a1013..530bc82c82 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt @@ -23,6 +23,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.platform.testTag import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import kotlinx.coroutines.flow.collectLatest @@ -154,7 +155,8 @@ fun SearchParametersScreen( Button( modifier = Modifier .fillMaxWidth() - .padding(16.dp, 8.dp, 16.dp, 8.dp), + .padding(16.dp, 8.dp, 16.dp, 8.dp) + .testTag("SEARCH_BUTTON"), style = ButtonStyle.FILLED, text = "Search", ) { diff --git a/form/src/androidTest/kotlin/org/dhis2/form/ui/provider/inputfield/DateProviderTest.kt b/form/src/androidTest/kotlin/org/dhis2/form/ui/provider/inputfield/DateProviderTest.kt index 2576ed2979..adedc34d10 100644 --- a/form/src/androidTest/kotlin/org/dhis2/form/ui/provider/inputfield/DateProviderTest.kt +++ b/form/src/androidTest/kotlin/org/dhis2/form/ui/provider/inputfield/DateProviderTest.kt @@ -4,10 +4,11 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.testTag import androidx.compose.ui.test.assertContentDescriptionEquals import androidx.compose.ui.test.assertIsDisplayed -import org.junit.Rule import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithTag import org.hisp.dhis.android.core.common.ValueType +import org.hisp.dhis.mobile.ui.designsystem.component.InputStyle +import org.junit.Rule import org.junit.Test @@ -35,56 +36,70 @@ class DateProviderTest { @Test fun shouldParseADateValueCorrectlyAndDisplayInput() { - val dateValueTypeFieldUiModel = generateFieldUiModel(FIELD_UI_MODEL_UID,DATE_VALUE,DATE_VALUE, ValueType.DATE) + val dateValueTypeFieldUiModel = + generateFieldUiModel(FIELD_UI_MODEL_UID, DATE_VALUE, DATE_VALUE, ValueType.DATE) composeTestRule.setContent { ProvideInputDate( + inputStyle = InputStyle.DataInputStyle(), modifier = Modifier.testTag(INPUT_DATE_TEST_TAG), fieldUiModel = dateValueTypeFieldUiModel, intentHandler = {}, - uiEventHandler = {} , + uiEventHandler = {}, onNextClicked = {}, ) } composeTestRule.onNodeWithTag(INPUT_DATE_TEST_TAG).assertIsDisplayed() - composeTestRule.onNodeWithTag(INPUT_DATE_TEST_TAG).assertContentDescriptionEquals(FORMATTED_DATE_VALUE) + composeTestRule.onNodeWithTag(INPUT_DATE_TEST_TAG) + .assertContentDescriptionEquals(FORMATTED_DATE_VALUE) } @Test fun shouldParseADateTimeValueCorrectlyAndDisplayInput() { - val dateValueTypeFieldUiModel = generateFieldUiModel(FIELD_UI_MODEL_UID,DATE_TIME_VALUE,DATE_TIME_VALUE, ValueType.DATETIME) + val dateValueTypeFieldUiModel = generateFieldUiModel( + FIELD_UI_MODEL_UID, + DATE_TIME_VALUE, + DATE_TIME_VALUE, + ValueType.DATETIME + ) composeTestRule.setContent { ProvideInputDate( + inputStyle = InputStyle.DataInputStyle(), modifier = Modifier.testTag(INPUT_DATE_TEST_TAG), fieldUiModel = dateValueTypeFieldUiModel, intentHandler = {}, - uiEventHandler = {} , + uiEventHandler = {}, onNextClicked = {}, ) } composeTestRule.onNodeWithTag(INPUT_DATE_TEST_TAG).assertIsDisplayed() - composeTestRule.onNodeWithTag(INPUT_DATE_TEST_TAG).assertContentDescriptionEquals(FORMATTED_DATE_TIME_VALUE) + composeTestRule.onNodeWithTag(INPUT_DATE_TEST_TAG) + .assertContentDescriptionEquals(FORMATTED_DATE_TIME_VALUE) } @Test fun shouldParseATimeValueCorrectlyAndDisplayInput() { - val dateValueTypeFieldUiModel = generateFieldUiModel(FIELD_UI_MODEL_UID, - TIME_VALUE,TIME_VALUE, ValueType.TIME) + val dateValueTypeFieldUiModel = generateFieldUiModel( + FIELD_UI_MODEL_UID, + TIME_VALUE, TIME_VALUE, ValueType.TIME + ) composeTestRule.setContent { ProvideInputDate( modifier = Modifier.testTag(INPUT_DATE_TEST_TAG), + inputStyle = InputStyle.DataInputStyle(), fieldUiModel = dateValueTypeFieldUiModel, intentHandler = {}, - uiEventHandler = {} , + uiEventHandler = {}, onNextClicked = {}, ) } composeTestRule.onNodeWithTag(INPUT_DATE_TEST_TAG).assertIsDisplayed() - composeTestRule.onNodeWithTag(INPUT_DATE_TEST_TAG).assertContentDescriptionEquals(FORMATTED_TIME_VALUE) + composeTestRule.onNodeWithTag(INPUT_DATE_TEST_TAG) + .assertContentDescriptionEquals(FORMATTED_TIME_VALUE) } } \ No newline at end of file From 7c23a5e882d70c454378080d4d64f1167f7babe5 Mon Sep 17 00:00:00 2001 From: andresmr Date: Wed, 14 Feb 2024 15:38:21 +0100 Subject: [PATCH 22/47] [ANDROAPP-5800] Add composeTestRule to functional tests --- .../flow/searchFlow/SearchFlowRobot.kt | 1 - .../usescases/flow/syncFlow/SyncFlowTest.kt | 10 ++- .../usescases/flow/teiFlow/TeiFlowRobot.kt | 20 +++-- .../java/org/dhis2/usescases/form/FormTest.kt | 11 +-- .../dhis2/usescases/searchte/SearchTETest.kt | 73 +++++++++---------- .../searchte/robot/SearchTeiRobot.kt | 48 ++++++++++-- .../TeiDashboardTestNoComposable.kt | 18 ++--- .../SearchParametersScreen.kt | 6 ++ .../provider/ParameterSelectorItemProvider.kt | 26 ++----- 9 files changed, 122 insertions(+), 91 deletions(-) diff --git a/app/src/androidTest/java/org/dhis2/usescases/flow/searchFlow/SearchFlowRobot.kt b/app/src/androidTest/java/org/dhis2/usescases/flow/searchFlow/SearchFlowRobot.kt index f4d9832ca8..da09cd4963 100644 --- a/app/src/androidTest/java/org/dhis2/usescases/flow/searchFlow/SearchFlowRobot.kt +++ b/app/src/androidTest/java/org/dhis2/usescases/flow/searchFlow/SearchFlowRobot.kt @@ -2,7 +2,6 @@ package org.dhis2.usescases.flow.searchFlow import org.dhis2.common.BaseRobot import org.dhis2.usescases.searchte.robot.filterRobot -import org.dhis2.usescases.searchte.robot.searchTeiRobot fun searchFlowRobot(searchFlowRobot: SearchFlowRobot.() -> Unit) { SearchFlowRobot().apply { diff --git a/app/src/androidTest/java/org/dhis2/usescases/flow/syncFlow/SyncFlowTest.kt b/app/src/androidTest/java/org/dhis2/usescases/flow/syncFlow/SyncFlowTest.kt index f92dda2703..53a98e2f3e 100644 --- a/app/src/androidTest/java/org/dhis2/usescases/flow/syncFlow/SyncFlowTest.kt +++ b/app/src/androidTest/java/org/dhis2/usescases/flow/syncFlow/SyncFlowTest.kt @@ -58,11 +58,13 @@ class SyncFlowTest : BaseTest() { prepareTBProgrammeIntentAndLaunchActivity(ruleSearch) - searchTeiRobot { + searchTeiRobot(composeTestRule) { clickOnOpenSearch() - typeAttributeAtPosition(teiName, 0) - typeAttributeAtPosition(teiLastName, 1) - clickOnSearch(composeTestRule) + openNextSearchParameter("First name") + typeOnNextSearchTextParameter(teiName) + openNextSearchParameter("Last name") + typeOnNextSearchTextParameter(teiLastName) + clickOnSearch() clickOnTEI(teiName, teiLastName) } diff --git a/app/src/androidTest/java/org/dhis2/usescases/flow/teiFlow/TeiFlowRobot.kt b/app/src/androidTest/java/org/dhis2/usescases/flow/teiFlow/TeiFlowRobot.kt index 8f4e1b2549..79c3515157 100644 --- a/app/src/androidTest/java/org/dhis2/usescases/flow/teiFlow/TeiFlowRobot.kt +++ b/app/src/androidTest/java/org/dhis2/usescases/flow/teiFlow/TeiFlowRobot.kt @@ -19,17 +19,21 @@ fun teiFlowRobot(teiFlowRobot: TeiFlowRobot.() -> Unit) { class TeiFlowRobot : BaseRobot() { - fun registerTEI(registrationModel: RegisterTEIUIModel, composeTestRule: ComposeContentTestRule) { + fun registerTEI( + registrationModel: RegisterTEIUIModel, + composeTestRule: ComposeContentTestRule + ) { val registrationDate = registrationModel.firstSpecificDate val enrollmentDate = registrationModel.enrollmentDate - searchTeiRobot { - typeAttributeAtPosition(registrationModel.name, 0) - typeAttributeAtPosition(registrationModel.lastName, 1) - clickOnDateField() - selectSpecificDate(registrationDate.year, registrationDate.month, registrationDate.day) - acceptDate() - clickOnSearch(composeTestRule) + searchTeiRobot(composeTestRule) { + openNextSearchParameter("First name") + typeOnNextSearchTextParameter(registrationModel.name) + openNextSearchParameter("Last name") + typeOnNextSearchTextParameter(registrationModel.lastName) + openNextSearchParameter("Date of birth") + typeOnDateParameter("${registrationDate.day}0${registrationDate.month}${registrationDate.year}") + clickOnSearch() clickOnEnroll() selectSpecificDate(enrollmentDate.year, enrollmentDate.month, enrollmentDate.day) acceptDate() diff --git a/app/src/androidTest/java/org/dhis2/usescases/form/FormTest.kt b/app/src/androidTest/java/org/dhis2/usescases/form/FormTest.kt index 20e1e26a55..453fa42d28 100644 --- a/app/src/androidTest/java/org/dhis2/usescases/form/FormTest.kt +++ b/app/src/androidTest/java/org/dhis2/usescases/form/FormTest.kt @@ -240,10 +240,10 @@ class FormTest : BaseTest() { val firstSectionPosition = 1 startSearchActivity(ruleSearch) - searchTeiRobot { + searchTeiRobot(composeTestRule) { clickOnOpenSearch() typeAttributeAtPosition("optionGroup", 1) - clickOnSearch(composeTestRule) + clickOnSearch() clickOnEnroll() orgUnitSelectorRobot(composeTestRule) { selectTreeOrgUnit("Ngelehun CHC") @@ -284,10 +284,11 @@ class FormTest : BaseTest() { startSearchActivity(ruleSearch) - searchTeiRobot { + searchTeiRobot(composeTestRule) { clickOnOpenSearch() - typeAttributeAtPosition("abc", 1) - clickOnSearch(composeTestRule) + openNextSearchParameter("First name") + typeOnNextSearchTextParameter("abc") + clickOnSearch() clickOnEnroll() orgUnitSelectorRobot(composeTestRule) { selectTreeOrgUnit("Ngelehun CHC") diff --git a/app/src/androidTest/java/org/dhis2/usescases/searchte/SearchTETest.kt b/app/src/androidTest/java/org/dhis2/usescases/searchte/SearchTETest.kt index 3b5a28634f..7be06c8d2d 100644 --- a/app/src/androidTest/java/org/dhis2/usescases/searchte/SearchTETest.kt +++ b/app/src/androidTest/java/org/dhis2/usescases/searchte/SearchTETest.kt @@ -60,15 +60,15 @@ class SearchTETest : BaseTest() { @Test fun shouldSuccessfullySearchByName() { val firstName = "Tim" - val firstNamePosition = 0 val orgUnit = "Ngelehun CHC" prepareChildProgrammeIntentAndLaunchActivity(rule) - searchTeiRobot { + searchTeiRobot(composeTestRule) { clickOnOpenSearch() - typeAttributeAtPosition(firstName, firstNamePosition) - clickOnSearch(composeTestRule) + openNextSearchParameter("First name") + typeOnNextSearchTextParameter(firstName) + clickOnSearch() checkListOfSearchTEI(firstName, orgUnit) } } @@ -76,14 +76,14 @@ class SearchTETest : BaseTest() { @Test fun shouldShowErrorWhenCanNotFindSearchResult() { val firstName = "asdssds" - val firstNamePosition = 1 prepareTestProgramRulesProgrammeIntentAndLaunchActivity(rule) - searchTeiRobot { + searchTeiRobot(composeTestRule) { clickOnOpenSearch() - typeAttributeAtPosition(firstName, firstNamePosition) - clickOnSearch(composeTestRule) + openNextSearchParameter("First name") + typeOnNextSearchTextParameter(firstName) + clickOnSearch() checkNoSearchResult() } } @@ -91,17 +91,17 @@ class SearchTETest : BaseTest() { @Test fun shouldSuccessfullySearchUsingMoreThanOneField() { val firstName = "Anna" - val firstNamePosition = 0 val lastName = "Jones" - val lastNamePosition = 1 prepareChildProgrammeIntentAndLaunchActivity(rule) - searchTeiRobot { + searchTeiRobot(composeTestRule) { clickOnOpenSearch() - typeAttributeAtPosition(firstName, firstNamePosition) - typeAttributeAtPosition(lastName, lastNamePosition) - clickOnSearch(composeTestRule) + openNextSearchParameter("First name") + typeOnNextSearchTextParameter(firstName) + openNextSearchParameter("Last name") + typeOnNextSearchTextParameter(lastName) + clickOnSearch() checkListOfSearchTEI(firstName, lastName) } } @@ -113,7 +113,7 @@ class SearchTETest : BaseTest() { prepareChildProgrammeIntentAndLaunchActivity(rule) - searchTeiRobot { + searchTeiRobot(composeTestRule) { clickOnProgramSpinner() selectAProgram(tbProgram) checkProgramHasChanged(tbProgram) @@ -122,23 +122,20 @@ class SearchTETest : BaseTest() { @Test fun shouldCheckDisplayInList() { - val birthdaySearch = createDateOfBirthSearch() val displayInListData = createDisplayListFields() - val namePosition = 0 - val lastNamePosition = 1 setDatePicker() prepareTestAdultWomanProgrammeIntentAndLaunchActivity(rule) - searchTeiRobot { - typeAttributeAtPosition(displayInListData.name, namePosition) - typeAttributeAtPosition(displayInListData.lastName, lastNamePosition) - clickOnDateField() - selectSpecificDate(birthdaySearch.year, birthdaySearch.month, birthdaySearch.day) - acceptDate() - clickOnSearch(composeTestRule) + searchTeiRobot(composeTestRule) { + openNextSearchParameter("First name") + typeOnNextSearchTextParameter(displayInListData.name) + openNextSearchParameter("Last name") + typeOnNextSearchTextParameter(displayInListData.lastName) + openNextSearchParameter("Date of birth") + typeOnDateParameter("01012001") + clickOnSearch() checkFieldsFromDisplayList( - composeTestRule, displayInListData, ) } @@ -276,17 +273,17 @@ class SearchTETest : BaseTest() { fun shouldSuccessfullyFilterBySync() { val teiName = "Frank" val teiLastName = "Fjordsen" - val firstNamePosition = 0 - val lastNamePosition = 1 val syncFilter = context.getString(R.string.action_sync) val totalCount = "1" prepareChildProgrammeIntentAndLaunchActivity(rule) - searchTeiRobot { + searchTeiRobot(composeTestRule) { clickOnOpenSearch() - typeAttributeAtPosition(teiName, firstNamePosition) - typeAttributeAtPosition(teiLastName, lastNamePosition) - clickOnSearch(composeTestRule) + openNextSearchParameter("First name") + typeOnNextSearchTextParameter(teiName) + openNextSearchParameter("Last name") + typeOnNextSearchTextParameter(teiLastName) + clickOnSearch() clickOnTEI(teiName, teiLastName) } @@ -311,7 +308,6 @@ class SearchTETest : BaseTest() { fun shouldSuccessfullySearchAndFilter() { val name = "Anna" val lastName = "Jones" - val namePosition = 0 val enrollmentStatus = context.getString(R.string.filters_title_enrollment_status) .format(context.resources.getQuantityString(R.plurals.enrollment, 1).capitalize(Locale.current)) val totalCount = "2" @@ -319,10 +315,11 @@ class SearchTETest : BaseTest() { prepareChildProgrammeIntentAndLaunchActivity(rule) - searchTeiRobot { + searchTeiRobot(composeTestRule) { clickOnOpenSearch() - typeAttributeAtPosition(name, namePosition) - clickOnSearch(composeTestRule) + openNextSearchParameter("First name") + typeOnNextSearchTextParameter(name) + clickOnSearch() } filterRobot { @@ -336,7 +333,7 @@ class SearchTETest : BaseTest() { checkTEIsAreOpen() } - searchTeiRobot { + searchTeiRobot(composeTestRule) { checkListOfSearchTEI(name, lastName) } } @@ -347,7 +344,7 @@ class SearchTETest : BaseTest() { prepareChildProgrammeIntentAndLaunchActivity(rule) - searchTeiRobot { + searchTeiRobot(composeTestRule) { clickOnShowMap() try { val device = UiDevice.getInstance(getInstrumentation()) diff --git a/app/src/androidTest/java/org/dhis2/usescases/searchte/robot/SearchTeiRobot.kt b/app/src/androidTest/java/org/dhis2/usescases/searchte/robot/SearchTeiRobot.kt index 5bb8fadb89..1d32e58326 100644 --- a/app/src/androidTest/java/org/dhis2/usescases/searchte/robot/SearchTeiRobot.kt +++ b/app/src/androidTest/java/org/dhis2/usescases/searchte/robot/SearchTeiRobot.kt @@ -1,10 +1,22 @@ package org.dhis2.usescases.searchte.robot +import androidx.compose.ui.test.assertAny import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.filter +import androidx.compose.ui.test.hasClickAction +import androidx.compose.ui.test.hasParent +import androidx.compose.ui.test.hasTestTag +import androidx.compose.ui.test.hasText import androidx.compose.ui.test.junit4.ComposeContentTestRule +import androidx.compose.ui.test.onAllNodesWithTag +import androidx.compose.ui.test.onAllNodesWithText +import androidx.compose.ui.test.onChildren +import androidx.compose.ui.test.onFirst +import androidx.compose.ui.test.onLast import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.performTextInput import androidx.recyclerview.widget.RecyclerView import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions.click @@ -15,6 +27,7 @@ import androidx.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition import androidx.test.espresso.contrib.RecyclerViewActions.scrollTo import androidx.test.espresso.contrib.RecyclerViewActions.scrollToPosition import androidx.test.espresso.matcher.ViewMatchers.hasDescendant +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withText import org.dhis2.R @@ -35,13 +48,16 @@ import org.hisp.dhis.mobile.ui.designsystem.component.ListCard import org.hisp.dhis.mobile.ui.designsystem.component.ListCardTitleModel -fun searchTeiRobot(searchTeiRobot: SearchTeiRobot.() -> Unit) { - SearchTeiRobot().apply { +fun searchTeiRobot( + composeTestRule: ComposeContentTestRule, + searchTeiRobot: SearchTeiRobot.() -> Unit +) { + SearchTeiRobot(composeTestRule).apply { searchTeiRobot() } } -class SearchTeiRobot : BaseRobot() { +class SearchTeiRobot(val composeTestRule: ComposeContentTestRule) : BaseRobot() { fun closeSearchForm() { waitToDebounce(2500) @@ -88,6 +104,23 @@ class SearchTeiRobot : BaseRobot() { ) } + fun openNextSearchParameter(parameterValue: String) { +// composeTestRule.onAllNodesWithTag("SEARCH_PARAM_ITEM").onFirst().performClick() + composeTestRule.onNodeWithText(parameterValue).performClick() + } + + fun typeOnNextSearchTextParameter(parameterValue: String) { + composeTestRule.apply { + onAllNodesWithTag("INPUT_TEXT_FIELD").onLast().performTextInput(parameterValue) + } + } + + fun typeOnDateParameter(dateValue: String) { + composeTestRule.apply { + onNodeWithTag("INPUT_DATE_TIME_TEXT_FIELD").performTextInput(dateValue) + } + } + fun typeAttributeAtPosition(searchWord: String, position: Int) { onView(withId(R.id.recyclerView)) .perform( @@ -131,7 +164,7 @@ class SearchTeiRobot : BaseRobot() { onView(withId(R.id.acceptBtn)).perform(click()) } - fun clickOnSearch(composeTestRule: ComposeContentTestRule) { + fun clickOnSearch() { closeKeyboard() composeTestRule.onNodeWithTag("SEARCH_BUTTON").performClick() } @@ -179,7 +212,6 @@ class SearchTeiRobot : BaseRobot() { fun checkFieldsFromDisplayList( - composeTestRule: ComposeContentTestRule, displayListFieldsUIModel: DisplayListFieldsUIModel ) { //Given the title is the first attribute @@ -201,7 +233,11 @@ class SearchTeiRobot : BaseRobot() { composeTestRule.onNodeWithText(title).assertIsDisplayed() displayedAttributes.forEach { item -> item.key?.let { composeTestRule.onNodeWithText(it).assertIsDisplayed() } - composeTestRule.onNodeWithText(item.value).assertIsDisplayed() +// composeTestRule.onNodeWithText(item.value).assertIsDisplayed() + composeTestRule.onNode( + hasParent(hasTestTag("LIST_CARD_ADDITIONAL_INFO_COLUMN")) + and hasText(item.value), useUnmergedTree = true + ).assertIsDisplayed() } } diff --git a/app/src/androidTest/java/org/dhis2/usescases/teidashboard/TeiDashboardTestNoComposable.kt b/app/src/androidTest/java/org/dhis2/usescases/teidashboard/TeiDashboardTestNoComposable.kt index aa305567a9..abfbbfe78d 100644 --- a/app/src/androidTest/java/org/dhis2/usescases/teidashboard/TeiDashboardTestNoComposable.kt +++ b/app/src/androidTest/java/org/dhis2/usescases/teidashboard/TeiDashboardTestNoComposable.kt @@ -34,7 +34,7 @@ class TeiDashboardTestNoComposable : BaseTest() { setupCredentials() prepareChildProgrammeIntentAndLaunchActivity(ruleSearch) - searchTeiRobot { + searchTeiRobot(composeTestRule) { clickOnTEI(teiName, teiLastName) } @@ -49,11 +49,11 @@ class TeiDashboardTestNoComposable : BaseTest() { waitToDebounce(500) } - searchTeiRobot { + searchTeiRobot(composeTestRule) { clickOnOpenSearch() typeAttributeAtPosition(relationshipName, 0) typeAttributeAtPosition(relationshipLastName, 1) - clickOnSearch(composeTestRule) + clickOnSearch() waitToDebounce(5000) clickOnTEI(relationshipName, relationshipLastName) } @@ -73,11 +73,11 @@ class TeiDashboardTestNoComposable : BaseTest() { setupCredentials() prepareChildProgrammeIntentAndLaunchActivity(ruleSearch) - searchTeiRobot { + searchTeiRobot(composeTestRule) { clickOnOpenSearch() typeAttributeAtPosition(teiName, firstNamePosition) typeAttributeAtPosition(teiLastName, lastNamePosition) - clickOnSearch(composeTestRule) + clickOnSearch() clickOnTEI(teiName, teiLastName) //scrollToTEIandClick() } @@ -87,7 +87,7 @@ class TeiDashboardTestNoComposable : BaseTest() { clickOnMenuDeleteTEI() } - searchTeiRobot { + searchTeiRobot(composeTestRule) { checkTEIsDelete(teiName, teiLastName) } } @@ -102,11 +102,11 @@ class TeiDashboardTestNoComposable : BaseTest() { setupCredentials() prepareChildProgrammeIntentAndLaunchActivity(ruleSearch) - searchTeiRobot { + searchTeiRobot(composeTestRule) { clickOnOpenSearch() typeAttributeAtPosition(teiName, firstNamePosition) typeAttributeAtPosition(teiLastName, lastNamePosition) - clickOnSearch(composeTestRule) + clickOnSearch() // waitToDebounce(400) clickOnTEI(teiName, teiLastName) } @@ -116,7 +116,7 @@ class TeiDashboardTestNoComposable : BaseTest() { clickOnMenuDeleteEnrollment() } - searchTeiRobot { + searchTeiRobot(composeTestRule) { checkTEIsDelete(teiName, teiLastName) } } diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt index 530bc82c82..1852d87c5b 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt @@ -77,6 +77,10 @@ fun SearchParametersScreen( uiEvent.orgUnitSelectorScope ?: OrgUnitSelectorScope.UserSearchScope(), ) + is RecyclerViewUiEvents.ScanQRCode -> { + TODO() + } + else -> {} } } @@ -125,6 +129,8 @@ fun SearchParametersScreen( uiState.items.forEach { fieldUiModel -> fieldUiModel.setCallback(callback) ParameterSelectorItem( + modifier = Modifier + .testTag("SEARCH_PARAM_ITEM"), model = provideParameterSelectorItem( resources = resourceManager, focusManager = focusManager, diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt index 56146606e8..a82c165d4f 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt @@ -4,13 +4,9 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusDirection import androidx.compose.ui.focus.FocusManager -import androidx.compose.ui.focus.FocusRequester -import androidx.compose.ui.focus.focusRequester -import androidx.compose.ui.focus.onFocusChanged import org.dhis2.commons.resources.ResourceManager import org.dhis2.form.model.FieldUiModel import org.dhis2.form.ui.provider.inputfield.FieldProvider @@ -24,11 +20,11 @@ fun provideParameterSelectorItem( fieldUiModel: FieldUiModel, callback: FieldUiModel.Callback, ): ParameterSelectorItemModel { - val focusRequester = remember { FocusRequester() } - - var status by remember { + val status by remember(fieldUiModel.focused) { mutableStateOf( - if (fieldUiModel.value.isNullOrEmpty()) { + if (fieldUiModel.focused) { + ParameterSelectorItemModel.Status.FOCUSED + } else if (fieldUiModel.value.isNullOrEmpty()) { ParameterSelectorItemModel.Status.CLOSED } else { ParameterSelectorItemModel.Status.UNFOCUSED @@ -36,22 +32,12 @@ fun provideParameterSelectorItem( ) } - val modifierWithFocus = Modifier - .focusRequester(focusRequester) - .onFocusChanged { - status = if (it.isFocused) { - ParameterSelectorItemModel.Status.FOCUSED - } else { - ParameterSelectorItemModel.Status.UNFOCUSED - } - } - return ParameterSelectorItemModel( label = fieldUiModel.label, helper = "Optional", inputField = { FieldProvider( - modifier = modifierWithFocus, + modifier = Modifier, inputStyle = InputStyle.ParameterInputStyle(), fieldUiModel = fieldUiModel, uiEventHandler = callback::recyclerViewUiEvents, @@ -63,7 +49,7 @@ fun provideParameterSelectorItem( }, status = status, onExpand = { - status = ParameterSelectorItemModel.Status.UNFOCUSED + fieldUiModel.onItemClick() }, ) } From 239753ac2ed89a2de04cda18fdf8ab333cdaec2b Mon Sep 17 00:00:00 2001 From: andresmr Date: Wed, 14 Feb 2024 16:04:36 +0100 Subject: [PATCH 23/47] [ANDROAPP-5800] Update design library version --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 69f79b77c4..954842e9c8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,7 +10,7 @@ kotlin = '1.9.21' hilt = '2.47' hiltCompiler = '1.0.0' jacoco = '0.8.10' -designSystem = "0.2-20240213.152311-21" +designSystem = "0.2-20240214.150049-22" dhis2sdk = "1.10.0-20240207.110936-11" ruleEngine = "2.1.9" appcompat = "1.6.1" From 9d43c4ed3be4e00b60fa043a955a62618354a00d Mon Sep 17 00:00:00 2001 From: andresmr Date: Wed, 14 Feb 2024 18:13:27 +0100 Subject: [PATCH 24/47] [ANDROAPP-5800] Remove SearchParametersRepository.kt and use SearchRepositoryKt --- .../SearchRepositoryImplKt.kt | 145 +++++++++++++++++- .../searchTrackEntity/SearchRepositoryKt.kt | 3 + .../searchTrackEntity/SearchTEActivity.java | 94 +++++------- .../searchTrackEntity/SearchTEIViewModel.kt | 8 +- .../searchTrackEntity/SearchTEModule.java | 25 ++- .../SearchTeiViewModelFactory.kt | 5 +- .../SearchParametersRepository.kt | 132 ---------------- .../SearchParametersScreen.kt | 36 ++++- .../SearchTEIViewModelTest.kt | 3 - 9 files changed, 225 insertions(+), 226 deletions(-) delete mode 100644 app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersRepository.kt diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchRepositoryImplKt.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchRepositoryImplKt.kt index 57987f08d5..e61c9c08f2 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchRepositoryImplKt.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchRepositoryImplKt.kt @@ -2,13 +2,28 @@ package org.dhis2.usescases.searchTrackEntity import androidx.paging.PagingData import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.withContext import org.dhis2.commons.filters.FilterManager +import org.dhis2.commons.viewmodel.DispatcherProvider import org.dhis2.data.search.SearchParametersModel +import org.dhis2.form.model.FieldUiModel +import org.dhis2.form.model.OptionSetConfiguration +import org.dhis2.form.ui.FieldViewModelFactory +import org.hisp.dhis.android.core.D2 +import org.hisp.dhis.android.core.arch.repositories.scope.RepositoryScope +import org.hisp.dhis.android.core.common.ObjectStyle +import org.hisp.dhis.android.core.common.ValueType +import org.hisp.dhis.android.core.program.ProgramTrackedEntityAttribute +import org.hisp.dhis.android.core.program.SectionRenderingType +import org.hisp.dhis.android.core.trackedentity.TrackedEntityAttribute import org.hisp.dhis.android.core.trackedentity.search.TrackedEntitySearchCollectionRepository import org.hisp.dhis.android.core.trackedentity.search.TrackedEntitySearchItem class SearchRepositoryImplKt( private val searchRepositoryJava: SearchRepository, + private val d2: D2, + private val dispatcher: DispatcherProvider, + private val fieldViewModelFactory: FieldViewModelFactory, ) : SearchRepositoryKt { private lateinit var savedSearchParamenters: SearchParametersModel @@ -27,10 +42,14 @@ class SearchRepositoryImplKt( savedSearchParamenters = searchParametersModel.copy() savedFilters = FilterManager.getInstance().copy() - if (searchParametersModel != savedSearchParamenters || !FilterManager.getInstance().sameFilters(savedFilters)) { - trackedEntityInstanceQuery = searchRepositoryJava.getFilteredRepository(searchParametersModel) + if (searchParametersModel != savedSearchParamenters || !FilterManager.getInstance() + .sameFilters(savedFilters) + ) { + trackedEntityInstanceQuery = + searchRepositoryJava.getFilteredRepository(searchParametersModel) } else { - trackedEntityInstanceQuery = searchRepositoryJava.getFilteredRepository(searchParametersModel) + trackedEntityInstanceQuery = + searchRepositoryJava.getFilteredRepository(searchParametersModel) allowCache = true } @@ -40,11 +59,127 @@ class SearchRepositoryImplKt( } val pagerFlow = if (isOnline && FilterManager.getInstance().stateFilters.isNotEmpty()) { - trackedEntityInstanceQuery.allowOnlineCache().eq(allowCache).offlineFirst().getPagingData(10) + trackedEntityInstanceQuery.allowOnlineCache().eq(allowCache).offlineFirst() + .getPagingData(10) } else { - trackedEntityInstanceQuery.allowOnlineCache().eq(allowCache).offlineOnly().getPagingData(10) + trackedEntityInstanceQuery.allowOnlineCache().eq(allowCache).offlineOnly() + .getPagingData(10) } return pagerFlow } + + override suspend fun searchParameters( + programUid: String?, + teiTypeUid: String, + ): List = + withContext(dispatcher.io()) { + programUid?.let { + programTrackedEntityAttributes(programUid) + } ?: trackedEntitySearchFields(teiTypeUid) + } + + private fun programTrackedEntityAttributes(programUid: String): List { + val searchableAttributes = d2.programModule().programTrackedEntityAttributes() + .withRenderType() + .byProgram().eq(programUid) + .blockingGet().filter { programAttribute -> + val isSearchable = programAttribute.searchable()!! + val isUnique = d2.trackedEntityModule().trackedEntityAttributes() + .uid(programAttribute.trackedEntityAttribute()!!.uid()) + .blockingGet()?.unique() === java.lang.Boolean.TRUE + isSearchable || isUnique + } + + return searchableAttributes.mapNotNull { programAttribute -> + d2.trackedEntityModule().trackedEntityAttributes() + .uid(programAttribute.trackedEntityAttribute()!!.uid()) + .blockingGet()?.let { attribute -> + + val optionSetConfiguration = attribute.optionSet()?.let { + OptionSetConfiguration.config( + d2.optionModule().options() + .byOptionSetUid().eq(attribute.optionSet()!!.uid()) + .blockingCount(), + ) { + d2.optionModule().options() + .byOptionSetUid().eq(attribute.optionSet()!!.uid()) + .orderBySortOrder(RepositoryScope.OrderByDirection.ASC) + .blockingGet() + } + } + createField( + trackedEntityAttribute = attribute, + programTrackedEntityAttribute = programAttribute, + optionSetConfiguration = optionSetConfiguration, + ) + } + }.filter { parameter -> + parameter.valueType !== ValueType.IMAGE && + parameter.valueType !== ValueType.COORDINATE && + parameter.valueType !== ValueType.FILE_RESOURCE + } + } + + private fun trackedEntitySearchFields(teiTypeUid: String): List { + val teTypeAttributes = d2.trackedEntityModule().trackedEntityTypeAttributes() + .byTrackedEntityTypeUid().eq(teiTypeUid) + .bySearchable().isTrue + .blockingGet() + + return teTypeAttributes.mapNotNull { typeAttribute -> + d2.trackedEntityModule().trackedEntityAttributes() + .uid(typeAttribute.trackedEntityAttribute()!!.uid()) + .blockingGet()?.let { attribute -> + + val optionSetConfiguration = attribute.optionSet()?.let { + OptionSetConfiguration.config( + d2.optionModule().options() + .byOptionSetUid().eq(attribute.optionSet()!!.uid()) + .blockingCount(), + ) { + d2.optionModule().options() + .byOptionSetUid().eq(attribute.optionSet()!!.uid()) + .orderBySortOrder(RepositoryScope.OrderByDirection.ASC) + .blockingGet() + } + } + + createField( + trackedEntityAttribute = attribute, + programTrackedEntityAttribute = null, + optionSetConfiguration = optionSetConfiguration, + ) + } + }.filter { parameter -> + parameter.valueType !== ValueType.IMAGE && + parameter.valueType !== ValueType.COORDINATE && + parameter.valueType !== ValueType.FILE_RESOURCE + } + } + + private fun createField( + trackedEntityAttribute: TrackedEntityAttribute, + programTrackedEntityAttribute: ProgramTrackedEntityAttribute?, + optionSetConfiguration: OptionSetConfiguration?, + ): FieldUiModel { + return fieldViewModelFactory.create( + id = trackedEntityAttribute.uid(), + label = trackedEntityAttribute.displayFormName() ?: "", + valueType = trackedEntityAttribute.valueType()!!, + mandatory = false, + optionSet = trackedEntityAttribute.optionSet()?.uid(), + value = null, + programStageSection = null, + allowFutureDates = programTrackedEntityAttribute?.allowFutureDate() ?: true, + editable = true, + renderingType = SectionRenderingType.LISTING, + description = null, + fieldRendering = programTrackedEntityAttribute?.renderType()?.mobile(), + objectStyle = trackedEntityAttribute.style() ?: ObjectStyle.builder().build(), + fieldMask = trackedEntityAttribute.fieldMask(), + optionSetConfiguration = optionSetConfiguration, + featureType = null, + ) + } } diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchRepositoryKt.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchRepositoryKt.kt index 382bc6c6a0..0163f8b1bb 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchRepositoryKt.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchRepositoryKt.kt @@ -3,6 +3,7 @@ package org.dhis2.usescases.searchTrackEntity import androidx.paging.PagingData import kotlinx.coroutines.flow.Flow import org.dhis2.data.search.SearchParametersModel +import org.dhis2.form.model.FieldUiModel import org.hisp.dhis.android.core.trackedentity.search.TrackedEntitySearchItem interface SearchRepositoryKt { @@ -11,4 +12,6 @@ interface SearchRepositoryKt { searchParametersModel: SearchParametersModel, isOnline: Boolean, ): Flow> + + suspend fun searchParameters(programUid: String?, teiTypeUid: String): List } diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java index befd2f9ef4..4298fa5f12 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java @@ -6,7 +6,6 @@ import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; -import android.graphics.Color; import android.os.Bundle; import android.view.View; @@ -35,8 +34,6 @@ import org.dhis2.commons.sync.SyncContext; import org.dhis2.data.forms.dataentry.ProgramAdapter; import org.dhis2.databinding.ActivitySearchBinding; -import org.dhis2.databinding.SnackbarMinAttrBinding; -import org.dhis2.form.ui.event.RecyclerViewUiEvents; import org.dhis2.form.ui.intent.FormIntent; import org.dhis2.ui.ThemeManager; import org.dhis2.usescases.general.ActivityGlobalAbstract; @@ -226,26 +223,6 @@ private void inject() { searchComponent.inject(this); } - private void showSnackbar(View view, String message, String actionText) { - Snackbar snackbar = Snackbar.make( - view, - actionText, - BaseTransientBottomBar.LENGTH_LONG - ); - SnackbarMinAttrBinding snackbarBinding = SnackbarMinAttrBinding.inflate(getLayoutInflater()); - snackbarBinding.message.setText(message); - snackbarBinding.actionButton.setOnClickListener(v -> { - if (snackbar.isShown()) snackbar.dismiss(); - }); - snackbar.getView().setBackgroundColor(Color.TRANSPARENT); - Snackbar.SnackbarLayout snackbarLayout = (Snackbar.SnackbarLayout) snackbar.getView(); - snackbarLayout.setPadding(0, 0, 0, 0); - - snackbarLayout.addView(snackbarBinding.getRoot(), 0); - - snackbar.show(); - } - @Override protected void onResume() { super.onResume(); @@ -332,11 +309,11 @@ private void openSyncDialog() { if (hasChanged) viewModel.refreshData(); }) .onNoConnectionListener(() -> - Snackbar.make( - contextView, - R.string.sync_offline_check_connection, - Snackbar.LENGTH_SHORT - ).show() + Snackbar.make( + contextView, + R.string.sync_offline_check_connection, + Snackbar.LENGTH_SHORT + ).show() ) .show("PROGRAM_SYNC"); } @@ -356,29 +333,34 @@ private void initSearchParameters() { initialProgram, tEType, resourceManager, - (uid, preselectedOrgUnits, orgUnitScope) -> { - new OUTreeFragment.Builder() - .showAsDialog() - .withPreselectedOrgUnits(preselectedOrgUnits) - .singleSelection() - .onSelection(selectedOrgUnits -> { - String selecteOrgUnit = null; - if(!selectedOrgUnits.isEmpty()) { - selecteOrgUnit = selectedOrgUnits.get(0).uid(); - } - viewModel.onParameterIntent( - new FormIntent.OnSave( - uid, - selecteOrgUnit, - ValueType.ORGANISATION_UNIT, - null - ) - ); - return Unit.INSTANCE; - }) - .orgUnitScope(orgUnitScope) - .build() - .show(getSupportFragmentManager(), "OUTreeFragment"); + (uid, preselectedOrgUnits, orgUnitScope, label) -> { + new OUTreeFragment.Builder() + .showAsDialog() + .withPreselectedOrgUnits(preselectedOrgUnits) + .singleSelection() + .onSelection(selectedOrgUnits -> { + String selecteOrgUnit = null; + if (!selectedOrgUnits.isEmpty()) { + selecteOrgUnit = selectedOrgUnits.get(0).uid(); + } + viewModel.onParameterIntent( + new FormIntent.OnSave( + uid, + selecteOrgUnit, + ValueType.ORGANISATION_UNIT, + null + ) + ); + return Unit.INSTANCE; + }) + .orgUnitScope(orgUnitScope) + .build() + .show(getSupportFragmentManager(), label); + return Unit.INSTANCE; + }, + (uid, optionSet, renderingType) -> { + + // TODO return Unit.INSTANCE; }, () -> { @@ -598,11 +580,11 @@ public void showSyncDialog(String enrollmentUid) { if (hasChanged) viewModel.refreshData(); }) .onNoConnectionListener(() -> - Snackbar.make( - contextView, - R.string.sync_offline_check_connection, - Snackbar.LENGTH_SHORT - ).show() + Snackbar.make( + contextView, + R.string.sync_offline_check_connection, + Snackbar.LENGTH_SHORT + ).show() ).show("TEI_SYNC"); } diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt index 2acff29514..a1a86a1b2b 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt @@ -30,7 +30,6 @@ import org.dhis2.form.ui.provider.DisplayNameProvider import org.dhis2.maps.layer.basemaps.BaseMapStyle import org.dhis2.maps.usecases.MapStyleConfiguration import org.dhis2.usescases.searchTrackEntity.listView.SearchResult -import org.dhis2.usescases.searchTrackEntity.searchparameters.SearchParametersRepository import org.dhis2.usescases.searchTrackEntity.searchparameters.model.SearchParametersUiState import org.dhis2.usescases.searchTrackEntity.ui.UnableToSearchOutsideData import org.dhis2.utils.customviews.navigationbar.NavigationPageConfigurator @@ -49,7 +48,6 @@ class SearchTEIViewModel( private val networkUtils: NetworkUtils, private val dispatchers: DispatcherProvider, private val mapStyleConfig: MapStyleConfiguration, - private val searchParametersRepository: SearchParametersRepository, private val resourceManager: ResourceManager, private val displayNameProvider: DisplayNameProvider, ) : ViewModel() { @@ -745,7 +743,7 @@ class SearchTEIViewModel( fetchJob?.cancel() fetchJob = viewModelScope.launch { val fieldUiModels = - searchParametersRepository.searchParameters(programUid, teiTypeUid) + searchRepositoryKt.searchParameters(programUid, teiTypeUid) uiState = uiState.copy(items = fieldUiModels) } } @@ -785,6 +783,8 @@ class SearchTEIViewModel( ) } - else -> {} + else -> { + // no-op + } } } diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEModule.java b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEModule.java index f5a0daf241..cce85b5b1d 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEModule.java +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEModule.java @@ -64,7 +64,6 @@ import org.dhis2.maps.usecases.MapStyleConfiguration; import org.dhis2.maps.utils.DhisMapUtils; import org.dhis2.ui.ThemeManager; -import org.dhis2.usescases.searchTrackEntity.searchparameters.SearchParametersRepository; import org.dhis2.usescases.searchTrackEntity.ui.mapper.TEICardMapper; import org.dhis2.utils.DateUtils; import org.dhis2.utils.analytics.AnalyticsHelper; @@ -162,9 +161,17 @@ SearchRepository searchRepository(@NonNull D2 d2, FilterPresenter filterPresente @Provides @PerActivity SearchRepositoryKt searchRepositoryKt( - SearchRepository searchRepository + SearchRepository searchRepository, + D2 d2, + DispatcherProvider dispatcherProvider, + FieldViewModelFactory fieldViewModelFactory ) { - return new SearchRepositoryImplKt(searchRepository); + return new SearchRepositoryImplKt( + searchRepository, + d2, + dispatcherProvider, + fieldViewModelFactory + ); } @Provides @@ -274,7 +281,6 @@ SearchTeiViewModelFactory providesViewModelFactory( MapDataRepository mapDataRepository, NetworkUtils networkUtils, D2 d2, - SearchParametersRepository searchParametersRepository, ResourceManager resourceManager, DisplayNameProvider displayNameProvider ) { @@ -288,7 +294,6 @@ SearchTeiViewModelFactory providesViewModelFactory( networkUtils, new SearchDispatchers(), new MapStyleConfiguration(d2), - searchParametersRepository, resourceManager, displayNameProvider ); @@ -304,16 +309,6 @@ DisplayNameProvider provideDisplayNameProvider(D2 d2) { ); } - @Provides - @PerActivity - SearchParametersRepository provideSearchParametersRepository( - D2 d2, - DispatcherProvider dispatcherProvider, - FieldViewModelFactory fieldViewModelFactory - ) { - return new SearchParametersRepository(d2, dispatcherProvider, fieldViewModelFactory); - } - @Provides @PerActivity MapDataRepository mapDataRepository( diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTeiViewModelFactory.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTeiViewModelFactory.kt index bcbd8ff8e4..a403aa8b9c 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTeiViewModelFactory.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTeiViewModelFactory.kt @@ -7,11 +7,10 @@ import org.dhis2.commons.resources.ResourceManager import org.dhis2.commons.viewmodel.DispatcherProvider import org.dhis2.form.ui.provider.DisplayNameProvider import org.dhis2.maps.usecases.MapStyleConfiguration -import org.dhis2.usescases.searchTrackEntity.searchparameters.SearchParametersRepository @Suppress("UNCHECKED_CAST") class SearchTeiViewModelFactory( - val searchRepository: SearchRepository, + private val searchRepository: SearchRepository, private val searchRepositoryKt: SearchRepositoryKt, private val searchNavPageConfigurator: SearchPageConfigurator, private val initialProgramUid: String?, @@ -20,7 +19,6 @@ class SearchTeiViewModelFactory( private val networkUtils: NetworkUtils, private val dispatchers: DispatcherProvider, private val mapStyleConfig: MapStyleConfiguration, - private val searchParameterRepository: SearchParametersRepository, private val resourceManager: ResourceManager, private val displayNameProvider: DisplayNameProvider, ) : ViewModelProvider.Factory { @@ -35,7 +33,6 @@ class SearchTeiViewModelFactory( networkUtils, dispatchers, mapStyleConfig, - searchParameterRepository, resourceManager, displayNameProvider, ) as T diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersRepository.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersRepository.kt deleted file mode 100644 index eb6eb4db65..0000000000 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersRepository.kt +++ /dev/null @@ -1,132 +0,0 @@ -package org.dhis2.usescases.searchTrackEntity.searchparameters - -import kotlinx.coroutines.withContext -import org.dhis2.commons.viewmodel.DispatcherProvider -import org.dhis2.form.model.FieldUiModel -import org.dhis2.form.model.OptionSetConfiguration -import org.dhis2.form.ui.FieldViewModelFactory -import org.hisp.dhis.android.core.D2 -import org.hisp.dhis.android.core.arch.repositories.scope.RepositoryScope -import org.hisp.dhis.android.core.common.ObjectStyle -import org.hisp.dhis.android.core.common.ValueType -import org.hisp.dhis.android.core.program.ProgramTrackedEntityAttribute -import org.hisp.dhis.android.core.program.SectionRenderingType -import org.hisp.dhis.android.core.trackedentity.TrackedEntityAttribute - -class SearchParametersRepository( - private val d2: D2, - private val dispatcher: DispatcherProvider, - private val fieldViewModelFactory: FieldViewModelFactory, -) { - - suspend fun searchParameters(programUid: String?, teiTypeUid: String): List = - withContext(dispatcher.io()) { - programUid?.let { - programTrackedEntityAttributes(programUid) - } ?: trackedEntitySearchFields(teiTypeUid) - } - - private fun programTrackedEntityAttributes(programUid: String): List { - val searchableAttributes = d2.programModule().programTrackedEntityAttributes() - .withRenderType() - .byProgram().eq(programUid) - .blockingGet().filter { programAttribute -> - val isSearchable = programAttribute.searchable()!! - val isUnique = d2.trackedEntityModule().trackedEntityAttributes() - .uid(programAttribute.trackedEntityAttribute()!!.uid()) - .blockingGet()?.unique() === java.lang.Boolean.TRUE - isSearchable || isUnique - } - - return searchableAttributes.mapNotNull { programAttribute -> - d2.trackedEntityModule().trackedEntityAttributes() - .uid(programAttribute.trackedEntityAttribute()!!.uid()) - .blockingGet()?.let { attribute -> - - val optionSetConfiguration = attribute.optionSet()?.let { - OptionSetConfiguration.config( - d2.optionModule().options() - .byOptionSetUid().eq(attribute.optionSet()!!.uid()) - .blockingCount(), - ) { - d2.optionModule().options() - .byOptionSetUid().eq(attribute.optionSet()!!.uid()) - .orderBySortOrder(RepositoryScope.OrderByDirection.ASC) - .blockingGet() - } - } - createField( - trackedEntityAttribute = attribute, - programTrackedEntityAttribute = programAttribute, - optionSetConfiguration = optionSetConfiguration, - ) - } - }.filter { parameter -> - parameter.valueType !== ValueType.IMAGE && - parameter.valueType !== ValueType.COORDINATE && - parameter.valueType !== ValueType.FILE_RESOURCE - } - } - - private fun trackedEntitySearchFields(teiTypeUid: String): List { - val teTypeAttributes = d2.trackedEntityModule().trackedEntityTypeAttributes() - .byTrackedEntityTypeUid().eq(teiTypeUid) - .bySearchable().isTrue - .blockingGet() - - return teTypeAttributes.mapNotNull { typeAttribute -> - d2.trackedEntityModule().trackedEntityAttributes() - .uid(typeAttribute.trackedEntityAttribute()!!.uid()) - .blockingGet()?.let { attribute -> - - val optionSetConfiguration = attribute.optionSet()?.let { - OptionSetConfiguration.config( - d2.optionModule().options() - .byOptionSetUid().eq(attribute.optionSet()!!.uid()) - .blockingCount(), - ) { - d2.optionModule().options() - .byOptionSetUid().eq(attribute.optionSet()!!.uid()) - .orderBySortOrder(RepositoryScope.OrderByDirection.ASC) - .blockingGet() - } - } - - createField( - trackedEntityAttribute = attribute, - programTrackedEntityAttribute = null, - optionSetConfiguration = optionSetConfiguration, - ) - } - }.filter { parameter -> - parameter.valueType !== ValueType.IMAGE && - parameter.valueType !== ValueType.COORDINATE && - parameter.valueType !== ValueType.FILE_RESOURCE - } - } - - private fun createField( - trackedEntityAttribute: TrackedEntityAttribute, - programTrackedEntityAttribute: ProgramTrackedEntityAttribute?, - optionSetConfiguration: OptionSetConfiguration?, - ): FieldUiModel { - return fieldViewModelFactory.create( - id = trackedEntityAttribute.uid(), - label = trackedEntityAttribute.displayFormName() ?: "", - valueType = trackedEntityAttribute.valueType()!!, - mandatory = false, - optionSet = trackedEntityAttribute.optionSet()?.uid(), - value = null, - programStageSection = null, - allowFutureDates = programTrackedEntityAttribute?.allowFutureDate() ?: true, - editable = true, - renderingType = SectionRenderingType.LISTING, - description = null, - fieldRendering = programTrackedEntityAttribute?.renderType()?.mobile(), - objectStyle = trackedEntityAttribute.style() ?: ObjectStyle.builder().build(), - fieldMask = trackedEntityAttribute.fieldMask(), - optionSetConfiguration = optionSetConfiguration, - featureType = null, - ) - } -} diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt index 1852d87c5b..6cc04d3036 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt @@ -33,6 +33,7 @@ import org.dhis2.commons.resources.ColorUtils import org.dhis2.commons.resources.ResourceManager import org.dhis2.form.model.FieldUiModel import org.dhis2.form.model.FieldUiModelImpl +import org.dhis2.form.model.UiRenderType import org.dhis2.form.ui.event.RecyclerViewUiEvents import org.dhis2.form.ui.intent.FormIntent import org.dhis2.usescases.searchTrackEntity.SearchTEIViewModel @@ -50,10 +51,16 @@ fun SearchParametersScreen( resourceManager: ResourceManager, uiState: SearchParametersUiState, intentHandler: (FormIntent) -> Unit, - showOrgUnit: ( + onShowOrgUnit: ( uid: String, preselectedOrgUnits: List, orgUnitScope: OrgUnitSelectorScope, + label: String, + ) -> Unit, + onScanQRCode: ( + uid: String, + optionSet: String?, + renderingType: UiRenderType?, ) -> Unit, onSearchClick: () -> Unit, onClear: () -> Unit, @@ -71,17 +78,24 @@ fun SearchParametersScreen( override fun recyclerViewUiEvents(uiEvent: RecyclerViewUiEvents) { when (uiEvent) { is RecyclerViewUiEvents.OpenOrgUnitDialog -> - showOrgUnit( + onShowOrgUnit( uiEvent.uid, uiEvent.value?.let { listOf(it) } ?: emptyList(), uiEvent.orgUnitSelectorScope ?: OrgUnitSelectorScope.UserSearchScope(), + uiEvent.label, ) is RecyclerViewUiEvents.ScanQRCode -> { - TODO() + onScanQRCode( + uiEvent.uid, + uiEvent.optionSet, + uiEvent.renderingType, + ) } - else -> {} + else -> { + // no-op + } } } } @@ -198,7 +212,8 @@ fun SearchFormPreview() { ), ), intentHandler = {}, - showOrgUnit = { _, _, _ -> }, + onShowOrgUnit = { _, _, _, _ -> }, + onScanQRCode = { _, _, _ -> }, onSearchClick = {}, onClear = {}, ) @@ -210,10 +225,16 @@ fun initSearchScreen( program: String?, teiType: String, resources: ResourceManager, - showOrgUnit: ( + onShowOrgUnit: ( uid: String, preselectedOrgUnits: List, orgUnitScope: OrgUnitSelectorScope, + label: String, + ) -> Unit, + onScanQRCode: ( + uid: String, + optionSet: String?, + renderingType: UiRenderType?, ) -> Unit, onClear: () -> Unit, ) { @@ -227,7 +248,8 @@ fun initSearchScreen( uiState = viewModel.uiState, onSearchClick = viewModel::onSearchClick, intentHandler = viewModel::onParameterIntent, - showOrgUnit = showOrgUnit, + onShowOrgUnit = onShowOrgUnit, + onScanQRCode = onScanQRCode, onClear = { onClear() viewModel.clearQueryData() diff --git a/app/src/test/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModelTest.kt b/app/src/test/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModelTest.kt index 2409149871..74141a674d 100644 --- a/app/src/test/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModelTest.kt +++ b/app/src/test/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModelTest.kt @@ -19,7 +19,6 @@ import org.dhis2.form.ui.provider.DisplayNameProvider import org.dhis2.maps.geometry.mapper.EventsByProgramStage import org.dhis2.maps.usecases.MapStyleConfiguration import org.dhis2.usescases.searchTrackEntity.listView.SearchResult.SearchResultType -import org.dhis2.usescases.searchTrackEntity.searchparameters.SearchParametersRepository import org.hisp.dhis.android.core.common.ValueType import org.hisp.dhis.android.core.program.Program import org.hisp.dhis.android.core.trackedentity.TrackedEntityType @@ -48,7 +47,6 @@ class SearchTEIViewModelTest { private val mapDataRepository: MapDataRepository = mock() private val networkUtils: NetworkUtils = mock() private val mapStyleConfiguration: MapStyleConfiguration = mock() - private val searchParametersRepository: SearchParametersRepository = mock() private val resourceManager: ResourceManager = mock() private val displayNameProvider: DisplayNameProvider = mock() @@ -86,7 +84,6 @@ class SearchTEIViewModelTest { } }, mapStyleConfiguration, - searchParametersRepository = searchParametersRepository, resourceManager = resourceManager, displayNameProvider = displayNameProvider, ) From c019054094c4d7863dc72607a41b128751d0463c Mon Sep 17 00:00:00 2001 From: andresmr Date: Wed, 14 Feb 2024 21:24:30 +0100 Subject: [PATCH 25/47] [ANDROAPP-5800] provide icons based on valueType --- .../provider/ParameterSelectorItemProvider.kt | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt index a82c165d4f..7a132b2a07 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt @@ -1,5 +1,9 @@ package org.dhis2.usescases.searchTrackEntity.searchparameters.provider +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.AddCircleOutline +import androidx.compose.material.icons.outlined.QrCode +import androidx.compose.material.icons.outlined.QrCode2 import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -7,9 +11,12 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusDirection import androidx.compose.ui.focus.FocusManager +import androidx.compose.ui.graphics.vector.ImageVector import org.dhis2.commons.resources.ResourceManager import org.dhis2.form.model.FieldUiModel +import org.dhis2.form.model.UiRenderType import org.dhis2.form.ui.provider.inputfield.FieldProvider +import org.hisp.dhis.android.core.common.ValueType import org.hisp.dhis.mobile.ui.designsystem.component.InputStyle import org.hisp.dhis.mobile.ui.designsystem.component.parameter.model.ParameterSelectorItemModel @@ -33,6 +40,7 @@ fun provideParameterSelectorItem( } return ParameterSelectorItemModel( + icon = provideIcon(fieldUiModel.valueType, fieldUiModel.renderingType), label = fieldUiModel.label, helper = "Optional", inputField = { @@ -53,3 +61,21 @@ fun provideParameterSelectorItem( }, ) } + +private fun provideIcon(valueType: ValueType?, renderingType: UiRenderType?): ImageVector = + when (valueType) { + ValueType.TEXT -> { + when (renderingType) { + UiRenderType.QR_CODE, UiRenderType.GS1_DATAMATRIX -> { + Icons.Outlined.QrCode2 + } + UiRenderType.BAR_CODE -> { + Icons.Outlined.QrCode + } + else -> { + Icons.Outlined.AddCircleOutline + } + } + } + else -> Icons.Outlined.AddCircleOutline + } From 076efb7466c2a7e9ce3adbf771b5762216fc7035 Mon Sep 17 00:00:00 2001 From: andresmr Date: Wed, 14 Feb 2024 22:05:08 +0100 Subject: [PATCH 26/47] [ANDROAPP-5800] Implement QR and Barcode scanning --- .../searchTrackEntity/SearchTEActivity.java | 5 -- .../SearchParametersScreen.kt | 57 ++++++++++++------- app/src/main/res/values/strings.xml | 1 + 3 files changed, 38 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java index 4298fa5f12..3eec80c2e2 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java @@ -358,11 +358,6 @@ private void initSearchParameters() { .show(getSupportFragmentManager(), label); return Unit.INSTANCE; }, - (uid, optionSet, renderingType) -> { - - // TODO - return Unit.INSTANCE; - }, () -> { presenter.onClearClick(); return Unit.INSTANCE; diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt index 6cc04d3036..50c7fbb788 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt @@ -1,5 +1,6 @@ package org.dhis2.usescases.searchTrackEntity.searchparameters +import androidx.activity.compose.rememberLauncherForActivityResult import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize @@ -26,14 +27,17 @@ import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.testTag import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import com.journeyapps.barcodescanner.ScanOptions import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch +import org.dhis2.R +import org.dhis2.commons.Constants import org.dhis2.commons.orgunitselector.OrgUnitSelectorScope import org.dhis2.commons.resources.ColorUtils import org.dhis2.commons.resources.ResourceManager +import org.dhis2.form.data.scan.ScanContract import org.dhis2.form.model.FieldUiModel import org.dhis2.form.model.FieldUiModelImpl -import org.dhis2.form.model.UiRenderType import org.dhis2.form.ui.event.RecyclerViewUiEvents import org.dhis2.form.ui.intent.FormIntent import org.dhis2.usescases.searchTrackEntity.SearchTEIViewModel @@ -57,11 +61,6 @@ fun SearchParametersScreen( orgUnitScope: OrgUnitSelectorScope, label: String, ) -> Unit, - onScanQRCode: ( - uid: String, - optionSet: String?, - renderingType: UiRenderType?, - ) -> Unit, onSearchClick: () -> Unit, onClear: () -> Unit, ) { @@ -69,6 +68,20 @@ fun SearchParametersScreen( val snackBarHostState = scaffoldState.snackbarHostState val coroutineScope = rememberCoroutineScope() val focusManager = LocalFocusManager.current + + val qrScanLauncher = rememberLauncherForActivityResult( + contract = ScanContract(), + ) { result -> + result.contents?.let { qrData -> + val intent = FormIntent.OnSave( + result.originalIntent.getStringExtra(Constants.UID)!!, + qrData, + ValueType.TEXT, + ) + intentHandler(intent) + } + } + val callback = remember { object : FieldUiModel.Callback { override fun intent(intent: FormIntent) { @@ -86,10 +99,21 @@ fun SearchParametersScreen( ) is RecyclerViewUiEvents.ScanQRCode -> { - onScanQRCode( - uiEvent.uid, - uiEvent.optionSet, - uiEvent.renderingType, + qrScanLauncher.launch( + ScanOptions().apply { + setDesiredBarcodeFormats() + setPrompt("") + setBeepEnabled(true) + setBarcodeImageEnabled(false) + addExtra(Constants.UID, uiEvent.uid) + uiEvent.optionSet?.let { + addExtra( + Constants.OPTION_SET, + uiEvent.optionSet, + ) + } + addExtra(Constants.SCAN_RENDERING_TYPE, uiEvent.renderingType) + }, ) } @@ -158,11 +182,11 @@ fun SearchParametersScreen( modifier = Modifier .align(Alignment.CenterHorizontally), style = ButtonStyle.TEXT, - text = "Clear search", + text = resourceManager.getString(R.string.clear_search), icon = { Icon( imageVector = Icons.Outlined.Cancel, - contentDescription = "Clear search", + contentDescription = resourceManager.getString(R.string.clear_search), tint = SurfaceColor.Primary, ) }, @@ -178,7 +202,7 @@ fun SearchParametersScreen( .padding(16.dp, 8.dp, 16.dp, 8.dp) .testTag("SEARCH_BUTTON"), style = ButtonStyle.FILLED, - text = "Search", + text = resourceManager.getString(R.string.search), ) { onSearchClick() } @@ -213,7 +237,6 @@ fun SearchFormPreview() { ), intentHandler = {}, onShowOrgUnit = { _, _, _, _ -> }, - onScanQRCode = { _, _, _ -> }, onSearchClick = {}, onClear = {}, ) @@ -231,11 +254,6 @@ fun initSearchScreen( orgUnitScope: OrgUnitSelectorScope, label: String, ) -> Unit, - onScanQRCode: ( - uid: String, - optionSet: String?, - renderingType: UiRenderType?, - ) -> Unit, onClear: () -> Unit, ) { viewModel.fetchSearchParameters( @@ -249,7 +267,6 @@ fun initSearchScreen( onSearchClick = viewModel::onSearchClick, intentHandler = viewModel::onParameterIntent, onShowOrgUnit = onShowOrgUnit, - onScanQRCode = onScanQRCode, onClear = { onClear() viewModel.clearQueryData() diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fdf2f1c4bf..eb9bdd78c5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -952,4 +952,5 @@ event %d events Timeline + \"Clear search\" From cf6d0011f3ef7372c4fe60706568ae418098fc9e Mon Sep 17 00:00:00 2001 From: andresmr Date: Wed, 14 Feb 2024 22:13:21 +0100 Subject: [PATCH 27/47] [ANDROAPP-5800] fix code smells --- .../usescases/searchTrackEntity/SearchTEIViewModel.kt | 2 +- .../searchparameters/model/SearchParametersUiState.kt | 11 +++++++++-- .../provider/ParameterSelectorItemProvider.kt | 3 ++- app/src/main/res/values/strings.xml | 1 + 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt index a1a86a1b2b..1d1fef8f86 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt @@ -427,7 +427,7 @@ class SearchTEIViewModel( minAttributesToSearch, ) uiState = uiState.copy(minAttributesMessage = message) - uiState.shouldShowMinAttributeWarning.emit(true) + uiState.updateMinAttributeWarning(true) } } } diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParametersUiState.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParametersUiState.kt index fb91ff1f91..9e12fee398 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParametersUiState.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParametersUiState.kt @@ -1,10 +1,17 @@ package org.dhis2.usescases.searchTrackEntity.searchparameters.model import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow import org.dhis2.form.model.FieldUiModel data class SearchParametersUiState( val items: List = listOf(), val minAttributesMessage: String? = null, - val shouldShowMinAttributeWarning: MutableSharedFlow = MutableSharedFlow(), -) + private val _shouldShowMinAttributeWarning: MutableSharedFlow = MutableSharedFlow(), +) { + val shouldShowMinAttributeWarning: SharedFlow = _shouldShowMinAttributeWarning + + suspend fun updateMinAttributeWarning(showWarning: Boolean) { + _shouldShowMinAttributeWarning.emit(showWarning) + } +} diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt index 7a132b2a07..cb595aa67e 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt @@ -12,6 +12,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusDirection import androidx.compose.ui.focus.FocusManager import androidx.compose.ui.graphics.vector.ImageVector +import org.dhis2.R import org.dhis2.commons.resources.ResourceManager import org.dhis2.form.model.FieldUiModel import org.dhis2.form.model.UiRenderType @@ -42,7 +43,7 @@ fun provideParameterSelectorItem( return ParameterSelectorItemModel( icon = provideIcon(fieldUiModel.valueType, fieldUiModel.renderingType), label = fieldUiModel.label, - helper = "Optional", + helper = resources.getString(R.string.optional), inputField = { FieldProvider( modifier = Modifier, diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index eb9bdd78c5..f61fc967de 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -953,4 +953,5 @@ %d events Timeline \"Clear search\" + Optional From 599211a6a1f82f979f50cf19f55a40e930f6cd6d Mon Sep 17 00:00:00 2001 From: andresmr Date: Thu, 15 Feb 2024 08:31:25 +0100 Subject: [PATCH 28/47] [ANDROAPP-5800] clear focus on clear search --- .../searchTrackEntity/SearchTEIViewModel.kt | 27 +++++++++++++------ .../SearchParametersScreen.kt | 1 + 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt index 1d1fef8f86..7a573b9db3 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt @@ -107,9 +107,9 @@ class SearchTEIViewModel( val displayFrontPageList = searchRepository.getProgram(initialProgramUid)?.displayFrontPageList() ?: true val shouldOpenSearch = !displayFrontPageList && - !searchRepository.canCreateInProgramWithoutSearch() && - !searching && - _filtersActive.value == false + !searchRepository.canCreateInProgramWithoutSearch() && + !searching && + _filtersActive.value == false createButtonScrollVisibility.value = if (searching) { true @@ -569,20 +569,20 @@ class SearchTEIViewModel( } hasGlobalResults == null && searchRepository.getProgram(initialProgramUid) != null && - searchRepository.filterQueryForProgram(queryData, null).isNotEmpty() && - searchRepository.filtersApplyOnGlobalSearch() -> { + searchRepository.filterQueryForProgram(queryData, null).isNotEmpty() && + searchRepository.filtersApplyOnGlobalSearch() -> { listOf( SearchResult( SearchResult.SearchResultType.SEARCH_OUTSIDE, searchRepository.getProgram(initialProgramUid)?.displayName(), - ), + ), ) } hasGlobalResults == null && searchRepository.getProgram(initialProgramUid) != null && - searchRepository.trackedEntityTypeFields().isNotEmpty() && - searchRepository.filtersApplyOnGlobalSearch() -> { + searchRepository.trackedEntityTypeFields().isNotEmpty() && + searchRepository.filtersApplyOnGlobalSearch() -> { listOf( SearchResult( type = SearchResult.SearchResultType.UNABLE_SEARCH_OUTSIDE, @@ -787,4 +787,15 @@ class SearchTEIViewModel( // no-op } } + + fun clearFocus() { + val updatedItems = uiState.items.map { + if (it.focused) { + (it as FieldUiModelImpl).copy(focused = false) + } else { + it + } + } + uiState = uiState.copy(items = updatedItems) + } } diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt index 50c7fbb788..002c3a5046 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt @@ -270,6 +270,7 @@ fun initSearchScreen( onClear = { onClear() viewModel.clearQueryData() + viewModel.clearFocus() }, ) } From b02644a237e05efea143327b604a629ebe7292dd Mon Sep 17 00:00:00 2001 From: andresmr Date: Thu, 15 Feb 2024 11:14:25 +0100 Subject: [PATCH 29/47] [ANDROAPP-5800] close keyboard om search and clear --- .../dhis2/usescases/searchTrackEntity/SearchTEActivity.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java index 3eec80c2e2..512181fdb3 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java @@ -1,6 +1,7 @@ package org.dhis2.usescases.searchTrackEntity; import static android.view.View.GONE; +import static org.dhis2.commons.extensions.ViewExtensionsKt.closeKeyboard; import static org.dhis2.usescases.searchTrackEntity.searchparameters.SearchParametersScreenKt.initSearchScreen; import android.annotation.SuppressLint; @@ -359,6 +360,7 @@ private void initSearchParameters() { return Unit.INSTANCE; }, () -> { + closeKeyboard(binding.root); presenter.onClearClick(); return Unit.INSTANCE; } @@ -425,6 +427,9 @@ private void showList() { FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); transaction.replace(R.id.mainComponent, SearchTEList.Companion.get(fromRelationship)).commit(); } + viewModel.getRefreshData().observe(this, refresh -> { + closeKeyboard(binding.root); + }); } private void showMap() { From 371fe00f61dc751560f8a5a68bb7c6c966e45d0e Mon Sep 17 00:00:00 2001 From: andresmr Date: Thu, 15 Feb 2024 12:00:36 +0100 Subject: [PATCH 30/47] [ANDROAPP-5800] close empty fields when clear search --- .../searchTrackEntity/SearchTEIViewModel.kt | 18 +++--- .../SearchParametersScreen.kt | 9 +-- .../provider/ParameterSelectorItemProvider.kt | 56 ++++++++++++------- .../SearchTEIViewModelTest.kt | 8 +-- gradle/libs.versions.toml | 2 +- 5 files changed, 54 insertions(+), 39 deletions(-) diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt index 7a573b9db3..5d3f03c54b 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt @@ -107,9 +107,9 @@ class SearchTEIViewModel( val displayFrontPageList = searchRepository.getProgram(initialProgramUid)?.displayFrontPageList() ?: true val shouldOpenSearch = !displayFrontPageList && - !searchRepository.canCreateInProgramWithoutSearch() && - !searching && - _filtersActive.value == false + !searchRepository.canCreateInProgramWithoutSearch() && + !searching && + _filtersActive.value == false createButtonScrollVisibility.value = if (searching) { true @@ -393,7 +393,7 @@ class SearchTEIViewModel( } } - fun onSearchClick() { + fun onSearch() { searchRepository.clearFetchedList() performSearch() } @@ -569,20 +569,20 @@ class SearchTEIViewModel( } hasGlobalResults == null && searchRepository.getProgram(initialProgramUid) != null && - searchRepository.filterQueryForProgram(queryData, null).isNotEmpty() && - searchRepository.filtersApplyOnGlobalSearch() -> { + searchRepository.filterQueryForProgram(queryData, null).isNotEmpty() && + searchRepository.filtersApplyOnGlobalSearch() -> { listOf( SearchResult( SearchResult.SearchResultType.SEARCH_OUTSIDE, searchRepository.getProgram(initialProgramUid)?.displayName(), - ), + ), ) } hasGlobalResults == null && searchRepository.getProgram(initialProgramUid) != null && - searchRepository.trackedEntityTypeFields().isNotEmpty() && - searchRepository.filtersApplyOnGlobalSearch() -> { + searchRepository.trackedEntityTypeFields().isNotEmpty() && + searchRepository.filtersApplyOnGlobalSearch() -> { listOf( SearchResult( type = SearchResult.SearchResultType.UNABLE_SEARCH_OUTSIDE, diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt index 002c3a5046..d50518e940 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt @@ -61,7 +61,7 @@ fun SearchParametersScreen( orgUnitScope: OrgUnitSelectorScope, label: String, ) -> Unit, - onSearchClick: () -> Unit, + onSearch: () -> Unit, onClear: () -> Unit, ) { val scaffoldState = rememberScaffoldState() @@ -204,7 +204,8 @@ fun SearchParametersScreen( style = ButtonStyle.FILLED, text = resourceManager.getString(R.string.search), ) { - onSearchClick() + focusManager.clearFocus() + onSearch() } } } @@ -237,7 +238,7 @@ fun SearchFormPreview() { ), intentHandler = {}, onShowOrgUnit = { _, _, _, _ -> }, - onSearchClick = {}, + onSearch = {}, onClear = {}, ) } @@ -264,7 +265,7 @@ fun initSearchScreen( SearchParametersScreen( resourceManager = resources, uiState = viewModel.uiState, - onSearchClick = viewModel::onSearchClick, + onSearch = viewModel::onSearch, intentHandler = viewModel::onParameterIntent, onShowOrgUnit = onShowOrgUnit, onClear = { diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt index cb595aa67e..fabc2fba0c 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt @@ -1,17 +1,13 @@ package org.dhis2.usescases.searchTrackEntity.searchparameters.provider +import androidx.compose.material.Icon import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.AddCircleOutline -import androidx.compose.material.icons.outlined.QrCode import androidx.compose.material.icons.outlined.QrCode2 import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusDirection import androidx.compose.ui.focus.FocusManager -import androidx.compose.ui.graphics.vector.ImageVector import org.dhis2.R import org.dhis2.commons.resources.ResourceManager import org.dhis2.form.model.FieldUiModel @@ -20,6 +16,8 @@ import org.dhis2.form.ui.provider.inputfield.FieldProvider import org.hisp.dhis.android.core.common.ValueType import org.hisp.dhis.mobile.ui.designsystem.component.InputStyle import org.hisp.dhis.mobile.ui.designsystem.component.parameter.model.ParameterSelectorItemModel +import org.hisp.dhis.mobile.ui.designsystem.resource.provideDHIS2Icon +import org.hisp.dhis.mobile.ui.designsystem.theme.SurfaceColor @Composable fun provideParameterSelectorItem( @@ -28,20 +26,16 @@ fun provideParameterSelectorItem( fieldUiModel: FieldUiModel, callback: FieldUiModel.Callback, ): ParameterSelectorItemModel { - val status by remember(fieldUiModel.focused) { - mutableStateOf( - if (fieldUiModel.focused) { - ParameterSelectorItemModel.Status.FOCUSED - } else if (fieldUiModel.value.isNullOrEmpty()) { - ParameterSelectorItemModel.Status.CLOSED - } else { - ParameterSelectorItemModel.Status.UNFOCUSED - }, - ) + val status = if (fieldUiModel.focused) { + ParameterSelectorItemModel.Status.FOCUSED + } else if (fieldUiModel.value.isNullOrEmpty()) { + ParameterSelectorItemModel.Status.CLOSED + } else { + ParameterSelectorItemModel.Status.UNFOCUSED } return ParameterSelectorItemModel( - icon = provideIcon(fieldUiModel.valueType, fieldUiModel.renderingType), + icon = { ProvideIcon(fieldUiModel.valueType, fieldUiModel.renderingType) }, label = fieldUiModel.label, helper = resources.getString(R.string.optional), inputField = { @@ -63,20 +57,40 @@ fun provideParameterSelectorItem( ) } -private fun provideIcon(valueType: ValueType?, renderingType: UiRenderType?): ImageVector = +@Composable +private fun ProvideIcon(valueType: ValueType?, renderingType: UiRenderType?) = when (valueType) { ValueType.TEXT -> { when (renderingType) { UiRenderType.QR_CODE, UiRenderType.GS1_DATAMATRIX -> { - Icons.Outlined.QrCode2 + Icon( + imageVector = Icons.Outlined.QrCode2, + contentDescription = "Icon Button", + tint = SurfaceColor.Primary, + ) } + UiRenderType.BAR_CODE -> { - Icons.Outlined.QrCode + Icon( + painter = provideDHIS2Icon("material_barcode_scanner"), + contentDescription = "Icon Button", + tint = SurfaceColor.Primary, + ) } + else -> { - Icons.Outlined.AddCircleOutline + Icon( + imageVector = Icons.Outlined.AddCircleOutline, + contentDescription = "Icon Button", + tint = SurfaceColor.Primary, + ) } } } - else -> Icons.Outlined.AddCircleOutline + + else -> Icon( + imageVector = Icons.Outlined.AddCircleOutline, + contentDescription = "Icon Button", + tint = SurfaceColor.Primary, + ) } diff --git a/app/src/test/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModelTest.kt b/app/src/test/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModelTest.kt index 74141a674d..ae949453e2 100644 --- a/app/src/test/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModelTest.kt +++ b/app/src/test/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModelTest.kt @@ -267,7 +267,7 @@ class SearchTEIViewModelTest { @Test fun `Should use callback to perform min attributes warning`() = runTest { setCurrentProgram(testingProgram(displayFrontPageList = false)) - viewModel.onSearchClick() + viewModel.onSearch() viewModel.uiState.shouldShowMinAttributeWarning.test { assertTrue(awaitItem()) } @@ -285,7 +285,7 @@ class SearchTEIViewModelTest { valueType = ValueType.TEXT, ), ) - viewModel.onSearchClick() + viewModel.onSearch() assertTrue(viewModel.refreshData.value != null) } @@ -321,7 +321,7 @@ class SearchTEIViewModelTest { valueType = ValueType.TEXT, ), ) - viewModel.onSearchClick() + viewModel.onSearch() testingDispatcher.scheduler.advanceUntilIdle() @@ -663,7 +663,7 @@ class SearchTEIViewModelTest { ) viewModel.setListScreen() viewModel.setSearchScreen() - viewModel.onSearchClick() + viewModel.onSearch() testingDispatcher.scheduler.advanceUntilIdle() } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 954842e9c8..c8ea9b5bcc 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,7 +10,7 @@ kotlin = '1.9.21' hilt = '2.47' hiltCompiler = '1.0.0' jacoco = '0.8.10' -designSystem = "0.2-20240214.150049-22" +designSystem = "0.2-20240215.095352-24" dhis2sdk = "1.10.0-20240207.110936-11" ruleEngine = "2.1.9" appcompat = "1.6.1" From f5eba04c469e2aa3e974cdd7d37c89ed95f0a1b7 Mon Sep 17 00:00:00 2001 From: andresmr Date: Thu, 15 Feb 2024 12:24:44 +0100 Subject: [PATCH 31/47] [ANDROAPP-5800] request focus when item is opened --- .../provider/ParameterSelectorItemProvider.kt | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt index fabc2fba0c..700299b36f 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt @@ -5,9 +5,13 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.AddCircleOutline import androidx.compose.material.icons.outlined.QrCode2 import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusDirection import androidx.compose.ui.focus.FocusManager +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester import org.dhis2.R import org.dhis2.commons.resources.ResourceManager import org.dhis2.form.model.FieldUiModel @@ -26,6 +30,8 @@ fun provideParameterSelectorItem( fieldUiModel: FieldUiModel, callback: FieldUiModel.Callback, ): ParameterSelectorItemModel { + val focusRequester = remember { FocusRequester() } + val status = if (fieldUiModel.focused) { ParameterSelectorItemModel.Status.FOCUSED } else if (fieldUiModel.value.isNullOrEmpty()) { @@ -34,13 +40,20 @@ fun provideParameterSelectorItem( ParameterSelectorItemModel.Status.UNFOCUSED } + LaunchedEffect(key1 = status) { + if (status == ParameterSelectorItemModel.Status.FOCUSED) { + focusRequester.requestFocus() + } + } + return ParameterSelectorItemModel( icon = { ProvideIcon(fieldUiModel.valueType, fieldUiModel.renderingType) }, label = fieldUiModel.label, helper = resources.getString(R.string.optional), inputField = { FieldProvider( - modifier = Modifier, + modifier = Modifier + .focusRequester(focusRequester), inputStyle = InputStyle.ParameterInputStyle(), fieldUiModel = fieldUiModel, uiEventHandler = callback::recyclerViewUiEvents, From 73379df1e32740f7bcdc3b21f0fd39a086861e75 Mon Sep 17 00:00:00 2001 From: andresmr Date: Thu, 15 Feb 2024 12:34:00 +0100 Subject: [PATCH 32/47] [ANDROAPP-5800] move focus to next item --- .../searchparameters/SearchParametersScreen.kt | 6 +++++- .../provider/ParameterSelectorItemProvider.kt | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt index d50518e940..bdbc4aaf64 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt @@ -164,7 +164,7 @@ fun SearchParametersScreen( .weight(1F) .verticalScroll(rememberScrollState()), ) { - uiState.items.forEach { fieldUiModel -> + uiState.items.forEachIndexed { index, fieldUiModel -> fieldUiModel.setCallback(callback) ParameterSelectorItem( modifier = Modifier @@ -174,6 +174,10 @@ fun SearchParametersScreen( focusManager = focusManager, fieldUiModel = fieldUiModel, callback = callback, + onNextClicked = { + val nextItemIndex = (index + 1) % uiState.items.size + uiState.items[nextItemIndex].onItemClick() + }, ), ) } diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt index 700299b36f..d4976759c1 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt @@ -8,7 +8,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.remember import androidx.compose.ui.Modifier -import androidx.compose.ui.focus.FocusDirection import androidx.compose.ui.focus.FocusManager import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester @@ -29,6 +28,7 @@ fun provideParameterSelectorItem( focusManager: FocusManager, fieldUiModel: FieldUiModel, callback: FieldUiModel.Callback, + onNextClicked: () -> Unit, ): ParameterSelectorItemModel { val focusRequester = remember { FocusRequester() } @@ -60,7 +60,7 @@ fun provideParameterSelectorItem( intentHandler = callback::intent, resources = resources, focusManager = focusManager, - onNextClicked = { focusManager.moveFocus(FocusDirection.Down) }, + onNextClicked = onNextClicked, ) }, status = status, From 3b2699d44bfb3309115cd94cee3503ec918d4cb6 Mon Sep 17 00:00:00 2001 From: andresmr Date: Fri, 16 Feb 2024 10:38:41 +0100 Subject: [PATCH 33/47] [ANDROAPP-5800] enable disable search button on empty parameters --- .../org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt | 1 + .../searchTrackEntity/searchparameters/SearchParametersScreen.kt | 1 + .../searchparameters/model/SearchParametersUiState.kt | 1 + 3 files changed, 3 insertions(+) diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt index 5d3f03c54b..8d7d84451a 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt @@ -269,6 +269,7 @@ class SearchTEIViewModel( ), ) } + uiState = uiState.copy(searchEnabled = queryData.isNotEmpty()) } fun fetchListResults(onPagedListReady: (Flow>?) -> Unit) { diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt index bdbc4aaf64..2ad3b9241e 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt @@ -201,6 +201,7 @@ fun SearchParametersScreen( } Button( + enabled = uiState.searchEnabled, modifier = Modifier .fillMaxWidth() .padding(16.dp, 8.dp, 16.dp, 8.dp) diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParametersUiState.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParametersUiState.kt index 9e12fee398..1798f5fab7 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParametersUiState.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParametersUiState.kt @@ -8,6 +8,7 @@ data class SearchParametersUiState( val items: List = listOf(), val minAttributesMessage: String? = null, private val _shouldShowMinAttributeWarning: MutableSharedFlow = MutableSharedFlow(), + val searchEnabled: Boolean = false, ) { val shouldShowMinAttributeWarning: SharedFlow = _shouldShowMinAttributeWarning From 0ba6231e3cef1709a79d55f64ca7ee67a3d9c795 Mon Sep 17 00:00:00 2001 From: andresmr Date: Tue, 20 Feb 2024 10:16:50 +0100 Subject: [PATCH 34/47] [ANDROAPP-5805] clear search button visibility --- .../searchTrackEntity/SearchTEIViewModel.kt | 1 + .../SearchParametersScreen.kt | 33 ++++++++++--------- .../model/SearchParametersUiState.kt | 1 + .../provider/ParameterSelectorItemProvider.kt | 19 ++++++++++- app/src/main/res/values/strings.xml | 2 +- gradle/libs.versions.toml | 2 +- 6 files changed, 40 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt index 8d7d84451a..2b85463535 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt @@ -403,6 +403,7 @@ class SearchTEIViewModel( viewModelScope.launch { if (canPerformSearch()) { searching = queryData.isNotEmpty() + uiState = uiState.copy(clearSearchEnabled = queryData.isNotEmpty()) when (_screenState.value?.screenState) { SearchScreenState.LIST -> { SearchIdlingResourceSingleton.increment() diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt index 2ad3b9241e..a81e6bf1af 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt @@ -182,21 +182,24 @@ fun SearchParametersScreen( ) } - Button( - modifier = Modifier - .align(Alignment.CenterHorizontally), - style = ButtonStyle.TEXT, - text = resourceManager.getString(R.string.clear_search), - icon = { - Icon( - imageVector = Icons.Outlined.Cancel, - contentDescription = resourceManager.getString(R.string.clear_search), - tint = SurfaceColor.Primary, - ) - }, - ) { - focusManager.clearFocus() - onClear() + if (uiState.clearSearchEnabled) { + Button( + modifier = Modifier + .align(Alignment.CenterHorizontally) + .padding(16.dp, 24.dp, 16.dp, 8.dp), + style = ButtonStyle.TEXT, + text = resourceManager.getString(R.string.clear_search), + icon = { + Icon( + imageVector = Icons.Outlined.Cancel, + contentDescription = resourceManager.getString(R.string.clear_search), + tint = SurfaceColor.Primary, + ) + }, + ) { + focusManager.clearFocus() + onClear() + } } } diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParametersUiState.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParametersUiState.kt index 1798f5fab7..5ef3bff7d0 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParametersUiState.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParametersUiState.kt @@ -9,6 +9,7 @@ data class SearchParametersUiState( val minAttributesMessage: String? = null, private val _shouldShowMinAttributeWarning: MutableSharedFlow = MutableSharedFlow(), val searchEnabled: Boolean = false, + val clearSearchEnabled: Boolean = false, ) { val shouldShowMinAttributeWarning: SharedFlow = _shouldShowMinAttributeWarning diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt index d4976759c1..81af569502 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/provider/ParameterSelectorItemProvider.kt @@ -15,6 +15,7 @@ import org.dhis2.R import org.dhis2.commons.resources.ResourceManager import org.dhis2.form.model.FieldUiModel import org.dhis2.form.model.UiRenderType +import org.dhis2.form.ui.event.RecyclerViewUiEvents import org.dhis2.form.ui.provider.inputfield.FieldProvider import org.hisp.dhis.android.core.common.ValueType import org.hisp.dhis.mobile.ui.designsystem.component.InputStyle @@ -65,11 +66,27 @@ fun provideParameterSelectorItem( }, status = status, onExpand = { - fieldUiModel.onItemClick() + performOnExpandActions(fieldUiModel, callback) }, ) } +private fun performOnExpandActions(fieldUiModel: FieldUiModel, callback: FieldUiModel.Callback) { + fieldUiModel.onItemClick() + + if (fieldUiModel.renderingType == UiRenderType.QR_CODE || + fieldUiModel.renderingType == UiRenderType.BAR_CODE + ) { + callback.recyclerViewUiEvents( + RecyclerViewUiEvents.ScanQRCode( + uid = fieldUiModel.uid, + optionSet = fieldUiModel.optionSet, + renderingType = fieldUiModel.renderingType, + ), + ) + } +} + @Composable private fun ProvideIcon(valueType: ValueType?, renderingType: UiRenderType?) = when (valueType) { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f61fc967de..ade5acf986 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -952,6 +952,6 @@ event %d events Timeline - \"Clear search\" + Clear search Optional diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c8ea9b5bcc..fd089ec667 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,7 @@ minSdk = "21" vCode = "130" vName = "2.10-DEV" kotlinCompilerExtensionVersion = "1.5.6" -gradle = "8.2.0" +gradle = "8.2.2" kotlin = '1.9.21' hilt = '2.47' hiltCompiler = '1.0.0' From 006d1640a1fa3fde3f739eed39980b5db4c88d29 Mon Sep 17 00:00:00 2001 From: andresmr Date: Tue, 20 Feb 2024 11:01:02 +0100 Subject: [PATCH 35/47] [ANDROAPP-5805] Adapt top bar when searching --- .../searchTrackEntity/ui/SearchScreenConfigurator.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/ui/SearchScreenConfigurator.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/ui/SearchScreenConfigurator.kt index 94770bf982..d4ac965106 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/ui/SearchScreenConfigurator.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/ui/SearchScreenConfigurator.kt @@ -72,6 +72,8 @@ class SearchScreenConfigurator( } private fun openFilters() { + binding.programSpinner.visibility = View.VISIBLE + binding.title.visibility = View.GONE binding.filterRecyclerLayout.visibility = View.VISIBLE binding.searchContainer.visibility = View.GONE if (isPortrait()) binding.navigationBar.hide() @@ -80,6 +82,8 @@ class SearchScreenConfigurator( } fun closeBackdrop() { + binding.programSpinner.visibility = View.VISIBLE + binding.title.visibility = View.GONE binding.filterRecyclerLayout.visibility = View.GONE binding.searchContainer.visibility = View.GONE if (isPortrait()) binding.navigationBar.show() @@ -89,6 +93,8 @@ class SearchScreenConfigurator( private fun openSearch() { binding.filterRecyclerLayout.visibility = View.GONE + binding.programSpinner.visibility = View.GONE + binding.title.visibility = View.VISIBLE binding.searchContainer.visibility = View.VISIBLE if (isPortrait()) binding.navigationBar.hide() filterIsOpenCallback(false) From e7f2a8e8bbe9728dd51499ddd58a709c322be250 Mon Sep 17 00:00:00 2001 From: andresmr Date: Tue, 20 Feb 2024 11:21:59 +0100 Subject: [PATCH 36/47] [ANDROAPP-5805] Add replay to mutable search flow in order to emit the first value --- .../searchparameters/model/SearchParametersUiState.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParametersUiState.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParametersUiState.kt index 5ef3bff7d0..01692d8488 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParametersUiState.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParametersUiState.kt @@ -7,7 +7,9 @@ import org.dhis2.form.model.FieldUiModel data class SearchParametersUiState( val items: List = listOf(), val minAttributesMessage: String? = null, - private val _shouldShowMinAttributeWarning: MutableSharedFlow = MutableSharedFlow(), + private val _shouldShowMinAttributeWarning: MutableSharedFlow = MutableSharedFlow( + replay = Int.MAX_VALUE, + ), val searchEnabled: Boolean = false, val clearSearchEnabled: Boolean = false, ) { From d88ef08cf74cb2b9cf617eab32cb62027d8aa98d Mon Sep 17 00:00:00 2001 From: andresmr Date: Tue, 20 Feb 2024 14:04:21 +0100 Subject: [PATCH 37/47] [ANDROAPP-5805] Remove connected rounded corners on landscape --- .../java/org/dhis2/bindings/Bindings.java | 114 +----------------- .../java/org/dhis2/bindings/ViewExtensions.kt | 56 ++++----- .../searchTrackEntity/SearchTEActivity.java | 7 +- .../SearchParametersScreen.kt | 19 ++- 4 files changed, 47 insertions(+), 149 deletions(-) diff --git a/app/src/main/java/org/dhis2/bindings/Bindings.java b/app/src/main/java/org/dhis2/bindings/Bindings.java index dac7553a33..85d4ca5b8b 100644 --- a/app/src/main/java/org/dhis2/bindings/Bindings.java +++ b/app/src/main/java/org/dhis2/bindings/Bindings.java @@ -1,41 +1,28 @@ package org.dhis2.bindings; -import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; -import android.graphics.Color; -import android.graphics.PorterDuff; -import android.graphics.Typeface; import android.graphics.drawable.AnimatedVectorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; import android.os.Build; import android.util.TypedValue; import android.view.View; -import android.widget.AdapterView; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import androidx.appcompat.content.res.AppCompatResources; -import androidx.appcompat.widget.AppCompatSpinner; import androidx.core.content.ContextCompat; import androidx.core.graphics.drawable.DrawableCompat; import androidx.databinding.BindingAdapter; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import com.google.android.material.floatingactionbutton.FloatingActionButton; - import org.dhis2.R; import org.dhis2.commons.animations.ViewAnimationsKt; -import org.dhis2.commons.filters.CatOptionComboFilter; -import org.dhis2.commons.data.ProgramEventViewModel; -import org.dhis2.utils.CatComboAdapter; import org.dhis2.utils.DateUtils; import org.dhis2.utils.NetworkUtils; -import org.hisp.dhis.android.core.category.CategoryOptionCombo; -import org.hisp.dhis.android.core.common.State; import org.hisp.dhis.android.core.enrollment.Enrollment; import org.hisp.dhis.android.core.enrollment.EnrollmentStatus; import org.hisp.dhis.android.core.event.Event; @@ -44,9 +31,7 @@ import org.hisp.dhis.android.core.program.Program; import org.hisp.dhis.android.core.program.ProgramStage; -import java.util.ArrayList; import java.util.Date; -import java.util.List; public class Bindings { @@ -55,10 +40,8 @@ public class Bindings { public static void setDrawableEnd(TextView textView, Drawable drawable) { textView.setCompoundDrawablesWithIntrinsicBounds(null, null, drawable, null); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - if (drawable instanceof AnimatedVectorDrawable) - ((AnimatedVectorDrawable) drawable).start(); - } + if (drawable instanceof AnimatedVectorDrawable) + ((AnimatedVectorDrawable) drawable).start(); } @BindingAdapter(value = {"initGrid", "spanCount"}, requireAll = false) @@ -73,19 +56,6 @@ public static void setLayoutManager(RecyclerView recyclerView, boolean horizonta } - @BindingAdapter("spanSize") - public static void setSpanSize(RecyclerView recyclerView, boolean setSpanSize) { - if (recyclerView.getLayoutManager() instanceof GridLayoutManager) { - ((GridLayoutManager) recyclerView.getLayoutManager()).setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { - @Override - public int getSpanSize(int position) { - int itemViewType = recyclerView.getAdapter().getItemViewType(position); - return (itemViewType == 4 || itemViewType == 8) ? 2 : 1; - } - }); - } - } - @BindingAdapter("enrolmentIcon") public static void setEnrolmentIcon(ImageView view, EnrollmentStatus status) { Drawable lock; @@ -133,27 +103,6 @@ public static void setEnrolmentText(TextView view, EnrollmentStatus status) { view.setText(text); } - public static String enrollmentText(Context context, EnrollmentStatus status) { - String text; - if (status == null) - status = EnrollmentStatus.ACTIVE; - switch (status) { - case ACTIVE: - text = context.getString(R.string.event_open); - break; - case COMPLETED: - text = context.getString(R.string.completed); - break; - case CANCELLED: - text = context.getString(R.string.cancelled); - break; - default: - text = context.getString(R.string.read_only); - break; - } - return text; - } - @BindingAdapter(value = {"eventStatusIcon", "enrollmentStatusIcon", "eventProgramStage", "eventProgram"}, requireAll = false) public static void setEventIcon(ImageView view, Event event, Enrollment enrollment, ProgramStage eventProgramStage, Program program) { if (event != null) { @@ -212,11 +161,9 @@ public static void setImageBackground(ImageView imageView, Drawable drawable) { TypedArray a = imageView.getContext().obtainStyledAttributes(typedValue.data, new int[]{R.attr.colorPrimaryDark}); TypedArray b = imageView.getContext().obtainStyledAttributes(typedValue.data, new int[]{R.attr.colorPrimaryLight}); int colorPrimaryDark = a.getColor(0, 0); - int colorPrimaryLight = b.getColor(0, 0); int px = (int) (1 * Resources.getSystem().getDisplayMetrics().density); ((GradientDrawable) drawable.mutate()).setStroke(px, colorPrimaryDark); - //((GradientDrawable) drawable.mutate()).setColor(colorPrimaryLight); imageView.setBackground(drawable); @@ -253,36 +200,6 @@ public static void setNetworkVisibility(View view, boolean checkNetwork) { } } - @BindingAdapter(value = {"catComboAdapterData", "catComboAdapterTitle"}) - public static void setCatComboAdapter(AppCompatSpinner spinner, List catComboAdapterData, String catComboAdapterTitle) { - CatComboAdapter spinnerAdapter = new CatComboAdapter(spinner.getContext(), - R.layout.spinner_layout, - R.id.spinner_text, - catComboAdapterData != null ? catComboAdapterData : new ArrayList<>(), - catComboAdapterTitle, - R.color.white_faf); - - spinner.setAdapter(spinnerAdapter); - } - - @BindingAdapter("onCatComboSelected") - public static void setOnCatComboSelected(AppCompatSpinner spinner, CatOptionComboFilter itemFilter) { - spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView adapterView, View view, int position, long l) { - if (position != 0) { - itemFilter.selectCatOptionCombo(position - 1); - spinner.setSelection(0); - } - } - - @Override - public void onNothingSelected(AdapterView adapterView) { - - } - }); - } - @BindingAdapter(value = {"dataSetStatus"}) public static void setDataSetStatusIcon(ImageView view, Boolean isComplete) { int drawableResource = isComplete ? R.drawable.ic_event_status_complete : R.drawable.ic_event_status_open; @@ -295,38 +212,11 @@ public static void setDataSetStatusIcon(ImageView view, Boolean isComplete) { view.setTag(drawableResource); } - @BindingAdapter("textStyle") - public static void setTextStyle(TextView textView, int style) { - switch (style) { - case Typeface.BOLD: - textView.setTypeface(null, Typeface.BOLD); - break; - default: - textView.setTypeface(null, Typeface.NORMAL); - break; - - } - } - @BindingAdapter("clipCorners") public static void setClipCorners(View view, int cornerRadiusInDp) { ViewExtensionsKt.clipWithRoundedCorners(view, ExtensionsKt.getDp(cornerRadiusInDp)); } - @BindingAdapter("clipAllCorners") - public static void setAllClipCorners(View view, int cornerRadiusInDp) { - ViewExtensionsKt.clipWithAllRoundedCorners(view, ExtensionsKt.getDp(cornerRadiusInDp)); - } - - @BindingAdapter("fabVisibility") - public static void setFabVisibility(FloatingActionButton fab, boolean isVisible) { - if (isVisible) { - fab.show(); - } else { - fab.hide(); - } - } - @BindingAdapter("viewVisibility") public static void setViewVisibility(View view, boolean isVisible) { if (isVisible) { diff --git a/app/src/main/java/org/dhis2/bindings/ViewExtensions.kt b/app/src/main/java/org/dhis2/bindings/ViewExtensions.kt index 0ccb1d7069..90318177fb 100644 --- a/app/src/main/java/org/dhis2/bindings/ViewExtensions.kt +++ b/app/src/main/java/org/dhis2/bindings/ViewExtensions.kt @@ -1,8 +1,6 @@ package org.dhis2.bindings import android.graphics.Outline -import android.os.Build -import android.util.TypedValue import android.view.View import android.view.ViewOutlineProvider import android.view.inputmethod.EditorInfo @@ -11,15 +9,7 @@ import android.widget.ListPopupWindow import android.widget.Spinner import android.widget.TextView import com.google.android.material.floatingactionbutton.FloatingActionButton -import com.tbuonomo.viewpagerdotsindicator.R import org.dhis2.commons.extensions.closeKeyboard -import java.lang.Exception - -fun View.getThemePrimaryColor(): Int { - val value = TypedValue() - context.theme.resolveAttribute(R.attr.colorPrimary, value, true) - return value.data -} fun View.onFocusRemoved(onFocusRemovedCallback: () -> Unit) { setOnFocusChangeListener { view, hasFocus -> @@ -42,37 +32,33 @@ fun TextView.clearFocusOnDone() { } fun View.clipWithRoundedCorners(curvedRadio: Int = 16.dp) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - outlineProvider = object : ViewOutlineProvider() { - override fun getOutline(view: View, outline: Outline) { - outline.setRoundRect( - 0, - 0, - view.width, - view.height + curvedRadio, - curvedRadio.toFloat(), - ) - } + outlineProvider = object : ViewOutlineProvider() { + override fun getOutline(view: View, outline: Outline) { + outline.setRoundRect( + 0, + 0, + view.width, + view.height + curvedRadio, + curvedRadio.toFloat(), + ) } - clipToOutline = true } + clipToOutline = true } -fun View.clipWithAllRoundedCorners(curvedRadio: Int = 16.dp) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - outlineProvider = object : ViewOutlineProvider() { - override fun getOutline(view: View, outline: Outline) { - outline.setRoundRect( - 0, - 0, - view.width, - view.height, - curvedRadio.toFloat(), - ) - } +fun View.clipWithTopRightRoundedCorner(curvedRadio: Int = 16.dp) { + outlineProvider = object : ViewOutlineProvider() { + override fun getOutline(view: View, outline: Outline) { + outline.setRoundRect( + 0 - curvedRadio, + 0, + view.width, + view.height + curvedRadio, + curvedRadio.toFloat(), + ) } - clipToOutline = true } + clipToOutline = true } fun Spinner.overrideHeight(desiredHeight: Int) { diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java index 512181fdb3..2cf88b2dd3 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java @@ -173,7 +173,12 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { } binding.setPresenter(presenter); binding.setTotalFilters(FilterManager.getInstance().getTotalFilters()); - ViewExtensionsKt.clipWithRoundedCorners(binding.mainComponent, ExtensionsKt.getDp(16)); + + if (OrientationUtilsKt.isPortrait()) { + ViewExtensionsKt.clipWithRoundedCorners(binding.mainComponent, ExtensionsKt.getDp(16)); + } else { + ViewExtensionsKt.clipWithTopRightRoundedCorner(binding.mainComponent, ExtensionsKt.getDp(16)); + } binding.filterRecyclerLayout.setAdapter(filtersAdapter); diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt index a81e6bf1af..93455d50de 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt @@ -1,5 +1,6 @@ package org.dhis2.usescases.searchTrackEntity.searchparameters +import android.content.res.Configuration import androidx.activity.compose.rememberLauncherForActivityResult import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column @@ -7,6 +8,8 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.CornerSize +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll import androidx.compose.material.Icon import androidx.compose.material.Scaffold @@ -22,6 +25,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.testTag @@ -47,6 +51,7 @@ import org.hisp.dhis.android.core.common.ValueType import org.hisp.dhis.mobile.ui.designsystem.component.Button import org.hisp.dhis.mobile.ui.designsystem.component.ButtonStyle import org.hisp.dhis.mobile.ui.designsystem.component.parameter.ParameterSelectorItem +import org.hisp.dhis.mobile.ui.designsystem.theme.Radius import org.hisp.dhis.mobile.ui.designsystem.theme.Shape import org.hisp.dhis.mobile.ui.designsystem.theme.SurfaceColor @@ -68,6 +73,7 @@ fun SearchParametersScreen( val snackBarHostState = scaffoldState.snackbarHostState val coroutineScope = rememberCoroutineScope() val focusManager = LocalFocusManager.current + val configuration = LocalConfiguration.current val qrScanLauncher = rememberLauncherForActivityResult( contract = ScanContract(), @@ -138,6 +144,17 @@ fun SearchParametersScreen( } } + val backgroundShape = when (configuration.orientation) { + Configuration.ORIENTATION_LANDSCAPE -> RoundedCornerShape( + topStart = CornerSize(Radius.L), + topEnd = CornerSize(Radius.NoRounding), + bottomEnd = CornerSize(Radius.NoRounding), + bottomStart = CornerSize(Radius.NoRounding), + ) + + else -> Shape.LargeTop + } + Scaffold( backgroundColor = Color.Transparent, scaffoldState = scaffoldState, @@ -156,7 +173,7 @@ fun SearchParametersScreen( Column( modifier = Modifier .fillMaxSize() - .background(color = Color.White, shape = Shape.LargeTop) + .background(color = Color.White, shape = backgroundShape) .padding(it), ) { Column( From 7655b381141a8365b7f42cd6f914cafb1befb495 Mon Sep 17 00:00:00 2001 From: andresmr Date: Tue, 20 Feb 2024 16:14:46 +0100 Subject: [PATCH 38/47] [ANDROAPP-5805] update ProgramEventTest to remove check on org unit when is not visible --- .../programevent/ProgramEventTest.kt | 10 +++++----- .../programevent/robot/ProgramEventsRobot.kt | 19 +++++++++---------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/app/src/androidTest/java/org/dhis2/usescases/programevent/ProgramEventTest.kt b/app/src/androidTest/java/org/dhis2/usescases/programevent/ProgramEventTest.kt index 6ec2122d91..d982cf5c6b 100644 --- a/app/src/androidTest/java/org/dhis2/usescases/programevent/ProgramEventTest.kt +++ b/app/src/androidTest/java/org/dhis2/usescases/programevent/ProgramEventTest.kt @@ -67,7 +67,7 @@ class ProgramEventTest : BaseTest() { prepareProgramAndLaunchActivity(atenatalCare) programEventsRobot { - clickOnEvent(eventDate, eventOrgUnit) + clickOnEvent(eventDate) } eventRobot { @@ -84,7 +84,7 @@ class ProgramEventTest : BaseTest() { disableRecyclerViewAnimations() programEventsRobot { - clickOnEvent(eventDate, eventOrgUnit) + clickOnEvent(eventDate) } eventRobot { @@ -95,7 +95,7 @@ class ProgramEventTest : BaseTest() { programEventsRobot { checkEventIsComplete(eventDate, eventOrgUnit) - clickOnEvent(eventDate, eventOrgUnit) + clickOnEvent(eventDate) } eventRobot { @@ -118,7 +118,7 @@ class ProgramEventTest : BaseTest() { prepareProgramAndLaunchActivity(atenatalCare) programEventsRobot { - clickOnEvent(eventDate, eventOrgUnit) + clickOnEvent(eventDate) } eventRobot { clickOnDetails() @@ -135,7 +135,7 @@ class ProgramEventTest : BaseTest() { disableRecyclerViewAnimations() programEventsRobot { - clickOnEvent(eventDate, eventOrgUnit) + clickOnEvent(eventDate) } eventRobot { openMenuMoreOptions() diff --git a/app/src/androidTest/java/org/dhis2/usescases/programevent/robot/ProgramEventsRobot.kt b/app/src/androidTest/java/org/dhis2/usescases/programevent/robot/ProgramEventsRobot.kt index db3e8a81cb..fe0876fbb3 100644 --- a/app/src/androidTest/java/org/dhis2/usescases/programevent/robot/ProgramEventsRobot.kt +++ b/app/src/androidTest/java/org/dhis2/usescases/programevent/robot/ProgramEventsRobot.kt @@ -5,7 +5,11 @@ import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.contrib.RecyclerViewActions import androidx.test.espresso.contrib.RecyclerViewActions.actionOnItem -import androidx.test.espresso.matcher.ViewMatchers.* +import androidx.test.espresso.matcher.ViewMatchers.hasDescendant +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.withTagValue +import androidx.test.espresso.matcher.ViewMatchers.withText import org.dhis2.R import org.dhis2.common.BaseRobot import org.dhis2.common.matchers.RecyclerviewMatchers.Companion.hasItem @@ -23,19 +27,14 @@ fun programEventsRobot(programEventsRobot: ProgramEventsRobot.() -> Unit) { class ProgramEventsRobot : BaseRobot() { - fun clickOnEvent(eventDate: String, eventOrgUnit: String) { + fun clickOnEvent(eventDate: String) { onView(withId(R.id.recycler)).perform( RecyclerViewActions.scrollTo( - allOf( - hasDescendant(withText(eventDate)), - hasDescendant(withText(eventOrgUnit)) - ) + hasDescendant(withText(eventDate)), ), actionOnItem( - allOf( - hasDescendant(withText(eventDate)), - hasDescendant(withText(eventOrgUnit)) - ), click() + hasDescendant(withText(eventDate)), + click(), ) ) } From d880fa70866b00c997e4ba9cc7e1c363402f0ced Mon Sep 17 00:00:00 2001 From: andresmr Date: Wed, 21 Feb 2024 12:38:35 +0100 Subject: [PATCH 39/47] [ANDROAPP-5800] configure toolbar for landscape --- .../ui/SearchScreenConfigurator.kt | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/ui/SearchScreenConfigurator.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/ui/SearchScreenConfigurator.kt index d4ac965106..60eb677897 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/ui/SearchScreenConfigurator.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/ui/SearchScreenConfigurator.kt @@ -72,8 +72,10 @@ class SearchScreenConfigurator( } private fun openFilters() { - binding.programSpinner.visibility = View.VISIBLE - binding.title.visibility = View.GONE + if (isPortrait()) { + binding.programSpinner.visibility = View.VISIBLE + binding.title.visibility = View.GONE + } binding.filterRecyclerLayout.visibility = View.VISIBLE binding.searchContainer.visibility = View.GONE if (isPortrait()) binding.navigationBar.hide() @@ -82,8 +84,10 @@ class SearchScreenConfigurator( } fun closeBackdrop() { - binding.programSpinner.visibility = View.VISIBLE - binding.title.visibility = View.GONE + if (isPortrait()) { + binding.programSpinner.visibility = View.VISIBLE + binding.title.visibility = View.GONE + } binding.filterRecyclerLayout.visibility = View.GONE binding.searchContainer.visibility = View.GONE if (isPortrait()) binding.navigationBar.show() @@ -93,8 +97,10 @@ class SearchScreenConfigurator( private fun openSearch() { binding.filterRecyclerLayout.visibility = View.GONE - binding.programSpinner.visibility = View.GONE - binding.title.visibility = View.VISIBLE + if (isPortrait()) { + binding.programSpinner.visibility = View.GONE + binding.title.visibility = View.VISIBLE + } binding.searchContainer.visibility = View.VISIBLE if (isPortrait()) binding.navigationBar.hide() filterIsOpenCallback(false) From 661cdf04103edca849161a76b7049483d2baadc0 Mon Sep 17 00:00:00 2001 From: andresmr Date: Wed, 21 Feb 2024 12:40:16 +0100 Subject: [PATCH 40/47] [ANDROAPP-5800] remove unused program variable --- app/src/main/java/org/dhis2/bindings/Bindings.java | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/org/dhis2/bindings/Bindings.java b/app/src/main/java/org/dhis2/bindings/Bindings.java index 85d4ca5b8b..f61f6b15e4 100644 --- a/app/src/main/java/org/dhis2/bindings/Bindings.java +++ b/app/src/main/java/org/dhis2/bindings/Bindings.java @@ -159,7 +159,6 @@ public static void setImageBackground(ImageView imageView, Drawable drawable) { TypedValue typedValue = new TypedValue(); TypedArray a = imageView.getContext().obtainStyledAttributes(typedValue.data, new int[]{R.attr.colorPrimaryDark}); - TypedArray b = imageView.getContext().obtainStyledAttributes(typedValue.data, new int[]{R.attr.colorPrimaryLight}); int colorPrimaryDark = a.getColor(0, 0); int px = (int) (1 * Resources.getSystem().getDisplayMetrics().density); From 04b1792e579fc9105631e9c48b8db6dca17cde5c Mon Sep 17 00:00:00 2001 From: andresmr Date: Wed, 21 Feb 2024 15:19:08 +0100 Subject: [PATCH 41/47] [ANDROAPP-5800] remove needs force update parameter from Form only used for search --- .../org/dhis2/form/ui/DataEntryAdapter.kt | 22 +++++------------- .../main/java/org/dhis2/form/ui/FormView.kt | 23 ------------------- .../dhis2/form/ui/FormViewFragmentFactory.kt | 2 -- 3 files changed, 6 insertions(+), 41 deletions(-) diff --git a/form/src/main/java/org/dhis2/form/ui/DataEntryAdapter.kt b/form/src/main/java/org/dhis2/form/ui/DataEntryAdapter.kt index 4c6b3ff553..68ded0287c 100644 --- a/form/src/main/java/org/dhis2/form/ui/DataEntryAdapter.kt +++ b/form/src/main/java/org/dhis2/form/ui/DataEntryAdapter.kt @@ -17,7 +17,6 @@ import org.dhis2.form.ui.intent.FormIntent import org.hisp.dhis.android.core.common.ValueType class DataEntryAdapter( - private val searchStyle: Boolean, private val collapsableSections: Boolean, ) : ListAdapter(DataEntryDiff()), @@ -50,21 +49,12 @@ class DataEntryAdapter( override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FormViewHolder { val layoutInflater = - if (searchStyle) { - LayoutInflater.from( - ContextThemeWrapper( - parent.context, - R.style.searchFormInputText, - ), - ) - } else { - LayoutInflater.from( - ContextThemeWrapper( - parent.context, - R.style.formInputText, - ), - ) - } + LayoutInflater.from( + ContextThemeWrapper( + parent.context, + R.style.formInputText, + ), + ) val binding = DataBindingUtil.inflate(layoutInflater, viewType, parent, false) return FormViewHolder(binding) diff --git a/form/src/main/java/org/dhis2/form/ui/FormView.kt b/form/src/main/java/org/dhis2/form/ui/FormView.kt index 4a68235ecf..430953977d 100644 --- a/form/src/main/java/org/dhis2/form/ui/FormView.kt +++ b/form/src/main/java/org/dhis2/form/ui/FormView.kt @@ -110,7 +110,6 @@ class FormView : Fragment() { private var onFocused: (() -> Unit)? = null private var onFinishDataEntry: (() -> Unit)? = null private var onActivityForResult: (() -> Unit)? = null - private var needToForceUpdate: Boolean = false private var completionListener: ((percentage: Float) -> Unit)? = null private var onDataIntegrityCheck: ((result: DataIntegrityCheckResult) -> Unit)? = null private var onFieldItemsRendered: ((fieldsEmpty: Boolean) -> Unit)? = null @@ -350,9 +349,6 @@ class FormView : Fragment() { dataEntryHeaderHelper.checkSectionHeader(recyclerView) } }) - if (needToForceUpdate) { - retainInstance = true - } FormFileProvider.init(contextWrapper.applicationContext) if (useCompose) { @@ -383,7 +379,6 @@ class FormView : Fragment() { FormCountingIdlingResource.increment() dataEntryHeaderHelper.observeHeaderChanges(viewLifecycleOwner) adapter = DataEntryAdapter( - needToForceUpdate, viewModel.areSectionCollapsable(), ) @@ -430,14 +425,6 @@ class FormView : Fragment() { onItemChangeListener?.let { it(rowAction) } } - viewModel.queryData.observe( - viewLifecycleOwner, - ) { rowAction -> - if (needToForceUpdate) { - onItemChangeListener?.let { it(rowAction) } - } - } - viewModel.items.observe( viewLifecycleOwner, ) { items -> @@ -1143,7 +1130,6 @@ class FormView : Fragment() { internal fun setConfiguration( locationProvider: LocationProvider?, - needToForceUpdate: Boolean, completionListener: ((percentage: Float) -> Unit)?, resultDialogUiProvider: EnrollmentResultDialogUiProvider?, actionIconsActivate: Boolean, @@ -1151,7 +1137,6 @@ class FormView : Fragment() { useCompose: Boolean, ) { this.locationProvider = locationProvider - this.needToForceUpdate = needToForceUpdate this.completionListener = completionListener this.resultDialogUiProvider = resultDialogUiProvider this.actionIconsActivate = actionIconsActivate @@ -1182,7 +1167,6 @@ class FormView : Fragment() { private var fragmentManager: FragmentManager? = null private var onItemChangeListener: ((action: RowAction) -> Unit)? = null private var locationProvider: LocationProvider? = null - private var needToForceUpdate: Boolean = false private var onLoadingListener: ((loading: Boolean) -> Unit)? = null private var onFocused: (() -> Unit)? = null private var onActivityForResult: (() -> Unit)? = null @@ -1212,12 +1196,6 @@ class FormView : Fragment() { fun locationProvider(locationProvider: LocationProvider) = apply { this.locationProvider = locationProvider } - /** - * If it's set to true, any change on the list will make update all of it's items. - */ - fun needToForceUpdate(needToForceUpdate: Boolean) = - apply { this.needToForceUpdate = needToForceUpdate } - /** * Sets if loading started or finished to handle loadingfeedback * */ @@ -1276,7 +1254,6 @@ class FormView : Fragment() { FormViewFragmentFactory( locationProvider, onItemChangeListener, - needToForceUpdate, onLoadingListener, onFocused, onFinishDataEntry, diff --git a/form/src/main/java/org/dhis2/form/ui/FormViewFragmentFactory.kt b/form/src/main/java/org/dhis2/form/ui/FormViewFragmentFactory.kt index b850b726ad..50e589e1cf 100644 --- a/form/src/main/java/org/dhis2/form/ui/FormViewFragmentFactory.kt +++ b/form/src/main/java/org/dhis2/form/ui/FormViewFragmentFactory.kt @@ -10,7 +10,6 @@ import org.dhis2.form.ui.provider.EnrollmentResultDialogUiProvider class FormViewFragmentFactory( val locationProvider: LocationProvider?, private val onItemChangeListener: ((action: RowAction) -> Unit)?, - private val needToForceUpdate: Boolean = false, private val onLoadingListener: ((loading: Boolean) -> Unit)?, private val onFocused: (() -> Unit)?, private val onFinishDataEntry: (() -> Unit)?, @@ -37,7 +36,6 @@ class FormViewFragmentFactory( ) setConfiguration( locationProvider = locationProvider, - needToForceUpdate = needToForceUpdate, completionListener = completionListener, resultDialogUiProvider = resultDialogUiProvider, actionIconsActivate = actionIconsActivate, From 8ef5ffd4f538511d098b17595ab7a1ef7374b0be Mon Sep 17 00:00:00 2001 From: andresmr Date: Wed, 21 Feb 2024 15:37:02 +0100 Subject: [PATCH 42/47] [ANDROAPP-5800] Remember ScanContract on SearchParametersScreen --- .../searchparameters/SearchParametersScreen.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt index 93455d50de..e2770c981f 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt @@ -75,8 +75,9 @@ fun SearchParametersScreen( val focusManager = LocalFocusManager.current val configuration = LocalConfiguration.current + val scanContract = remember { ScanContract() } val qrScanLauncher = rememberLauncherForActivityResult( - contract = ScanContract(), + contract = scanContract, ) { result -> result.contents?.let { qrData -> val intent = FormIntent.OnSave( From 9fe4601ac83fe242e18fc36e107349ea77f5cf14 Mon Sep 17 00:00:00 2001 From: andresmr Date: Thu, 22 Feb 2024 11:36:58 +0100 Subject: [PATCH 43/47] [ANDROAPP-5800] Clear focus when closing search --- .../searchTrackEntity/SearchTEIViewModel.kt | 3 +++ .../searchparameters/SearchParametersScreen.kt | 13 +++++++++++++ .../model/SearchParametersUiState.kt | 8 ++++++++ 3 files changed, 24 insertions(+) diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt index 2b85463535..d34f39ca27 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt @@ -647,6 +647,9 @@ class SearchTEIViewModel( if (isPortrait && searchOrFilterIsOpen && !searchScreenIsForced) { if (keyBoardIsOpen) closeKeyboardCallback() closeSearchOrFilterCallback() + viewModelScope.launch { + uiState.onBackPressed(true) + } } else if (keyBoardIsOpen) { closeKeyboardCallback() goBackCallback() diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt index e2770c981f..5d0afff4ba 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt @@ -19,6 +19,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Cancel import androidx.compose.material.rememberScaffoldState import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment @@ -68,6 +69,7 @@ fun SearchParametersScreen( ) -> Unit, onSearch: () -> Unit, onClear: () -> Unit, + onClose: () -> Unit, ) { val scaffoldState = rememberScaffoldState() val snackBarHostState = scaffoldState.snackbarHostState @@ -145,6 +147,15 @@ fun SearchParametersScreen( } } + LaunchedEffect(uiState.isOnBackPressed) { + uiState.isOnBackPressed.collectLatest { + if (it) { + focusManager.clearFocus() + onClose() + } + } + } + val backgroundShape = when (configuration.orientation) { Configuration.ORIENTATION_LANDSCAPE -> RoundedCornerShape( topStart = CornerSize(Radius.L), @@ -266,6 +277,7 @@ fun SearchFormPreview() { onShowOrgUnit = { _, _, _, _ -> }, onSearch = {}, onClear = {}, + onClose = {}, ) } @@ -299,6 +311,7 @@ fun initSearchScreen( viewModel.clearQueryData() viewModel.clearFocus() }, + onClose = { viewModel.clearFocus() }, ) } } diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParametersUiState.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParametersUiState.kt index 01692d8488..c842718638 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParametersUiState.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/model/SearchParametersUiState.kt @@ -12,10 +12,18 @@ data class SearchParametersUiState( ), val searchEnabled: Boolean = false, val clearSearchEnabled: Boolean = false, + private val _isOnBackPressed: MutableSharedFlow = MutableSharedFlow( + replay = Int.MAX_VALUE, + ), ) { val shouldShowMinAttributeWarning: SharedFlow = _shouldShowMinAttributeWarning + val isOnBackPressed: SharedFlow = _isOnBackPressed suspend fun updateMinAttributeWarning(showWarning: Boolean) { _shouldShowMinAttributeWarning.emit(showWarning) } + + suspend fun onBackPressed(pressed: Boolean) { + _isOnBackPressed.emit(pressed) + } } From ed4c6ac432ca691d2d8b3a711c3ca6a610eedda4 Mon Sep 17 00:00:00 2001 From: andresmr Date: Thu, 22 Feb 2024 12:13:36 +0100 Subject: [PATCH 44/47] [ANDROAPP-5800] Avoid double updating Age field --- .../java/org/dhis2/form/ui/provider/inputfield/AgeProvider.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/form/src/main/java/org/dhis2/form/ui/provider/inputfield/AgeProvider.kt b/form/src/main/java/org/dhis2/form/ui/provider/inputfield/AgeProvider.kt index 2adb4d7de2..2515c0b188 100644 --- a/form/src/main/java/org/dhis2/form/ui/provider/inputfield/AgeProvider.kt +++ b/form/src/main/java/org/dhis2/form/ui/provider/inputfield/AgeProvider.kt @@ -34,7 +34,7 @@ fun ProvideInputAge( uiEventHandler: (RecyclerViewUiEvents) -> Unit, resources: ResourceManager, ) { - var inputType by remember(fieldUiModel.value) { + var inputType by remember { mutableStateOf( if (!fieldUiModel.value.isNullOrEmpty()) { formatStoredDateToUI(fieldUiModel.value!!).let { From 415218887036479892b145d3b7beab891b690abf Mon Sep 17 00:00:00 2001 From: andresmr Date: Thu, 22 Feb 2024 12:30:12 +0100 Subject: [PATCH 45/47] [ANDROAPP-5800] Not loop on next click when gets the last item --- .../searchparameters/SearchParametersScreen.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt index 5d0afff4ba..a5168b5a42 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/searchparameters/SearchParametersScreen.kt @@ -204,8 +204,10 @@ fun SearchParametersScreen( fieldUiModel = fieldUiModel, callback = callback, onNextClicked = { - val nextItemIndex = (index + 1) % uiState.items.size - uiState.items[nextItemIndex].onItemClick() + val nextIndex = index + 1 + if (nextIndex < uiState.items.size) { + uiState.items[nextIndex].onItemClick() + } }, ), ) From 0f493d0df869e00477ebc7cb93caf640e610eb78 Mon Sep 17 00:00:00 2001 From: andresmr Date: Thu, 22 Feb 2024 13:44:50 +0100 Subject: [PATCH 46/47] [ANDROAPP-5800] set rounded corners on show filters landscape --- .../searchTrackEntity/SearchTEActivity.java | 12 +++++++++--- app/src/main/res/layout-land/activity_search.xml | 1 + 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java index 2cf88b2dd3..3e370828e3 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java @@ -174,10 +174,16 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { binding.setPresenter(presenter); binding.setTotalFilters(FilterManager.getInstance().getTotalFilters()); - if (OrientationUtilsKt.isPortrait()) { - ViewExtensionsKt.clipWithRoundedCorners(binding.mainComponent, ExtensionsKt.getDp(16)); + if (OrientationUtilsKt.isLandscape()) { + viewModel.getFiltersOpened().observe(this, isOpened -> { + if (isOpened) { + ViewExtensionsKt.clipWithRoundedCorners(binding.mainComponent, ExtensionsKt.getDp(16)); + } else { + ViewExtensionsKt.clipWithTopRightRoundedCorner(binding.mainComponent, ExtensionsKt.getDp(16)); + } + }); } else { - ViewExtensionsKt.clipWithTopRightRoundedCorner(binding.mainComponent, ExtensionsKt.getDp(16)); + ViewExtensionsKt.clipWithRoundedCorners(binding.mainComponent, ExtensionsKt.getDp(16)); } binding.filterRecyclerLayout.setAdapter(filtersAdapter); diff --git a/app/src/main/res/layout-land/activity_search.xml b/app/src/main/res/layout-land/activity_search.xml index 5f3e0c6fac..4d15c913a0 100644 --- a/app/src/main/res/layout-land/activity_search.xml +++ b/app/src/main/res/layout-land/activity_search.xml @@ -208,6 +208,7 @@ android:id="@+id/mainComponent" android:layout_width="0dp" android:layout_height="0dp" + android:animateLayoutChanges="true" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@id/backdropGuideDiv" From 4c4730a6e82eb5e4183cbf140854c51b3944d995 Mon Sep 17 00:00:00 2001 From: andresmr Date: Fri, 23 Feb 2024 08:32:15 +0100 Subject: [PATCH 47/47] [ANDROAPP-5800] fix code smell --- .../org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java index 3e370828e3..afa003d269 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java @@ -176,7 +176,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { if (OrientationUtilsKt.isLandscape()) { viewModel.getFiltersOpened().observe(this, isOpened -> { - if (isOpened) { + if (Boolean.TRUE.equals(isOpened)) { ViewExtensionsKt.clipWithRoundedCorners(binding.mainComponent, ExtensionsKt.getDp(16)); } else { ViewExtensionsKt.clipWithTopRightRoundedCorner(binding.mainComponent, ExtensionsKt.getDp(16));