java.lang.illegalargumentexception: cannot add the same observer with different lifecycles

39 Views Asked by At

I've this code in my fragment and it is really problematic. Cannot find any way to refactor or clean it. It is giving me error regarding mutliple observers and crashing when i click btnSwitch to switch between personal / business profile. I need help in majorly sorting this code and refactoring it and resolving the observer issues. due to stackoverflow's word limit, i've removed a few private functions from the code in this prompt, thos functions have nothing to do with observer

import android.annotation.SuppressLint
import android.app.Activity
import android.content.res.ColorStateList
import android.net.Uri
import android.os.Bundle
import android.util.Log
import android.view.View
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
import com.android.billingclient.api.BillingClient
import com.android.billingclient.api.PurchasesUpdatedListener
import com.bumptech.glide.Glide
import com.github.dhaval2404.imagepicker.ImagePicker
import com.truevast.app.R
import com.truevast.app.data.network.Resource
import com.truevast.app.data.response.Inventory
import com.truevast.app.data.response.LinksItem
import com.truevast.app.data.response.User
import com.truevast.app.databinding.FragmentProfileBinding
import com.truevast.app.ui.home.HomeActivity
import com.truevast.app.ui.home.ui.connects.ConnectionsActivity
import com.truevast.app.ui.inventory.InventoryActivity
import com.truevast.app.ui.socialLink.SocialLinkActivity
import com.truevast.app.util.Constant
import com.truevast.app.util.EventObserver
import com.truevast.app.util.GeneralListener
import com.truevast.app.util.Loading.cancelLoading
import com.truevast.app.util.Loading.showLoading
import com.truevast.app.util.Purchasing
import com.truevast.app.util.Purchasing.Companion.displayPurchasedBottomSheet
import com.truevast.app.util.Purchasing.Companion.verifySubPurchase
import com.truevast.app.util.bool
import com.truevast.app.util.displayPaymentSuccessPopUp
import com.truevast.app.util.displayPopUp
import com.truevast.app.util.doAfterTextChanged
import com.truevast.app.util.handleApiError
import com.truevast.app.util.openActivity
import com.truevast.app.util.openWebsite
import com.truevast.app.util.toEditable
import com.truevast.app.util.toast
import dagger.hilt.android.AndroidEntryPoint
import io.paperdb.Paper
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.RequestBody.Companion.asRequestBody
import java.io.File
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Collections
import java.util.Locale

@AndroidEntryPoint
class ProfileFragment : Fragment(R.layout.fragment_profile) {
    private lateinit var binding: FragmentProfileBinding
    private val updateUserViewModel: ProfileViewModel by viewModels()
    private lateinit var billingClient: BillingClient
    private val purchasesUpdatedListener = PurchasesUpdatedListener { billingResult, purchases ->
        if (billingResult.responseCode == BillingClient.BillingResponseCode.OK && purchases != null) {
            purchases.forEach {
                requireActivity().verifySubPurchase(it, billingClient)
            }
        }
    }

