Skip to content

Commit

Permalink
Add landscape support for Event Capture (dhis2#3586)
Browse files Browse the repository at this point in the history
* Remove edit person button in the TeiDashboardMobileActivity landscape

* feat: Add Enrollment form in landscape TeidashboardMobileActivity

* feat: Remove the TEI name on the toobar of TeiDashboard in landscape

* feat: Add support to not show data entry screen option in eventcapture landscape bottom navbar

* feat: Add a new layout file to support landscape view in EventCaptureActivity

* Rename eventViewandPager to eventViewPager

* Add impementation to set ViewPagerAdapter for landscape view EventCapture

* Show the DataEntry screen when it is portrait view

* feat: Hide DataEntry icon from the BottomNavBar in Landscape

* feat: Add support for EventCapture to adapt to orientation changes

* Remove calling isPortrait() instead use  isPortrait value from EventCapture module

* Handle portrait pages and landscape pages in EventPagerAdapter

* feat: Add support to show complete actions on landscape

* feat: Select the first page in navigationBar as the current page upon orientation changes

* Create a global property eventViewPager to avoid dupicate assignment of ViewPager

* feat: Add event capture form in landscape

* Add a method that will by dagger to  return DashboardViewModelFactory

* Add support to instantiate  DashboardViewModel in EventCaptureActivity in landscape

* Add interface class to enable EventCaptureActivity and TeiDashboardActivityto  share the TEIDataFragment

* feat: Add support to show the Tei detail card on the EventCaptureActivity landscape

* fix: Remove saving EventCaptureFormFragment instance on configuration changes

* feat: Assign programUid a value from an intent to help load Events

* feat: Add support to open new events without starting a new instance of EventCaptureActivity

* feat: Add support to highlight the selected event

* Remove print statement in production codes

* fix: Fix a behavior where an app crash when selecting events without registration on landscape

* feat: Make programs clickabe in EventCaptureActivity

* fix: Remove highlighting OVERDUE, SCHEDULE and SKIPPED Events when selected

* Remove logics to highlight the selected event for portrait view

* Remove unused import

* feat: update dashboardViewModel after syncing Tei details in Eventcapture to refresh the changes

* Remove unused import directive

* Refactor codes with ktlintFormat

* Move default statement in switch statement to the end

* retrigger github checks

* fix: Remove non-null assertions as they may cause NullPointerException

* Add support to show Event details after updating scheduled events

* Remove if null check for viewModelFactory and use ?.let instead

* Format code with ./gradlew ktlintFormat
  • Loading branch information
yuzalsif authored Apr 23, 2024
1 parent 7a1d504 commit 768a3de
Show file tree
Hide file tree
Showing 19 changed files with 797 additions and 179 deletions.
2 changes: 1 addition & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@
<activity android:name=".usescases.reservedValue.ReservedValueActivity" />
<activity
android:name=".usescases.eventsWithoutRegistration.eventCapture.EventCaptureActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:configChanges="keyboardHidden|screenSize"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
<activity android:name=".usescases.sync.SyncActivity" />

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import android.widget.PopupMenu
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModelProvider
import androidx.viewpager2.widget.ViewPager2
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
Expand All @@ -22,6 +24,7 @@ import org.dhis2.commons.dialogs.CustomDialog
import org.dhis2.commons.dialogs.DialogClickListener
import org.dhis2.commons.popupmenu.AppMenuHelper
import org.dhis2.commons.resources.ResourceManager
import org.dhis2.commons.sync.OnDismissListener
import org.dhis2.commons.sync.SyncContext
import org.dhis2.databinding.ActivityEventCaptureBinding
import org.dhis2.form.model.EventMode
Expand All @@ -31,14 +34,18 @@ import org.dhis2.ui.dialogs.bottomsheet.BottomSheetDialog
import org.dhis2.ui.dialogs.bottomsheet.BottomSheetDialogUiModel
import org.dhis2.ui.dialogs.bottomsheet.DialogButtonStyle.DiscardButton
import org.dhis2.ui.dialogs.bottomsheet.DialogButtonStyle.MainButton
import org.dhis2.usescases.eventsWithoutRegistration.eventCapture.eventCaptureFragment.EventCaptureFormFragment
import org.dhis2.usescases.eventsWithoutRegistration.eventCapture.eventCaptureFragment.OnEditionListener
import org.dhis2.usescases.eventsWithoutRegistration.eventCapture.model.EventCompletionDialog
import org.dhis2.usescases.eventsWithoutRegistration.eventDetails.injection.EventDetailsComponent
import org.dhis2.usescases.eventsWithoutRegistration.eventDetails.injection.EventDetailsComponentProvider
import org.dhis2.usescases.eventsWithoutRegistration.eventDetails.injection.EventDetailsModule
import org.dhis2.usescases.eventsWithoutRegistration.eventInitial.EventInitialActivity
import org.dhis2.usescases.general.ActivityGlobalAbstract
import org.dhis2.usescases.teiDashboard.DashboardViewModel
import org.dhis2.usescases.teiDashboard.dashboardfragments.relationships.MapButtonObservable
import org.dhis2.usescases.teiDashboard.dashboardfragments.teidata.TEIDataActivityContract
import org.dhis2.usescases.teiDashboard.dashboardfragments.teidata.TEIDataFragment.Companion.newInstance
import org.dhis2.utils.analytics.CLICK
import org.dhis2.utils.analytics.DELETE_EVENT
import org.dhis2.utils.analytics.SHOW_HELP
Expand All @@ -48,13 +55,16 @@ import org.dhis2.utils.customviews.navigationbar.NavigationPageConfigurator
import org.dhis2.utils.granularsync.OPEN_ERROR_LOCATION
import org.dhis2.utils.granularsync.SyncStatusDialog
import org.dhis2.utils.granularsync.shouldLaunchSyncDialog
import org.dhis2.utils.isLandscape
import org.dhis2.utils.isPortrait
import javax.inject.Inject

class EventCaptureActivity :
ActivityGlobalAbstract(),
EventCaptureContract.View,
MapButtonObservable,
EventDetailsComponentProvider {
EventDetailsComponentProvider,
TEIDataActivityContract {
private lateinit var binding: ActivityEventCaptureBinding

@JvmField
Expand All @@ -78,37 +88,53 @@ class EventCaptureActivity :
var eventCaptureComponent: EventCaptureComponent? = null
var programUid: String? = null
var eventUid: String? = null
private var teiUid: String? = null
private var enrollmentUid: String? = null
private val relationshipMapButton: LiveData<Boolean> = MutableLiveData(false)
private var onEditionListener: OnEditionListener? = null
private var adapter: EventCapturePagerAdapter? = null
private var eventViewPager: ViewPager2? = null
private var dashboardViewModel: DashboardViewModel? = null
override fun onCreate(savedInstanceState: Bundle?) {
eventUid = intent.getStringExtra(Constants.EVENT_UID)
eventCaptureComponent = this.app().userComponent()!!.plus(
EventCaptureModule(
this,
eventUid,
),
)
eventCaptureComponent!!.inject(this)
programUid = intent.getStringExtra(Constants.PROGRAM_UID)
setUpEventCaptureComponent(eventUid)
teiUid = presenter!!.teiUid
enrollmentUid = presenter!!.enrollmentUid
themeManager!!.setProgramTheme(intent.getStringExtra(Constants.PROGRAM_UID)!!)
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_event_capture)
binding.presenter = presenter
eventViewPager = when {
this.isLandscape() -> binding.eventViewLandPager
else -> binding.eventViewPager
}
eventMode = intent.getSerializableExtra(Constants.EVENT_MODE) as EventMode?
setUpViewPagerAdapter()
setUpNavigationBar()
setUpEventCaptureFormLandscape(eventUid ?: "")
if (this.isLandscape() && areTeiUidAndEnrollmentUidNotNull()) {
val viewModelFactory = this.app().dashboardComponent()?.dashboardViewModelFactory()

viewModelFactory?.let {
dashboardViewModel =
ViewModelProvider(this, viewModelFactory)[DashboardViewModel::class.java]
supportFragmentManager.beginTransaction().replace(R.id.tei_column, newInstance(programUid, teiUid, enrollmentUid)).commit()
dashboardViewModel?.updateSelectedEventUid(eventUid)
}
}
showProgress()
presenter!!.initNoteCounter()
presenter!!.init()
binding.syncButton.setOnClickListener { showSyncDialog() }
binding.syncButton.setOnClickListener { showSyncDialog(EVENT_SYNC) }

if (intent.shouldLaunchSyncDialog()) {
showSyncDialog()
showSyncDialog(EVENT_SYNC)
}
}

private fun setUpViewPagerAdapter() {
binding.eventViewPager.isUserInputEnabled = false
eventViewPager?.isUserInputEnabled = false
adapter = EventCapturePagerAdapter(
this,
intent.getStringExtra(Constants.PROGRAM_UID),
Expand All @@ -118,8 +144,8 @@ class EventCaptureActivity :
intent.getBooleanExtra(OPEN_ERROR_LOCATION, false),
eventMode,
)
binding.eventViewPager.adapter = adapter
binding.eventViewPager.registerOnPageChangeCallback(object : OnPageChangeCallback() {
eventViewPager?.adapter = adapter
eventViewPager?.registerOnPageChangeCallback(object : OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
if (position == 0 && eventMode !== EventMode.NEW) {
Expand All @@ -136,12 +162,56 @@ class EventCaptureActivity :

private fun setUpNavigationBar() {
binding.navigationBar.pageConfiguration(pageConfigurator!!)
eventViewPager?.registerOnPageChangeCallback(
object : OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
binding.navigationBar.selectItemAt(position)
}
},
)
binding.navigationBar.setOnItemSelectedListener { item: MenuItem ->
binding.eventViewPager.currentItem = adapter!!.getDynamicTabIndex(item.itemId)
eventViewPager?.currentItem = adapter!!.getDynamicTabIndex(item.itemId)
true
}
}

private fun setUpEventCaptureFormLandscape(eventUid: String) {
if (this.isLandscape()) {
supportFragmentManager.beginTransaction()
.replace(R.id.event_form, EventCaptureFormFragment.newInstance(eventUid, false, eventMode))
.commit()
}
}

private fun setUpEventCaptureComponent(eventUid: String?) {
eventCaptureComponent = app().userComponent()!!.plus(
EventCaptureModule(
this,
eventUid,
this.isPortrait(),
),
)
eventCaptureComponent!!.inject(this)
}

private fun updateLandscapeViewsOnEventChange(newEventUid: String) {
if (newEventUid != this.eventUid) {
this.eventUid = newEventUid
setUpEventCaptureComponent(newEventUid)
setUpViewPagerAdapter()
setUpNavigationBar()
setUpEventCaptureFormLandscape(newEventUid)
showProgress()
presenter!!.initNoteCounter()
presenter!!.init()
}
}

private fun areTeiUidAndEnrollmentUidNotNull(): Boolean {
return teiUid != null && enrollmentUid != null
}

fun openDetails() {
binding.navigationBar.selectItemAt(0)
}
Expand All @@ -158,6 +228,9 @@ class EventCaptureActivity :
override fun onResume() {
super.onResume()
presenter!!.refreshTabCounters()
with(dashboardViewModel) {
this?.selectedEventUid()?.observe(this@EventCaptureActivity, ::updateLandscapeViewsOnEventChange)
}
}

override fun onDestroy() {
Expand Down Expand Up @@ -210,7 +283,11 @@ class EventCaptureActivity :
}

private fun isFormScreen(): Boolean {
return adapter?.isFormScreenShown(binding.eventViewPager.currentItem) == true
return if (this.isPortrait()) {
adapter?.isFormScreenShown(binding.eventViewPager?.currentItem) == true
} else {
true
}
}

override fun updatePercentage(primaryValue: Float) {
Expand All @@ -225,25 +302,28 @@ class EventCaptureActivity :
emptyMandatoryFields: Map<String, String>,
eventCompletionDialog: EventCompletionDialog,
) {
if (binding.navigationBar.selectedItemId == R.id.navigation_data_entry) {
val dialog = BottomSheetDialog(
bottomSheetDialogUiModel = eventCompletionDialog.bottomSheetDialogUiModel,
onMainButtonClicked = {
setAction(eventCompletionDialog.mainButtonAction)
},
onSecondaryButtonClicked = {
eventCompletionDialog.secondaryButtonAction?.let { setAction(it) }
},
content = if (eventCompletionDialog.fieldsWithIssues.isNotEmpty()) {
{ bottomSheetDialog ->
ErrorFieldList(eventCompletionDialog.fieldsWithIssues) {
bottomSheetDialog.dismiss()
}
val dialog = BottomSheetDialog(
bottomSheetDialogUiModel = eventCompletionDialog.bottomSheetDialogUiModel,
onMainButtonClicked = {
setAction(eventCompletionDialog.mainButtonAction)
},
onSecondaryButtonClicked = {
eventCompletionDialog.secondaryButtonAction?.let { setAction(it) }
},
content = if (eventCompletionDialog.fieldsWithIssues.isNotEmpty()) {
{ bottomSheetDialog ->
ErrorFieldList(eventCompletionDialog.fieldsWithIssues) {
bottomSheetDialog.dismiss()
}
} else {
null
},
)
}
} else {
null
},
)
if (binding.navigationBar.selectedItemId == R.id.navigation_data_entry) {
dialog.show(supportFragmentManager, SHOW_OPTIONS)
}
if (this.isLandscape()) {
dialog.show(supportFragmentManager, SHOW_OPTIONS)
}
}
Expand Down Expand Up @@ -452,23 +532,66 @@ class EventCaptureActivity :
return eventCaptureComponent!!.plus(module)
}

private fun showSyncDialog() {
SyncStatusDialog.Builder()
.withContext(this)
.withSyncContext(SyncContext.Event(eventUid!!))
.onNoConnectionListener {
val contextView = findViewById<View>(R.id.navigationBar)
Snackbar.make(
contextView,
R.string.sync_offline_check_connection,
Snackbar.LENGTH_SHORT,
).show()
}
.show("EVENT_SYNC")
private fun showSyncDialog(syncType: String) {
val syncContext = when (syncType) {
TEI_SYNC -> enrollmentUid?.let { SyncContext.Enrollment(it) }
EVENT_SYNC -> SyncContext.Event(eventUid!!)
else -> null
}

syncContext?.let {
SyncStatusDialog.Builder()
.withContext(this)
.withSyncContext(it)
.onDismissListener(object : OnDismissListener {
override fun onDismiss(hasChanged: Boolean) {
if (hasChanged && syncType == TEI_SYNC) {
dashboardViewModel?.updateDashboard()
}
}
})
.onNoConnectionListener {
val contextView = findViewById<View>(R.id.navigationBar)
Snackbar.make(
contextView,
R.string.sync_offline_check_connection,
Snackbar.LENGTH_SHORT,
).show()
}
.show(syncType)
}
}

override fun openSyncDialog() {
showSyncDialog(TEI_SYNC)
}

override fun finishActivity() {
finish()
}

override fun restoreAdapter(programUid: String, teiUid: String, enrollmentUid: String) {
// we do not restore adapter in events
}

override fun executeOnUIThread() {
activity.runOnUiThread {
showDescription(getString(R.string.error_applying_rule_effects))
}
}

override fun getContext(): Context {
return this
}

override fun activityTeiUid(): String? {
return teiUid
}

companion object {
private const val SHOW_OPTIONS = "SHOW_OPTIONS"
private const val TEI_SYNC = "SYNC_TEI"
private const val EVENT_SYNC = "EVENT_SYNC"

@JvmStatic
fun getActivityBundle(eventUid: String, programUid: String, eventMode: EventMode): Bundle {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,12 @@ void attemptFinish(boolean canComplete,
void emitAction(@NotNull EventCaptureAction onBack);

String programStage();

@Nullable
String getTeiUid();

@Nullable
String getEnrollmentUid();
}

public interface EventCaptureRepository {
Expand Down Expand Up @@ -149,6 +155,12 @@ public interface EventCaptureRepository {
boolean hasRelationships();

ValidationStrategy validationStrategy();

@Nullable
String getTeiUid();

@Nullable
String getEnrollmentUid();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,12 @@ public class EventCaptureModule {
private final String eventUid;
private final EventCaptureContract.View view;

public EventCaptureModule(EventCaptureContract.View view, String eventUid) {
private final boolean isPortrait;

public EventCaptureModule(EventCaptureContract.View view, String eventUid, boolean isPortrait) {
this.view = view;
this.eventUid = eventUid;
this.isPortrait = isPortrait;
}

@Provides
Expand Down Expand Up @@ -138,7 +141,7 @@ FlowableProcessor<RowAction> getProcessor() {
NavigationPageConfigurator pageConfigurator(
EventCaptureContract.EventCaptureRepository repository
) {
return new EventPageConfigurator(repository);
return new EventPageConfigurator(repository, isPortrait);
}

@Provides
Expand Down
Loading

0 comments on commit 768a3de

Please sign in to comment.