diff --git a/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailActivity.kt b/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailActivity.kt index 6e1cdab635..642fbe283d 100644 --- a/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailActivity.kt +++ b/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailActivity.kt @@ -9,6 +9,7 @@ import android.transition.TransitionManager import android.view.View import androidx.activity.viewModels import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.core.tween import androidx.compose.animation.slideInVertically import androidx.compose.animation.slideOutVertically import androidx.compose.foundation.layout.fillMaxWidth @@ -185,8 +186,8 @@ class ProgramEventDetailActivity : AnimatedVisibility( visible = uiState.items.size > 1 && isBackdropActive.not(), - enter = slideInVertically { it }, - exit = slideOutVertically { it }, + enter = slideInVertically(animationSpec = tween(200)) { it }, + exit = slideOutVertically(animationSpec = tween(200)) { it }, ) { NavigationBar( modifier = Modifier.fillMaxWidth(), @@ -321,13 +322,13 @@ class ProgramEventDetailActivity : val transition: Transition = ChangeBounds() transition.addListener(object : Transition.TransitionListener { override fun onTransitionStart(transition: Transition) { + programEventsViewModel.updateBackdrop(backDropActive) if (!backDropActive) { binding.clearFilters.hide() } } override fun onTransitionEnd(transition: Transition) { - programEventsViewModel.updateBackdrop(backDropActive) if (backDropActive) { binding.clearFilters.show() } @@ -345,11 +346,11 @@ class ProgramEventDetailActivity : /*No action needed*/ } }) + backDropActive = !backDropActive + transition.duration = 200 TransitionManager.beginDelayedTransition(binding.backdropLayout, transition) - backDropActive = !backDropActive - val initSet = ConstraintSet() initSet.clone(binding.backdropLayout) if (backDropActive) { @@ -360,6 +361,20 @@ class ProgramEventDetailActivity : ConstraintSet.BOTTOM, 16.dp, ) + initSet.connect( + R.id.fragmentContainer, + ConstraintSet.BOTTOM, + ConstraintSet.PARENT_ID, + ConstraintSet.BOTTOM, + 0, + ) + initSet.connect( + R.id.addEventButton, + ConstraintSet.BOTTOM, + R.id.fragmentContainer, + ConstraintSet.BOTTOM, + 16.dp, + ) } else { initSet.connect( R.id.fragmentContainer, @@ -368,6 +383,20 @@ class ProgramEventDetailActivity : ConstraintSet.BOTTOM, 0, ) + initSet.connect( + R.id.fragmentContainer, + ConstraintSet.BOTTOM, + R.id.navigationBar, + ConstraintSet.TOP, + 0, + ) + initSet.connect( + R.id.addEventButton, + ConstraintSet.BOTTOM, + R.id.navigationBar, + ConstraintSet.TOP, + 16.dp, + ) } initSet.applyTo(binding.backdropLayout) } @@ -424,12 +453,15 @@ class ProgramEventDetailActivity : override fun showPeriodRequest(periodRequest: PeriodRequest) { if (periodRequest == PeriodRequest.FROM_TO) { - DateUtils.getInstance().fromCalendarSelector(this.context) { datePeriod: List? -> - FilterManager.getInstance().addPeriod(datePeriod) - } + DateUtils.getInstance() + .fromCalendarSelector(this.context) { datePeriod: List? -> + FilterManager.getInstance().addPeriod(datePeriod) + } } else { val onFromToSelector = - OnFromToSelector { datePeriods -> FilterManager.getInstance().addPeriod(datePeriods) } + OnFromToSelector { datePeriods -> + FilterManager.getInstance().addPeriod(datePeriods) + } DateUtils.getInstance().showPeriodDialog( this, diff --git a/app/src/main/java/org/dhis2/usescases/programEventDetail/eventList/EventListFragment.kt b/app/src/main/java/org/dhis2/usescases/programEventDetail/eventList/EventListFragment.kt index dfd4766d67..882e42bc41 100644 --- a/app/src/main/java/org/dhis2/usescases/programEventDetail/eventList/EventListFragment.kt +++ b/app/src/main/java/org/dhis2/usescases/programEventDetail/eventList/EventListFragment.kt @@ -4,12 +4,12 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import org.dhis2.commons.filters.workingLists.WorkingListViewModel import org.dhis2.commons.filters.workingLists.WorkingListViewModelFactory +import org.dhis2.databinding.FragmentComposeHolderBinding import org.dhis2.usescases.general.FragmentGlobalAbstract import org.dhis2.usescases.programEventDetail.ProgramEventDetailActivity import org.dhis2.usescases.programEventDetail.ProgramEventDetailViewModel @@ -48,16 +48,18 @@ class EventListFragment : FragmentGlobalAbstract() { programEventsViewModel.eventClicked.value = Pair(eventUid, orgUnitUid) } - return ComposeView(requireContext()).apply { + val binding = FragmentComposeHolderBinding.inflate(inflater, container, false) + binding.composeView.apply { setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) setContent { val workingListViewModel by viewModels { workingListViewModelFactory } EventListScreen( - eventListViewModel, - workingListViewModel, + eventListViewModel = eventListViewModel, + workingListViewModel = workingListViewModel, ) } } + return binding.root } override fun onResume() { diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.kt index 7aff3ec49c..0a813a057b 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.kt @@ -8,9 +8,13 @@ import android.view.View import androidx.activity.viewModels import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.ExperimentalAnimationApi +import androidx.compose.animation.core.tween +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutVertically import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue @@ -272,7 +276,7 @@ class SearchTEActivity : ActivityGlobalAbstract(), SearchTEContractsModule.View override fun onBackPressed() { viewModel.onBackPressed( isPortrait(), - viewModel.searchOrFilterIsOpen(), + viewModel.backdropActive.value ?: false, this.isKeyboardOpened(), { super.onBackPressed() @@ -369,6 +373,7 @@ class SearchTEActivity : ActivityGlobalAbstract(), SearchTEContractsModule.View binding.navigationBar.setContent { DHIS2Theme { val uiState by viewModel.navigationBarUIState + val isBackdropActive by viewModel.backdropActive.observeAsState(false) var selectedItemIndex by remember(uiState) { mutableIntStateOf( uiState.items.indexOfFirst { @@ -387,7 +392,9 @@ class SearchTEActivity : ActivityGlobalAbstract(), SearchTEContractsModule.View } AnimatedVisibility( - visible = viewModel.searchOrFilterIsOpen().not() && uiState.items.isNotEmpty(), + visible = (isBackdropActive.not() && uiState.items.isNotEmpty()) || isLandscape(), + enter = slideInVertically(animationSpec = tween(200)) { it }, + exit = slideOutVertically(animationSpec = tween(200)) { it }, ) { NavigationBar( modifier = Modifier.fillMaxWidth(), @@ -397,7 +404,7 @@ class SearchTEActivity : ActivityGlobalAbstract(), SearchTEContractsModule.View selectedItemIndex = uiState.items.indexOfFirst { it.id == page } if (sessionManagerServiceImpl.isUserLoggedIn().not()) return@NavigationBar - if (viewModel.searchOrFilterIsOpen()) { + if (viewModel.backdropActive.value == true) { searchScreenConfigurator.closeBackdrop() } @@ -511,6 +518,7 @@ class SearchTEActivity : ActivityGlobalAbstract(), SearchTEContractsModule.View private fun observeScreenState() { viewModel.screenState.observe(this, searchScreenConfigurator::configure) + viewModel.screenState.observe(this, viewModel::updateBackdrop) } private fun observeDownload() { 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 8ff9607a9d..f9ac63a0ec 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt @@ -118,6 +118,9 @@ class SearchTEIViewModel( private val _filtersOpened = MutableLiveData(false) val filtersOpened: LiveData = _filtersOpened + private val _backdropActive = MutableLiveData() + val backdropActive: LiveData get() = _backdropActive + private val _teTypeName = MutableLiveData("") val teTypeName: LiveData = _teTypeName @@ -727,7 +730,6 @@ class SearchTEIViewModel( SearchResult( SearchResult.SearchResultType.SEARCH_OUTSIDE, searchRepository.getProgram(initialProgramUid)?.displayName(), - ), ) } @@ -865,11 +867,13 @@ class SearchTEIViewModel( } } - fun searchOrFilterIsOpen(): Boolean { - return _screenState.value?.takeIf { it is SearchList }?.let { - val currentScreen = it as SearchList - currentScreen.searchForm.isOpened || currentScreen.searchFilters.isOpened - } ?: false + fun updateBackdrop(screenState: SearchTEScreenState) { + _backdropActive.postValue( + screenState.takeIf { it is SearchList }?.let { + val currentScreen = it as SearchList + currentScreen.searchForm.isOpened || currentScreen.searchFilters.isOpened + } ?: false, + ) } fun filterIsOpen(): Boolean { diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/ui/BackdropManager.kt b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/ui/BackdropManager.kt index 81261b2a44..eb2c4ea1b3 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/ui/BackdropManager.kt +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/ui/BackdropManager.kt @@ -10,7 +10,12 @@ import org.dhis2.R object BackdropManager { private const val changeBoundDuration = 200L - private fun changeBounds(backdropLayout: ConstraintLayout, endID: Int, margin: Int) { + private fun changeBounds( + isNavigationBarVisible: Boolean, + backdropLayout: ConstraintLayout, + endID: Int, + margin: Int, + ) { val transition: Transition = ChangeBounds() transition.duration = changeBoundDuration TransitionManager.beginDelayedTransition(backdropLayout, transition) @@ -19,15 +24,33 @@ object BackdropManager { initSet.clone(backdropLayout) initSet.connect(R.id.mainComponent, ConstraintSet.TOP, endID, ConstraintSet.BOTTOM, margin) + if (isNavigationBarVisible) { + initSet.connect( + R.id.mainComponent, + ConstraintSet.BOTTOM, + R.id.navigationBar, + ConstraintSet.TOP, + 0, + ) + } else { + initSet.connect( + R.id.mainComponent, + ConstraintSet.BOTTOM, + ConstraintSet.PARENT_ID, + ConstraintSet.BOTTOM, + 0, + ) + } initSet.applyTo(backdropLayout) } fun changeBoundsIf( condition: Boolean, + isNavigationBarVisible: Boolean, backdropLayout: ConstraintLayout, endID: Int, margin: Int, ) { - if (condition) changeBounds(backdropLayout, endID, margin) + if (condition) changeBounds(isNavigationBarVisible, backdropLayout, endID, margin) } } 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 44966a3f16..9a07f35cf5 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 @@ -6,8 +6,6 @@ import androidx.constraintlayout.widget.ConstraintSet import org.dhis2.R import org.dhis2.bindings.display import org.dhis2.bindings.dp -import org.dhis2.commons.animations.hide -import org.dhis2.commons.animations.show import org.dhis2.databinding.ActivitySearchBinding import org.dhis2.usescases.searchTrackEntity.SearchAnalytics import org.dhis2.usescases.searchTrackEntity.SearchList @@ -83,9 +81,8 @@ class SearchScreenConfigurator( } binding.filterRecyclerLayout.visibility = View.VISIBLE binding.searchContainer.visibility = View.GONE - if (isPortrait()) binding.navigationBar.hide() filterIsOpenCallback(true) - changeBounds(R.id.filterRecyclerLayout, 16.dp) + changeBounds(false, R.id.filterRecyclerLayout, 16.dp) } fun closeBackdrop() { @@ -95,9 +92,8 @@ class SearchScreenConfigurator( } binding.filterRecyclerLayout.visibility = View.GONE binding.searchContainer.visibility = View.GONE - if (isPortrait()) binding.navigationBar.show() filterIsOpenCallback(false) - changeBounds(R.id.backdropGuideTop, 0) + changeBounds(true, R.id.backdropGuideTop, 0) } private fun openSearch() { @@ -107,14 +103,14 @@ class SearchScreenConfigurator( binding.title.visibility = View.VISIBLE } binding.searchContainer.visibility = View.VISIBLE - if (isPortrait()) binding.navigationBar.hide() filterIsOpenCallback(false) - changeBounds(R.id.searchContainer, 0) + changeBounds(false, R.id.searchContainer, 0) } - private fun changeBounds(endID: Int, margin: Int) { + private fun changeBounds(isNavigationBarVisible: Boolean, endID: Int, margin: Int) { changeBoundsIf( isPortrait(), + isNavigationBarVisible, binding.backdropLayout, endID, margin, diff --git a/app/src/main/res/layout/activity_search.xml b/app/src/main/res/layout/activity_search.xml index 1c3de29877..18b91ac443 100644 --- a/app/src/main/res/layout/activity_search.xml +++ b/app/src/main/res/layout/activity_search.xml @@ -210,6 +210,7 @@ android:id="@+id/navigationBar" android:layout_width="match_parent" android:layout_height="wrap_content" + app:layout_constraintTop_toBottomOf="@id/mainComponent" app:layout_constraintBottom_toBottomOf="parent" /> diff --git a/app/src/main/res/layout/fragment_compose_holder.xml b/app/src/main/res/layout/fragment_compose_holder.xml new file mode 100644 index 0000000000..4fa0cbbcab --- /dev/null +++ b/app/src/main/res/layout/fragment_compose_holder.xml @@ -0,0 +1,23 @@ + + + + + + + + + +