    private val viewModel: ProfileViewModel by viewModels()
    var user = Paper.book().read<User>(Constant.k_user) ?: User()
    private var profileAdapter = ProfileAdapter()
    private lateinit var mListLinks: MutableList<LinksItem>
    private val itemTouchHelper by lazy {
        val simpleItemTouchCallback =
            object : ItemTouchHelper.SimpleCallback(
                ItemTouchHelper.UP or
                        ItemTouchHelper.DOWN or
                        ItemTouchHelper.START or
                        ItemTouchHelper.END, 0
            ) {

                var from = -1
                var to = -1

                override fun isLongPressDragEnabled() = true
                override fun isItemViewSwipeEnabled() = false

                override fun getMovementFlags(
                    recyclerView: RecyclerView,
                    viewHolder: RecyclerView.ViewHolder
                ): Int {
                    val dragFlags =
                        ItemTouchHelper.UP or ItemTouchHelper.DOWN or ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
                    val swipeFlags =
                        if (isItemViewSwipeEnabled) ItemTouchHelper.START or ItemTouchHelper.END else 0
                    return makeMovementFlags(dragFlags, swipeFlags)
                }

                override fun onSelectedChanged(
                    viewHolder: RecyclerView.ViewHolder?,
                    actionState: Int
                ) {
                    super.onSelectedChanged(viewHolder, actionState)

                    if (actionState == ItemTouchHelper.ACTION_STATE_DRAG) {
                        viewHolder?.itemView?.alpha = 0.5f
                    }
                }

                override fun clearView(
                    recyclerView: RecyclerView,
                    viewHolder: RecyclerView.ViewHolder
                ) {
                    super.clearView(recyclerView, viewHolder)
                    viewHolder.itemView.alpha = 1.0f

                    viewHolder.itemView.requestLayout()
                    val adapter = recyclerView.adapter as ProfileAdapter

                    Log.e("ProfileFragment", "getItems: ${adapter.getItems()}")

                    val requestBody = MultipartBody.Builder().setType(MultipartBody.FORM).apply {
                        addFormDataPart("user_id", user.id.toString())
                        addFormDataPart(
                            "links[]",
                            adapter.getItems()
                        )
                    }.build()
                    viewModel.updateLinksArrangement(requestBody)

                }

                override fun onMove(
                    recyclerView: RecyclerView,
                    viewHolder: RecyclerView.ViewHolder,
                    target: RecyclerView.ViewHolder
                ): Boolean {

                    from = viewHolder.absoluteAdapterPosition
                    to = target.absoluteAdapterPosition

                    if (from < to) {
                        for (i in from until to) {
                            Collections.swap(mListLinks, i, i + 1)
                        }
                    } else {
                        for (i in from downTo to + 1) {
                            Collections.swap(mListLinks, i, i - 1)
                        }
                    }

                    (recyclerView.adapter as ProfileAdapter).notifyItemMoved(from, to)

                    return false
                }

                override fun onSwiped(
                    viewHolder: RecyclerView.ViewHolder,
                    direction: Int
                ) {
                }

            }
        ItemTouchHelper(simpleItemTouchCallback)
    }

    @SuppressLint("NotifyDataSetChanged")
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding = FragmentProfileBinding.bind(view)

        checkExpiryDate()

        binding.btnAddNew.setOnClickListener {
            requireActivity().openActivity(SocialLinkActivity::class.java, false)
        }

        binding.btnSwitchProfile.setOnClickListener {
            requireActivity().showLoading()
            if (binding.btnSwitchProfile.text == getString(R.string.switch_to_business)) {
                viewModel.getUserData(Constant.k_roleBusiness)
            } else {
                viewModel.getUserData(Constant.k_rolePersonal)
            }
        }

        binding.ivUser.setOnClickListener {
            openImagePicker()
        }
        binding.civEdit.setOnClickListener {
            openImagePicker()
        }


        binding.ivCompany.setOnClickListener {

            com.github.drjacky.imagepicker.ImagePicker.with(requireActivity())
                .crop()
                .cropOval()
                .maxResultSize(512, 512, true)
                .provider(com.github.drjacky.imagepicker.constant.ImageProvider.BOTH)
                .createIntentFromDialog {
                    logoImageResult.launch(it)
                }

        }

        binding.civBannerEdit.setOnClickListener {
            com.github.drjacky.imagepicker.ImagePicker.with(requireActivity())
                .crop(16f, 9f)
                .maxResultSize(512, 512, true)
                .provider(com.github.drjacky.imagepicker.constant.ImageProvider.BOTH)
                .createIntentFromDialog {
                    coverImageResult.launch(it)
                }

        }
        binding.ivBanner.setOnClickListener {
            com.github.drjacky.imagepicker.ImagePicker.with(requireActivity())
                .crop(16f, 9f)
                .maxResultSize(512, 512, true)
                .provider(com.github.drjacky.imagepicker.constant.ImageProvider.BOTH)
                .createIntentFromDialog {
                    coverImageResult.launch(it)
                }

        }

        binding.sbPersonal.setOnCheckedChangeListener { _, isChecked ->
            if (isChecked) {
                binding.sbLead.isChecked = false
                if (user.directMode == 0) {
                    if (profileAdapter.mList.isNotEmpty()) {
                        val item = profileAdapter.mList[0]
                        profileAdapter.directMode = true
                        item.isDirect = true
                        viewModel.updateDirectMode(
                            userId = user.id,
                            status = 1,
                            linkId = item.linkId.toString(),
                            linkValue = item.value
                        )
                    }
                }
            } else {
                profileAdapter.directMode = false
                viewModel.updateDirectMode(
                    userId = user.id,
                    status = 0,
                    linkId = null,
                    linkValue = null
                )
            }
            profileAdapter.notifyDataSetChanged()
        }

