I get quite a lot of errors in crashlitycs when I try to dismiss a DialogFragment. This is the error that I get:
Attempt to invoke virtual method 'android.os.Handler android.app.FragmentHostCallback.getHandler()' on a null object reference
The line were I am getting is this one showGenericError { activity?.onBackPressed() }
viewLifecycleOwner.observe(viewModel.showErrorAndExit, {
showGenericError { activity?.onBackPressed() }
})
and here is the method that initalize the dialog:
fun showGenericError(actionOnDismiss: (() -> Unit)? = null) {
val manager = childFragmentManager
if (popUpErrorCard == null) {
popUpErrorCard = PopupCard.Builder(R.string.button_try_later)?.apply {
setDescription(R.string.error_card_description_text)
setTitle(R.string.subscribe_error_dialog_title)
setImage(R.drawable.channels_error_popup)
}.build()?.apply {
setDismissListener(object : PopupCard.DismissListener {
override fun onDismiss() {
actionOnDismiss?.invoke()
}
})
}
}
if (popUpErrorCard?.isAdded == false && popUpErrorCard?.isVisible == false && manager.findFragmentByTag(ERROR_DIALOG_TAG) == null) {
popUpErrorCard?.show(manager, ERROR_DIALOG_TAG)
manager.executePendingTransactions()
}
}
The line where I am getting the error is actionOnDismiss?.invoke()
And finally the DialogFragment is this one:
class PopupCard private constructor() : DialogFragment() {
private lateinit var dialog: AlertDialog
private var negativeListener: View.OnClickListener? = null
private var positiveListener: View.OnClickListener? = null
private var dismissLitener: DismissListener? = null
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val builder = AlertDialog.Builder(requireActivity())
val inflater = requireActivity().layoutInflater
val view = inflater.inflate(R.layout.popup_card, null)
@Suppress("UNCHECKED_CAST")
arguments?.let args@{ bundle ->
val negativeText: Int? = bundle.getInt(NEGATIVE_BUTTON_TEXT)
if (negativeText != null && negativeText != 0) {
view.negativeButton.setText(negativeText)
} else {
view.negativeButton.visibility = View.GONE
}
val image: Int? = bundle.getInt(IMAGE_RESOURCE)
image?.let {
view.imageHeader.setImageResource(it)
} ?: run {
view.imageHeader.visibility = View.GONE
}
val titleRes: Int? = bundle.getInt(TITLE_RES)
val titleText: String? = bundle.getString(TITLE)
when {
!titleText.isNullOrBlank() -> {
view.title.text = titleText
}
titleRes != null && titleRes != 0 -> {
view.title.setText(titleRes)
}
else -> view.title.visibility = View.GONE
}
val descriptionRes: Int? = bundle.getInt(DESCRIPTION_RES)
val descriptionText: String? = bundle.getString(DESCRIPTION)
when {
!descriptionText.isNullOrBlank() -> {
view.description.text = descriptionText
}
descriptionRes != null && descriptionRes != 0 -> {
view.description.setText(descriptionRes)
}
else -> view.description.visibility = View.GONE
}
val actionPair = bundle.getInt(POSITIVE_BUTTON_TEXT)
view.positiveButton.setText(actionPair)
}
builder.setView(view)
dialog = builder.create()
view.positiveButton.setOnClickListener {
positiveListener?.onClick(it)
dialog.dismiss()
}
view.negativeButton.setOnClickListener {
negativeListener?.onClick(it)
dialog.dismiss()
}
return dialog
}
fun setOnPositiveClickListener(listener: View.OnClickListener) {
this.positiveListener = listener
}
fun setOnNegativeClickListener(listener: View.OnClickListener) {
this.negativeListener = listener
}
fun setDismissListener(listener: DismissListener) {
this.dismissLitener = listener
}
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
dismissLitener?.onDismiss()
}
interface DismissListener {
fun onDismiss()
}
companion object {
private const val NEGATIVE_BUTTON_TEXT = "PopupCard#NEGATIVE_BUTTON_TEXT"
private const val IMAGE_RESOURCE = "PopupCard#IMAGE_RESOURCE"
private const val TITLE = "PopupCard#TITLE"
private const val TITLE_RES = "PopupCard#TITLE_RES"
private const val DESCRIPTION = "PopupCard#DESCRIPTION"
private const val DESCRIPTION_RES = "PopupCard#DESCRIPTION_RES"
private const val POSITIVE_BUTTON_TEXT = "PopupCard#POSITIVE_BUTTON_TEXT"
}
class Builder(
@StringRes private val positiveText: Int
) {
private var negativeText: Int? = null
@DrawableRes
private var image: Int? = null
@StringRes
private var titleRes: Int? = null
private var titleText: String? = null
@StringRes
private var descriptionRes: Int? = null
private var descriptionText: String? = null
fun setTitle(@StringRes title: Int): Builder {
this.titleRes = title
return this
}
fun setTitle(title: String): Builder {
this.titleText = title
return this
}
fun setDescription(@StringRes description: Int): Builder {
this.descriptionRes = description
return this
}
fun setDescription(description: String): Builder {
this.descriptionText = description
return this
}
fun setNegativeText(@StringRes negativeText: Int): Builder {
this.negativeText = negativeText
return this
}
fun setImage(@DrawableRes image: Int): Builder {
this.image = image
return this
}
fun build(): PopupCard {
val bundle = Bundle().apply {
negativeText?.let {
putInt(NEGATIVE_BUTTON_TEXT, it)
}
image?.let {
putInt(IMAGE_RESOURCE, it)
}
titleRes?.let {
putInt(TITLE_RES, it)
}
titleText?.let {
putString(TITLE, it)
}
descriptionRes?.let {
putInt(DESCRIPTION_RES, it)
}
descriptionText?.let {
putString(DESCRIPTION, it)
}
putInt(POSITIVE_BUTTON_TEXT, positiveText)
}
return PopupCard().apply {
arguments = bundle
}
}
}
}
In the DialogFragment the error is here dismissLitener?.onDismiss()
As you can see in all of the lines that are causing the error have safe calls (?) so I don't know why I am getting NullPointerException and I was unable to reproduce it so I cannot give more details about the issue.
from NullPointerException on dismiss DialogFragment
No comments:
Post a Comment