Caused by: kotlin.UninitializedPropertyAccessException: lateinit property dialog has not been initialized - android

I just started learning Kotlin. I am using a custom ProgressDialog. Everytime I press back from the MainActivity, the app crashes with the following error:
Caused by: kotlin.UninitializedPropertyAccessException: lateinit property dialog has not been initialized
at com.devApps.blog.CustomProgressDialog.getDialog(CustomProgressDialog.kt:17)
Here is my CustomProgressDialog :
private val progressDialognew = CustomProgressDialog()
lateinit var dialog: CustomDialog
fun show(context: Context): Dialog {
return show(context, null)
}
fun show(context: Context, title: CharSequence?): Dialog {
val inflater = (context as Activity).layoutInflater
val view = inflater.inflate(R.layout.progress_dialog_view, null)
if (title != null) {
view.cp_title.text = title
}
dialog = CustomDialog(context)
dialog.setContentView(view)
dialog.show()
return dialog
}
And this is my MainActivity code :
private val progressDialognew = CustomProgressDialog()
progressDialognew.show(this, "Optimizing Image...")
{
my tasks here
}
override fun onDestroy() {
super.onDestroy()
progressDialognew.dialog.dismiss()
}
UPDATE : I did as suggested now I am getting the same Error in the show() at the return dialog line. How to fix that ?
lateinit var dialog: CustomDialog
fun show(context: Context): Dialog {
return show(context, null)
}
fun show(context: Context, title: CharSequence?): Dialog {
if (::dialog.isInitialized) {
val inflater = (context as Activity).layoutInflater
val view = inflater.inflate(R.layout.progress_dialog_view, null)
if (title != null) {
view.cp_title.text = title
}
dialog = CustomDialog(context)
dialog.setContentView(view)
dialog.show()
}
return dialog
}
}
fun hideProgress() {
if (::dialog.isInitialized) {
if (dialog != null) {
dialog.dismiss()
}
}
Complete StackTrace :
kotlin.UninitializedPropertyAccessException: lateinit property dialog has not been initialized
at com.devApps.blog.CustomProgressDialog.show(CustomProgressDialog.kt:50)
at com.devApps.blog.PostActivity.upload(PostActivity.kt:328)
at com.devApps.blog.PostActivity.post(PostActivity.kt:320)
at com.devApps.blog.PostActivity.access$post(PostActivity.kt:54)
at com.devApps.blog.PostActivity$onCreate$5$2.onClick(PostActivity.kt:198)

If you close the activity without ever showing the dialog, this exception will happen. Because the dialog property in your CustomProgressDialog gets initialized only in show() method.
And onDestroy() method of the activity calls dismiss() on the dialog property that might not be initialized.
You can check if it's initialized first before dismissing it, consider adding this method inside CustomProgressDialog:
fun dismissDialog() {
if (::dialog.isInitialized) {
dialog.dismiss()
}
}
And call it from the activity:
override fun onDestroy() {
super.onDestroy()
progressDialognew.dismissDialog()
}

Related

Catch dialog close event from caller class

I have a custom dialog.
How can I catch dialog close event from class which call dialog?
class DialogShow {
companion object {
fun showDialog() {
//some logic
val customDialog = CustomDialog(account, context)
customDialog.onDismissListener = {
// here I need to return close event
}
customDialog.show(activity,"rawer")
}
}
}
calss CallDialog{
DialogShow.showDialog()
//here I need to catch dialog dismiss
}
I need to catch this event from kotlin and Java classes.
Create a callback parameter in your function, and call it in the onDismissListener:
class DialogShow {
companion object {
fun showDialog(context: Context, onDismiss: ()->Unit) {
//some logic
val customDialog = CustomDialog(account, context)
customDialog.onDismissListener = {
onDismiss()
}
customDialog.show(context, "rawer")
}
}
}
fun foo() {
DialogShow.showDialog(context) {
// code that is run after dialog is closed
}
}

GenericDialog did not return a View from onCreateView() or this was called before onCreateView()

I was developing an app in kotlin, and I try to make a custom dialog, which extends of my parent class BaseDialogFragment, and when I try to instace my GenericDialog I get the following error:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: es.renaultbank.andr, PID: 21498
java.lang.IllegalStateException: Fragment GenericDialog{a0c2f1e} (ffc56cd1-3856-466f-8705-c66209c24963 tag=sucess_otp_dialog) did not return a View from onCreateView() or this was called before onCreateView().
at androidx.fragment.app.Fragment.requireView(Fragment.java:1964)
at es.renaultbank.andr.utils.GenericDialog.configureEvents(GenericDialog.kt:39)
at es.renaultbank.andr.ui.customviews.dialog.BaseDialogFragment.onCreateView(BaseDialogFragment.kt:17)
at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2963)
at androidx.fragment.app.DialogFragment.performCreateView(DialogFragment.java:489)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:518)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:282)
My GenericDialog class is the following:
package es.renaultbank.andr.utils
import android.app.Dialog
import android.content.Context
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.ImageView
import android.widget.TextView
import es.renaultbank.andr.R
import es.renaultbank.andr.ui.customviews.dialog.BaseDialogFragment
import es.renaultbank.andr.ui.extensions.find
class GenericDialog(
context: Context,
private val icon: Int? = null,
private val title: String? = null,
private val subtitle: String? = null,
private val buttonText: String? = null,
private var clickListener: View.OnClickListener? = null,
private val cancelable: Boolean = true
) : BaseDialogFragment() {
private var iv_icon: ImageView = ImageView(context)
private var tv_title: TextView = TextView(context)
private var tv_subtitle: TextView = TextView(context)
private var btn_action: Button = Button(context)
private var iv_close: ImageView = ImageView(context)
override fun configureView(view: View?) {
dialog?.setCancelable(true)
dialog?.setCanceledOnTouchOutside(true)
}
override fun configureEvents(view: View?) {
iv_icon = requireView().findViewById(R.id.iv_icon)
tv_title = requireView().findViewById(R.id.tv_title)
tv_subtitle = requireView().findViewById(R.id.tv_subtitle)
btn_action = requireView().findViewById(R.id.btn_action)
iv_close = requireView().findViewById(R.id.iv_close)
}
override fun getLayout(): Int {
return R.layout.dialog_generic
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setView()
setListeners()
}
fun setIcon(icon: Int) {
iv_icon.setImageDrawable(requireContext().getDrawable(icon))
iv_icon.visibility = View.VISIBLE
iv_icon.setColorFilter(R.color.black)
}
fun setTitle(title: String) {
tv_title.text = title
tv_title.visibility = View.VISIBLE
}
fun setSubtitle(subtitle: String) {
tv_subtitle.text = subtitle
tv_subtitle.visibility = View.VISIBLE
}
fun setButtonText(text: String) {
btn_action.text = text
btn_action.visibility = View.VISIBLE
}
fun setOnClickListener() {
btn_action.setOnClickListener { clickListener }
}
override fun setCancelable(flag: Boolean) {
super.setCancelable(flag)
if (flag) {
iv_close.visibility = View.VISIBLE
} else {
iv_close.visibility = View.GONE
}
}
private fun setView() {
if (icon != null) {
setIcon(icon)
} else {
iv_icon.visibility = View.GONE
}
if (title != null) {
setTitle(title)
} else {
tv_title.visibility = View.GONE
}
if (subtitle != null) {
setSubtitle(subtitle)
} else {
tv_subtitle.visibility = View.GONE
}
if (buttonText != null) {
setButtonText(buttonText)
} else {
btn_action.visibility = View.GONE
}
setCancelable(cancelable)
}
fun dismissDialog(){
this.dismiss()
}
private fun setListeners() {
iv_close.setOnClickListener {
dismiss()
}
if (clickListener != null) {
btn_action.setOnClickListener(clickListener)
} else {
btn_action.setOnClickListener {
dismiss()
}
}
}
}
and their parent class is this:
BaseDialogFragment.kt
package es.renaultbank.andr.ui.customviews.dialog
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import androidx.fragment.app.DialogFragment
abstract class BaseDialogFragment : DialogFragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val rootView: View = inflater.inflate(getLayout(), container, false)
configureView(rootView)
configureEvents(rootView)
return rootView
}
fun hideKeyboard(view: View?) {
view?.let {
val imm = view.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(view.windowToken, 0)
}
}
/**
* Configuracion de los findViewById la vista, colores, animaciones...
*/
protected abstract fun configureView(view: View?)
/**
* Configuracion de listeners
*/
protected abstract fun configureEvents(view: View?)
/**
* Setea el layout del dialog
*/
protected abstract fun getLayout(): Int
}
Finally the used of the GenericDialog is a simple network call, and if sucess, show the dialog
if(it.errorBase.getErrorType() == ErrorBase.ErrorType.NON_ERROR) {
(it.responseBase).let {resp ->
if ((resp as ValidatePassOTPResponse).response == "200 OK") {
val sucessDialog = GenericDialog(
context = requireContext(),
icon = R.drawable.ic_tick_green,
title = getString(R.string.change_password_title),
subtitle = getString(R.string.password_change_sucess),
buttonText = getString(R.string.understand),
cancelable = true,
clickListener = { (activity as DashboarActivity).redirectToLogin() }
)
sucessDialog.show(requireFragmentManager(), "sucess_otp_dialog")
} else {
showOtpError().also {
(activity as DashboarActivity).redirectToLogin()
}
}
}
}else{
(activity as BaseActivity<*>).showError(it.errorBase, null)
dialog?.dismiss().also {
(activity as DashboarActivity).redirectToLogin()
}
}
})
I hope you can help, in case you've faced some similar problem.
Take thanks in advance !
[EDIT]
I've taken to show the dialog replacing requireView to view, in the configureEvents method. but althought is shown and debugging I see that the set method in the dialog are invoked, the dialog show like that:
As you can check is empty although the params which I'm passing are not empty.
Call setView() and setListener() method inside configureEvents() method after initializing the views.
Refer the following code
class GenericDialog : BaseDialogFragment() {
private lateinit var iv_icon: ImageView
private lateinit var tv_title: TextView
private lateinit var tv_subtitle: TextView
private lateinit var btn_action: Button
private lateinit var iv_close: ImageView
override fun configureView(view: View?) {
dialog?.setCancelable(true)
dialog?.setCanceledOnTouchOutside(true)
}
override fun configureEvents(view: View?) {
iv_icon = requireView().findViewById(R.id.iv_icon)
tv_title = requireView().findViewById(R.id.tv_title)
tv_subtitle = requireView().findViewById(R.id.tv_subtitle)
btn_action = requireView().findViewById(R.id.btn_action)
iv_close = requireView().findViewById(R.id.iv_close)
setView()
setListeners()
}
override fun getLayout(): Int {
return R.layout.dialog_generic
}
private fun setIcon(icon: Int) {
iv_icon.setImageDrawable(requireContext().getDrawable(icon))
iv_icon.visibility = View.VISIBLE
iv_icon.setColorFilter(R.color.black)
}
private fun setTitle(title: String) {
tv_title.text = title
tv_title.visibility = View.VISIBLE
}
private fun setSubtitle(subtitle: String) {
tv_subtitle.text = subtitle
tv_subtitle.visibility = View.VISIBLE
}
private fun setButtonText(text: String) {
btn_action.text = text
btn_action.visibility = View.VISIBLE
}
private fun setView() {
val icon = requireArguments().getInt(ICON, -1)
val title = requireArguments().getString(TITLE)
val subtitle = requireArguments().getString(SUB_TITLE)
val buttonText = requireArguments().getString(BUTTON_TEXT)
val cancelable = requireArguments().getBoolean(CANCELABLE)
if (icon != -1) {
setIcon(icon)
} else {
iv_icon.visibility = View.GONE
}
if (title != null) {
setTitle(title)
} else {
tv_title.visibility = View.GONE
}
if (subtitle != null) {
setSubtitle(subtitle)
} else {
tv_subtitle.visibility = View.GONE
}
if (buttonText != null) {
setButtonText(buttonText)
} else {
btn_action.visibility = View.GONE
}
setCancelable(cancelable)
}
fun dismissDialog() {
this.dismiss()
}
private fun setListeners() {
iv_close.setOnClickListener {
dismiss()
}
btn_action.setOnClickListener {
val clickListener = getActionListener()
if (clickListener != null) {
clickListener.onAction()
} else {
dismiss()
}
}
}
private fun getActionListener(): ActionListener? {
val activity = this.activity
return if (activity is ActionListener) activity
else null
}
companion object {
private const val ICON = "ICON"
private const val TITLE = "TITLE"
private const val SUB_TITLE = "SUB_TITLE"
private const val BUTTON_TEXT = "BUTTON_TEXT"
private const val CANCELABLE = "cancelable"
fun newInstance(
icon: Int? = null,
title: String? = null,
subtitle: String? = null,
buttonText: String? = null,
cancelable: Boolean = true) = GenericDialog().apply {
arguments = Bundle().apply {
putString(TITLE, title)
putString(SUB_TITLE, subtitle)
putString(BUTTON_TEXT, buttonText)
if (icon != null) {
putInt(ICON, icon)
}
putBoolean(CANCELABLE, cancelable)
}
}
}
interface ActionListener {
fun onAction()
}
}