        binding.tilAbout.setEndIconOnClickListener {
            requireActivity().displayPopUp(
                getString(R.string.information),
                "You can add your bio here"
            )
        }

        binding.tilAbout.editText?.doAfterTextChanged(delay = 2000) {
            if (binding.tilAbout.hasFocus()) {
                updateBio(it)
            }
        }

        viewModel.userResponse.observe(viewLifecycleOwner) {
            when (it) {
                is Resource.Failure -> {
                    requireActivity().handleApiError(it)
                }

                is Resource.Success -> {
                    it.value.response?.let { it1 -> Paper.book().write(Constant.k_user, it1) }
                    requireActivity().cancelLoading()
                    requireActivity().openActivity(HomeActivity::class.java)
                }
            }
        }

    }

    override fun onResume() {
        super.onResume()

        mListLinks = user.mListLinks.toMutableList()

        val profileLink = "${user.baseUrl}"

        binding.ivEdit.setOnClickListener {
//            requireActivity().share("Hey, \nYou can find my profile link below: \n\n$profileLink")
            findNavController().navigate(R.id.nav_edit_profile)
        }

        binding.ivPreview.setOnClickListener {
            requireActivity().openWebsite(profileLink)
        }

        binding.btnInventory.setOnClickListener {
            if (hasActiveSubscription()) {
                findNavController().navigate(R.id.inventoryActivity)
            } else {
//                launchBillingFlow()
                requireActivity().displayPurchasedBottomSheet(billingClient) {
                    if (it!!) {
                        val user = Paper.book().read<User>(Constant.k_user)!!
                        if (Constant.pro) {
                            user.subscription = Constant.k_proMonthly_user
                        } else {
                            user.subscription = Constant.k_basicMonthly_user
                        }
                        user.versionPurchaseDate = Purchasing.getPurchaseDate()
                        user.versionExpiryDate = Purchasing.getExpiryDate()

                        val requestBody =
                            MultipartBody.Builder().setType(MultipartBody.FORM).apply {
                                addFormDataPart("id", user?.id.toString())
                                addFormDataPart("subscription", user?.subscription.toString())
                                addFormDataPart(
                                    "versionPurchaseDate",
                                    user?.versionPurchaseDate.toString()
                                )
                                addFormDataPart(
                                    "versionExpiryDate",
                                    user?.versionExpiryDate.toString()
                                )
                            }.build()
                        requireActivity().showLoading()
                        updateUserViewModel.updateUser(requestBody)
                    }
                }
            }
        }

        if (!::billingClient.isInitialized) {
            launchBillingFlow()
        }

        if (user.role == Constant.k_roleBusiness) {
            setBusinessRoleButton()
            binding.ivCompany.visibility = View.VISIBLE
        } else {
            setSocialRoleButton()
            binding.ivCompany.visibility = View.GONE
        }

        if (user.profileUrl?.isNotEmpty() == true) {
//            loadProfileImage(user.profileUrl, binding.ivUser)
            Glide.with(this)
                .load(user.profileUrl)
                .into(binding.ivUser)
//            loadImage(item?.image, binding.ivSocial)
        }

        if (user.logoUrl?.isNotEmpty() == true) {
            Glide.with(this)
                .load(user.logoUrl)
                .into(binding.ivCompany)
        }

        if (user.coverUrl?.isNotEmpty() == true) {
            Glide.with(this)
                .load(user.coverUrl)
                .into(binding.ivBanner)
        } else {
            Glide.with(this)
                .load(R.drawable.ic_cover_placeholder)
                .into(binding.ivBanner)
        }

        binding.sbLead.isChecked = user.leadMode.bool()

        binding.sbLead.setOnCheckedChangeListener { _, isChecked ->
            if (isChecked) {
                binding.sbPersonal.isChecked = false
            }
            updateLeadMode(isChecked)
        }

        if (user.name?.isNotEmpty() == true) {
            binding.tilFullName.editText?.text = user.name?.toEditable()
            binding.tvFullName.text = user.name
        } else {

        }

        if (user.designation?.isNotEmpty() == true) {
            binding.tvDesignation.text = user.designation
        } else {
            binding.tvDesignation.visibility = View.GONE
        }

        if (user.company?.isNotEmpty() == true) {
            binding.tvCompany.text = user.company
        } else {
            binding.tvCompany.visibility = View.GONE
        }

        if (user.bio?.isNotEmpty() == true) {
            binding.tilAbout.editText?.text = user.bio?.toEditable()
        } else {
        }

        if (mListLinks.isNotEmpty()) {
            user.directMode.bool().apply {
                binding.sbPersonal.isChecked = this
                profileAdapter.directMode = this
                if (this) {
                    mListLinks.forEach {
                        if (it.linkId == user.directLinkId) {
                            it.isDirect = true
                        }
                    }
                }
            }
            profileAdapter.apply {
                mList = mListLinks
                itemClickListener = { item, isChecked, directMode ->
                    if (!directMode) {
                        updateSocialLink(item, isChecked)
                    } else {
                        viewModel.updateDirectMode(
                            userId = user.id,
                            status = 1,
                            linkId = item?.linkId.toString(),
                            linkValue = item?.value
                        )
                    }
                }
            }

            binding.rvSocialLinks.apply {
                adapter = profileAdapter
                itemTouchHelper.attachToRecyclerView(this)
            }

        }

        Constant.k_purchasing.observe(requireActivity())
        {
            Log.e("Profile", "onResume: $it")
        }
        updateUserViewModel.updateUser.observe(viewLifecycleOwner, EventObserver {
            Log.e("EditContactActivity", "onCreate: $it")
            requireActivity().cancelLoading()
            when (it) {
                is Resource.Failure -> {
                    requireActivity().handleApiError(it)
                }

                is Resource.Success -> {
                    Log.e("Profile", "onResume: ${it.value.response}")
                    requireActivity().displayPaymentSuccessPopUp(
                        getString(R.string.success),
                        resources.getString(R.string.subscribed_successfully),
                        object : GeneralListener {
                            override fun buttonClick(clicked: Boolean) {
                                it.value.response?.let { user ->
                                     Paper.book().write(Constant.k_user, user)
                                }
                                requireActivity().openActivity(
                                    InventoryActivity::class.java,
                                    false
                                )
                            }
                        })
                }
            }
        })
    }

    private fun hasActiveSubscription(): Boolean {
        val subscription = Paper.book().read<User>(Constant.k_user)?.subscription
        return subscription == Constant.k_basicMonthly_user || subscription == Constant.k_proMonthly_user
    }

    private fun launchBillingFlow() {
        billingClient = BillingClient.newBuilder(requireActivity())
            .setListener(purchasesUpdatedListener)
            .enablePendingPurchases()
            .build()
    }

    private val profileImageResult =
        registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {

            val resultCode = it.resultCode
            val data = it.data

            when (resultCode) {
                Activity.RESULT_OK -> {
                    val fileUri = data?.data!!
                    binding.ivUser.setImageURI(fileUri)
                    updateImage("profileUrl", fileUri)
                }

                ImagePicker.RESULT_ERROR -> {
                    toast { ImagePicker.getError(data) }
                }

                else -> {
                    toast { "Task Cancelled" }
                }
            }

        }

    private val coverImageResult =
        registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
            val resultCode = it.resultCode
            val data = it.data

            when (resultCode) {
                Activity.RESULT_OK -> {
                    val fileUri = data?.data!!
//                    loadImage(fileUri,binding.ivBanner)
                    Glide.with(this)
                        .load(fileUri)
                        .into(binding.ivBanner)

//                    binding.ivBanner.setImageURI(fileUri)
                    updateImage("coverUrl", fileUri)
                }

                ImagePicker.RESULT_ERROR -> {
                    toast { ImagePicker.getError(data) }
                }

                else -> {
                    toast { "Task Cancelled" }
                }
            }
        }

    private val logoImageResult =
        registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
            val resultCode = it.resultCode
            val data = it.data

            when (resultCode) {
                Activity.RESULT_OK -> {
                    val fileUri = data?.data!!
                    binding.ivCompany.setImageURI(fileUri)
                    updateImage("logoUrl", fileUri)
                }

                ImagePicker.RESULT_ERROR -> {
                    toast { ImagePicker.getError(data) }
                }

                else -> {
                    toast { "Task Cancelled" }
                }
            }
        }

    private fun setBusinessRoleButton() {
        binding.btnSwitchProfile.text = getString(R.string.switch_to_social)
//        binding.btnSwitchProfile.iconGravity = MaterialButton.ICON_GRAVITY_TEXT_END
        binding.btnSwitchProfile.iconTint =
            ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.white))
        binding.btnSwitchProfile.backgroundTintList =
            ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.blue))
        binding.btnSwitchProfile.strokeColor =
            ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.blue))
        binding.btnSwitchProfile.setTextColor(
            ContextCompat.getColor(
                requireContext(),
                R.color.white
            )
        )
    }

    private fun setSocialRoleButton() {
        binding.btnSwitchProfile.text = getString(R.string.switch_to_business)
//        binding.btnSwitchProfile.iconGravity = MaterialButton.ICON_GRAVITY_TEXT_START
        binding.btnSwitchProfile.iconTint =
            ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.black))
        binding.btnSwitchProfile.backgroundTintList =
            ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.white))
        binding.btnSwitchProfile.strokeColor =
            ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.black))
        binding.btnSwitchProfile.setTextColor(
            ContextCompat.getColor(
                requireContext(),
                R.color.black
            )
        )
    }

    private fun updateImage(key: String, value: Uri) {
        val requestBody = MultipartBody.Builder().setType(MultipartBody.FORM).apply {
            addFormDataPart("id", user.id.toString())
            val file = File(value.path!!)
            addFormDataPart(
                key, "${file.name}.jpg", file.asRequestBody(
                    "multipart/form-data".toMediaTypeOrNull()
                )
            )
        }.build()
        viewModel.updateUser(requestBody)
//        viewModel.updateUser.observe(viewLifecycleOwner) {
//
//            when (it) {
//                is Resource.Failure -> {
//                    requireActivity().handleApiError(it)
//                }
//
//                is Resource.Success -> {
//                    it.value.response?.let { updatedUser ->
//                        when (key) {
//                            "profileUrl" -> user.profileUrl = updatedUser.profileUrl
//                            "coverUrl" -> user.coverUrl = updatedUser.coverUrl
//                            "logoUrl" -> user.logoUrl = updatedUser.logoUrl
//                        }
//
//                        when (key) {
//                            "profileUrl" -> Glide.with(this).load(user.profileUrl)
//                                .into(binding.ivUser)
//
//                            "coverUrl" -> Glide.with(this).load(user.coverUrl)
//                                .into(binding.ivBanner)
//
//                            "logoUrl" -> Glide.with(this).load(user.logoUrl)
//                                .into(binding.ivCompany)
//                        }
//                        Paper.book().write(Constant.k_user, user)
//                    }
//                }
//            }
//        }
    }

    private fun updateBio(bio: String) {
        val requestBody = MultipartBody.Builder().setType(MultipartBody.FORM).apply {
            addFormDataPart("id", user.id.toString())
            addFormDataPart("bio", bio)
        }.build()
        viewModel.updateUser(requestBody)
//        viewModel.updateUser.observe(viewLifecycleOwner) {
//            Log.e("ProfileFragment", "updateBio: $it")
//            when (it) {
//                is Resource.Failure -> {
//                    requireActivity().handleApiError(it)
//                }
//
//                is Resource.Success -> {
//                    it.value.response?.let { it1 -> Paper.book().write(Constant.k_user, it1) }
//                }
//            }
//        }
    }

   
    private fun updateSocialLink(item: LinksItem?, isChecked: Boolean) {
        requireActivity().showLoading()
        viewModel.updateSocialLink(item?.linkId, user.id, if (isChecked) 1 else 0)
        viewModel.updateSocialLink.observe(viewLifecycleOwner, EventObserver {
            requireActivity().cancelLoading()
            Log.e("ProfileFragment", "updateSocialLink: $it")
            when (it) {
                is Resource.Failure -> {
                    requireActivity().handleApiError(it)
                }

                is Resource.Success -> {
                    if (isChecked) {
                        mListLinks?.get(mListLinks!!.indexOf(item))?.status = 1
                    } else {
                        mListLinks?.get(mListLinks!!.indexOf(item))?.status = 0
                    }
                    Paper.book().write(Constant.k_user, user)
                }
            }
        })
    }


    override fun onPause() {
        super.onPause()
        viewModel.userResponse.removeObservers(viewLifecycleOwner)
        updateUserViewModel.updateUser.removeObservers(viewLifecycleOwner)
    }
}
0

There are 0 best solutions below