Skip to content

Commit

Permalink
fix: [ANDROAPP-6652] null pointer exception attempt to invoke virtual…
Browse files Browse the repository at this point in the history
… method sentry (#3897)

* fix: [ANDROAPP-6652] Crash when showing snackbar

* fix issues

* fix: change clickOnAddEvent to composeTestRule

---------

Co-authored-by: Pablo Pajuelo Cabezas <[email protected]>
  • Loading branch information
xavimolloy and Balcan authored Nov 28, 2024
1 parent 6af97ea commit 3620ad3
Show file tree
Hide file tree
Showing 9 changed files with 246 additions and 218 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class ProgramEventsRobot(val composeTestRule: ComposeContentTestRule) : BaseRobo
}

fun clickOnAddEvent() {
onView(withId(R.id.addEventButton)).perform(click())
composeTestRule.onNodeWithTag("ADD_EVENT_BUTTON").performClick()
}

fun clickOnMap() {
Expand Down
8 changes: 8 additions & 0 deletions app/src/main/java/org/dhis2/model/SnackbarMessage.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.dhis2.model

import java.util.UUID

data class SnackbarMessage(
val id: UUID = UUID.randomUUID(),
val message: String = "",
)
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,18 @@ import android.transition.ChangeBounds
import android.transition.Transition
import android.transition.TransitionManager
import android.view.View
import androidx.activity.compose.setContent
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
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
import androidx.compose.ui.Modifier
import androidx.constraintlayout.widget.ConstraintSet
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.viewModelScope
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
import dhis2.org.analytics.charts.ui.GroupAnalyticsFragment
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import org.dhis2.R
import org.dhis2.bindings.app
import org.dhis2.bindings.clipWithRoundedCorners
import org.dhis2.bindings.dp
import org.dhis2.bindings.userComponent
import org.dhis2.commons.Constants
import org.dhis2.commons.date.DateUtils
import org.dhis2.commons.date.DateUtils.OnFromToSelector
Expand Down Expand Up @@ -60,12 +47,10 @@ import org.dhis2.utils.analytics.DATA_CREATION
import org.dhis2.utils.category.CategoryDialog
import org.dhis2.utils.category.CategoryDialog.Companion.TAG
import org.dhis2.utils.customviews.RxDateDialog
import org.dhis2.utils.customviews.navigationbar.NavigationPage
import org.dhis2.utils.granularsync.SyncStatusDialog
import org.dhis2.utils.granularsync.shouldLaunchSyncDialog
import org.hisp.dhis.android.core.period.DatePeriod
import org.hisp.dhis.android.core.program.Program
import org.hisp.dhis.mobile.ui.designsystem.component.navigationBar.NavigationBar
import org.hisp.dhis.mobile.ui.designsystem.theme.DHIS2Theme
import timber.log.Timber
import java.util.Date
Expand Down Expand Up @@ -110,17 +95,22 @@ class ProgramEventDetailActivity :
initInjection()
themeManager?.setProgramTheme(programUid)
super.onCreate(savedInstanceState)
initEventFilters()
initViewModel()
binding = DataBindingUtil.setContentView(this, R.layout.activity_program_event_detail)
binding.presenter = presenter
binding.totalFilters = FilterManager.getInstance().totalFilters

setupBottomNavigation()
binding.fragmentContainer.clipWithRoundedCorners(16.dp)
binding.filterLayout.adapter = filtersAdapter
presenter.init()
binding.syncButton.setOnClickListener { showSyncDialogProgram() }
setContent {
DHIS2Theme {
ProgramEventDetailScreen(
programEventsViewModel,
presenter,
networkUtils,
{ binding = it },
{
initBindings()
initEventFilters()
initViewModel()
},
)
}
}

if (intent.shouldLaunchSyncDialog()) {
showSyncDialogProgram()
Expand All @@ -140,73 +130,21 @@ class ProgramEventDetailActivity :
}
}