Kotlin: targetfragment is deprecated in DialogFragment. How to replace it?

My DialogFragment
private lateinit var listener: OnItemClickDialogListener
My interface
interface OnItemClickDialogListener{
fun onItemClickDialog(sortDesc: Boolean){
}
And my problem
listener = try {
targetFragment as OnItemClickDialogListener
}catch (e: TypeCastException){
throw TypeCastException()
}
targetFragment is deprecated and I don't know how to fix it. I spent a lot of time find solution.
My full code:
class SortDialogFragment: DialogFragment() {
private lateinit var listener: OnItemClickDialogListener
private lateinit var radioGroup: RadioGroup
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val builder = AlertDialog.Builder(requireContext())
val inflater = requireActivity().layoutInflater
val view: View = inflater.inflate(R.layout.fragment_dialog_sort, null)
radioGroup = view.findViewById(R.id.sort_RG)
builder.setView(view)
.setPositiveButton("OK") {di, i ->
when(radioGroup.checkedRadioButtonId){
R.id.sort_desc_RB -> { listener.onItemClickDialog(true) }
R.id.sort_asc_RB -> { listener.onItemClickDialog(false) }
}
}
.setNegativeButton("Cancel") {di, i -> }
return super.onCreateDialog(savedInstanceState)
}
override fun onAttach(context: Context) {
super.onAttach(context)
listener = try {
targetFragment as OnItemClickDialogListener
}catch (e: TypeCastException){
throw TypeCastException()
}
}
interface OnItemClickDialogListener{
fun onItemClickDialog(sortDesc: Boolean){
}
}
}

