Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

fix: [ANDROAPP-6652] null pointer exception attempt to invoke virtual method sentry #3897

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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