private fun setupBottomNavigation() {
binding.navigationBar.setContent {
DHIS2Theme {
val uiState by programEventsViewModel.navigationBarUIState
val isBackdropActive by programEventsViewModel.backdropActive.observeAsState(false)
var selectedItemIndex by remember(uiState) {
mutableIntStateOf(
uiState.items.indexOfFirst {
it.id == uiState.selectedItem
},
)
}

LaunchedEffect(uiState.selectedItem) {
when (uiState.selectedItem) {
NavigationPage.LIST_VIEW -> {
programEventsViewModel.showList()
}

NavigationPage.MAP_VIEW -> {
networkUtils.performIfOnline(
context = this@ProgramEventDetailActivity,
action = {
presenter.trackEventProgramMap()
programEventsViewModel.showMap()
},
onDialogDismissed = {
selectedItemIndex = 0
},
noNetworkMessage = getString(R.string.msg_network_connection_maps),
)
}

NavigationPage.ANALYTICS -> {
presenter.trackEventProgramAnalytics()
programEventsViewModel.showAnalytics()
}

else -> {
// no-op
}
}
}

AnimatedVisibility(
visible = uiState.items.size > 1 && isBackdropActive.not(),
enter = slideInVertically(animationSpec = tween(200)) { it },
exit = slideOutVertically(animationSpec = tween(200)) { it },
) {
NavigationBar(
modifier = Modifier.fillMaxWidth(),
items = uiState.items,
selectedItemIndex = selectedItemIndex,
) { page ->
programEventsViewModel.onNavigationPageChanged(page)
}
}
}
}
private fun initBindings() {
binding.presenter = presenter
binding.totalFilters = FilterManager.getInstance().totalFilters
binding.fragmentContainer.clipWithRoundedCorners(16.dp)
binding.filterLayout.adapter = filtersAdapter
binding.syncButton.setOnClickListener { showSyncDialogProgram() }
binding.totalFilters = FilterManager.getInstance().totalFilters
}

private fun initExtras() {
programUid = intent.getStringExtra(EXTRA_PROGRAM_UID) ?: ""
}

private fun initInjection() {
component = app().userComponent()
component = userComponent()
?.plus(
ProgramEventDetailModule(
this,
Expand Down Expand Up @@ -243,9 +181,7 @@ class ProgramEventDetailActivity :
programEventsViewModel.onRecreationActivity(false)
}
}
programEventsViewModel.writePermission.observe(this) { canWrite: Boolean ->
binding.addEventButton.visibility = if (canWrite) View.VISIBLE else View.GONE
}

programEventsViewModel.currentScreen.observe(this) { currentScreen: EventProgramScreen? ->
currentScreen?.let {
when (it) {
Expand All @@ -257,12 +193,6 @@ class ProgramEventDetailActivity :
}
}

override fun onResume() {
super.onResume()
binding.addEventButton.isEnabled = true
binding.totalFilters = FilterManager.getInstance().totalFilters
}

private fun showSyncDialogProgram() {
SyncStatusDialog.Builder()
.withContext(this)
Expand All @@ -273,12 +203,9 @@ class ProgramEventDetailActivity :
}
})
.onNoConnectionListener {
val contextView = findViewById<View>(R.id.navigationBar)
Snackbar.make(
contextView,
R.string.sync_offline_check_connection,
Snackbar.LENGTH_SHORT,
).show()
programEventsViewModel.displayMessage(
getString(R.string.sync_offline_check_connection),
)
}
.show("EVENT_SYNC")
}
Expand Down Expand Up @@ -368,13 +295,6 @@ class ProgramEventDetailActivity :
ConstraintSet.BOTTOM,
0,
)
initSet.connect(
R.id.addEventButton,
ConstraintSet.BOTTOM,
R.id.fragmentContainer,
ConstraintSet.BOTTOM,
16.dp,
)
} else {
initSet.connect(
R.id.fragmentContainer,
Expand All @@ -390,13 +310,6 @@ class ProgramEventDetailActivity :
ConstraintSet.TOP,
0,
)
initSet.connect(
R.id.addEventButton,
ConstraintSet.BOTTOM,
R.id.navigationBar,
ConstraintSet.TOP,
16.dp,
)
}
initSet.applyTo(binding.backdropLayout)
}
Expand Down Expand Up @@ -429,19 +342,13 @@ class ProgramEventDetailActivity :
programStageUid = it,
)
}
} else {
enableAddEventButton(true)
}
}
.build()
.show(supportFragmentManager, "ORG_UNIT_DIALOG")
}
}

private fun enableAddEventButton(enable: Boolean) {
binding.addEventButton.isEnabled = enable
}

override fun setWritePermission(canWrite: Boolean) {
programEventsViewModel.writePermission.value = canWrite
}
Expand Down Expand Up @@ -524,47 +431,36 @@ class ProgramEventDetailActivity :
}
})
.onNoConnectionListener {
val contextView = findViewById<View>(R.id.rootView)
Snackbar.make(
contextView,
R.string.sync_offline_check_connection,
Snackbar.LENGTH_SHORT,
).show()
programEventsViewModel.displayMessage(
getString(R.string.sync_offline_check_connection),
)
}
.show(FRAGMENT_TAG)
}

private fun showList() {
supportFragmentManager.beginTransaction().replace(
R.id.fragmentContainer,
binding.fragmentContainer.id,
EventListFragment(),
"EVENT_LIST",
).commitNow()
binding.addEventButton.visibility =
if (programEventsViewModel.writePermission.value == true) {
View.VISIBLE
} else {
View.GONE
}
binding.filter.visibility = View.VISIBLE
}

private fun showMap() {
supportFragmentManager.beginTransaction().replace(
R.id.fragmentContainer,
binding.fragmentContainer.id,
EventMapFragment(),
"EVENT_MAP",
).commitNow()
binding.addEventButton.visibility = View.GONE
binding.filter.visibility = View.VISIBLE
}

private fun showAnalytics() {
supportFragmentManager.beginTransaction().replace(
R.id.fragmentContainer,
binding.fragmentContainer.id,
GroupAnalyticsFragment.forProgram(programUid),
).commitNow()
binding.addEventButton.visibility = View.GONE
binding.filter.visibility = View.GONE
}

Expand Down
Loading

0 comments on commit 3620ad3

Please sign in to comment.