What is the Context for a listener to get Info From a DialogFragment to the Parent Fragment

I've been trying to search for an answer on my own but no one seems to be talking about my exact issue. It's usually a question about the RecyclerView Adapter Listener, and I've used that and it works every time.
When I run my code, I get this error,
java.lang.ClassCastException: com.wereworkingonit.communitystore.SettingsActivity#d180f18must implement ExampleDialogListener
which, without the try, catch block gives me
java.lang.ClassCastException: com.wereworkingonit.communitystore.SettingsActivity cannot be cast to com.wereworkingonit.communitystore.JoinStoreDialog$EngagedDialogListener, which doesn't make sense because I followed a tutorial and just translated it to Kotlin. I assume I am missing something about the difference in how Java and Kotlin.
My question is, what do I put in the listener = line in the OnAttach function?
This is where the issue comes from.
override fun onAttach(context: Context) {
super.onAttach(context)
try {
listener = context as EngagedDialogListener
} catch (e: ClassCastException) {
throw ClassCastException(
context.toString() +
"must implement ExampleDialogListener"
)
}
}
This is the Parent Activity for the Dialog
package com.wereworkingonit.communitystore
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreferenceCompat
import com.wereworkingonit.communitystore.participant.ParticipantLandingPage
import com.wereworkingonit.communitystore.user.UserLandingPage
import com.wereworkingonit.communitystore.util.FirestoreUtil
private const val TAG = "SettingsActivity"
class SettingsActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.settings_activity)
supportFragmentManager
.beginTransaction()
.replace(R.id.settings, SettingsFragment())
.commit()
supportActionBar?.setDisplayHomeAsUpEnabled(true)
}
class SettingsFragment : PreferenceFragmentCompat(),
SharedPreferences.OnSharedPreferenceChangeListener, JoinStoreDialog.EngagedDialogListener {
private lateinit var contextForPreferences: Context
private var engagedPreference: Boolean = false
private lateinit var sharedPref: SharedPreferences
private var switchPreference: SwitchPreferenceCompat? = null
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.root_preferences, rootKey)
switchPreference = findPreference("engage")
androidx.preference.PreferenceManager.setDefaultValues(
contextForPreferences,
R.xml.root_preferences,
false
)
sharedPref =
androidx.preference.PreferenceManager.getDefaultSharedPreferences(
contextForPreferences
)
engagedPreference = sharedPref.getBoolean(AppConstants.ENGAGEMENT, false)
Log.d(TAG, "onCreate: $engagedPreference")
FirestoreUtil.getCurrentUser {
switchPreference!!.isChecked = it.engagement
}
}
override fun onAttach(context: Context) {
super.onAttach(context)
contextForPreferences = context
}
override fun onStart() {
super.onStart()
androidx.preference.PreferenceManager.getDefaultSharedPreferences(contextForPreferences)
.registerOnSharedPreferenceChangeListener(this)
}
override fun onStop() {
super.onStop()
androidx.preference.PreferenceManager.getDefaultSharedPreferences(contextForPreferences)
.unregisterOnSharedPreferenceChangeListener(this)
}
override fun onPause() {
super.onPause()
engagedPreference = sharedPref.getBoolean(AppConstants.ENGAGEMENT, false)
Log.d(TAG, "onPause: $engagedPreference")
if (engagedPreference) {
//TODO: Add welcome splash for Engagement Change
val intent = Intent(contextForPreferences, ParticipantLandingPage::class.java)
startActivity(intent)
} else {
val intent = Intent(contextForPreferences, UserLandingPage::class.java)
startActivity(intent)
}
}
override fun notThisTime(nope: Boolean) {
FirestoreUtil.getCurrentUser {
switchPreference!!.isChecked = nope
}
}
override fun onSharedPreferenceChanged(
sharedPreferences: SharedPreferences?,
key: String?
) {
when (key) {
"engage" -> {
val joinStoreDialog = JoinStoreDialog()
if (switchPreference!!.isChecked) {
joinStoreDialog.show(childFragmentManager, "JoinStoreDialog")
} else {
FirestoreUtil.updateCurrentUser("", null, switchPreference!!.isChecked)
}
//TODO: One way trip, or what happens if the user clicks to end engagement?
}
}
}
}
}
This is the Dialog Fragment where the listener is made
package com.wereworkingonit.communitystore
import android.app.AlertDialog
import android.app.Dialog
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.os.Bundle
import androidx.appcompat.app.AppCompatDialogFragment
import com.wereworkingonit.communitystore.participant.ParticipantLandingPage
import com.wereworkingonit.communitystore.util.FirestoreUtil
import kotlinx.android.synthetic.main.dialog_join_the_fold.view.*
class JoinStoreDialog : AppCompatDialogFragment() {
private lateinit var sharedPreferences: SharedPreferences
private lateinit var listener: EngagedDialogListener
override fun onAttach(context: Context) {
super.onAttach(context)
try {
listener = context as EngagedDialogListener
} catch (e: ClassCastException) {
throw ClassCastException(
context.toString() +
"must implement ExampleDialogListener"
)
}
}
interface EngagedDialogListener {
fun notThisTime(nope: Boolean)
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
sharedPreferences =
androidx.preference.PreferenceManager.getDefaultSharedPreferences(
activity
)
val builder = AlertDialog.Builder(activity)
val inflater = requireActivity().layoutInflater
val view = inflater.inflate(R.layout.dialog_join_the_fold, null)
view.dialog_new_store_name
view.dialog_new_store_email
view.dialog_new_store_password
builder.setView(view)
.setTitle("Thank You for Joining")
.setNegativeButton("Na, Not Now") { dialogInterface, i ->
engagedParticipant(false)
listener.notThisTime(false)
dialogInterface.dismiss()
}
.setPositiveButton("Let's Get Started") { dialogInterface, i ->
engagedParticipant(true)
FirestoreUtil.updateCurrentUser("", null, true)
val intent = Intent(activity, ParticipantLandingPage::class.java)
startActivity(intent)
}
return builder.create()
}
private fun engagedParticipant(engagement: Boolean) {
val editor = sharedPreferences.edit()
editor.putBoolean(AppConstants.ENGAGEMENT, engagement)
editor.apply()
}
}
The Context is your Activity. If you want to reference your parent fragment, you need to use parentFragment (or, on newer versions of Fragments, use requireParentFragment() to get a non-null fragment):
override fun onAttach(context: Context) {
super.onAttach(context)
try {
listener = requireParentFragment() as EngagedDialogListener
} catch (e: ClassCastException) {
throw ClassCastException(
requireParentFragment().toString() +
"must implement ExampleDialogListener"
)
}
}

Android Kotlin - Create dialog inside a callback function

I'm trying to create a dialog inside a callback onSuccess() which again is inside the onCreate() method of the activity, but the dialog view doesn't show up.
When I call createDialog() directly inside the onCreate() it works. What could be the reason why it's not working in the onSuccess() callback function? onSuccess() and createDialog() definitely get called because the println show up.
Code:
class BleDevicesControlActivity : AppCompatActivity() {
private var device: BluetoothDevice? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_ble_devices_control)
connectToDevice(object : BleCommunication.OnConnectionListener {
override fun onSuccess() {
println("onSuccess called")
createDialog()
}
override fun onFailure() {
println("onFailure called")
}
})
}
private fun connectToDevice(onConnectionListener : BleCommunication.OnConnectionListener) {
bleCommunication.connect(device!!, onConnectionListener)
}
private fun createDialog() {
println("createDialog called")
val dialogInflater = LayoutInflater.from(this)
val alertDialogView = dialogInflater.inflate(R.layout.dialog_alert, null)
val alertDialog = AlertDialog.Builder(this).create()
alertDialog.setView(alertDialogView)
alertDialog.show()
}
}
Try this
override fun onSuccess() {
println("onSuccess called")
runOnUiThread { createDialog() }
}
Set view to dialog builder:
private fun createDialog() {
println("createDialog called")
val dialogInflater = LayoutInflater.from(this)
val alertDialogView = dialogInflater.inflate(R.layout.dialog_alert, null)
val alertDialog = AlertDialog.Builder(this).setView(alertDialogView).create()
alertDialog.show()
}

Categories

Resources