diff --git a/app/build.gradle b/app/build.gradle index 6aaaf4b1d8..f1e7b1b1c0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -23,10 +23,12 @@ kapt { } + def VERSION_CODE = 295 def VERSION_NAME = "3.3.2" + android { namespace 'com.naviapp' compileSdk 32 diff --git a/navi-amc/src/main/java/com/navi/amc/common/adapter/PennyDropOptionsAdapter.kt b/navi-amc/src/main/java/com/navi/amc/common/adapter/PennyDropOptionsAdapter.kt new file mode 100644 index 0000000000..d88b01b475 --- /dev/null +++ b/navi-amc/src/main/java/com/navi/amc/common/adapter/PennyDropOptionsAdapter.kt @@ -0,0 +1,86 @@ +package com.navi.amc.common.adapter + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.core.view.isVisible +import androidx.databinding.DataBindingUtil +import androidx.recyclerview.widget.RecyclerView +import com.navi.amc.R +import com.navi.amc.common.model.PennyDropOption +import com.navi.amc.databinding.PennyDropOptionBinding +import com.navi.amc.utils.ColorUtils +import com.navi.amc.utils.orFalse +import com.navi.base.utils.isValidIndex +import com.navi.design.utils.* + +class PennyDropOptionsAdapter( + private val items: List, + val listener: ((PennyDropOption) -> Unit)? = null +) : RecyclerView.Adapter() { + var lastCheckedPosition = -1 + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PennyDropOptionsViewHolder { + return PennyDropOptionsViewHolder( + DataBindingUtil.inflate( + LayoutInflater.from(parent.context), + R.layout.penny_drop_option, + parent, + false + ) + ) + } + + override fun getItemCount(): Int { + return items.size + } + + override fun onBindViewHolder(holder: PennyDropOptionsViewHolder, position: Int) { + if (!isValidIndex(position, itemCount)) return + val itemData = items[position] + holder.binding.apply { + title.setSpannableString(itemData.title) + subtitle.setSpannableString(itemData.subTitle) + root.background = + ColorUtils.getDarkGreyBorder8StrokeDrawable(holder.binding.root.context) + label.isVisible = itemData.label?.let { + label.setSpannableString(it.title) + label.background = getNaviDrawable( + radii = CornerRadius( + leftBottom = dpToPx(4), + rightTop = dpToPx(8) + ), backgroundColor = it.bgColor.parseColorSafe() + ) + true + } ?: run { + false + } + if (lastCheckedPosition == -1 && itemData.isSelected.orFalse()) { + lastCheckedPosition = position + listener?.invoke(itemData) + } + radio.isChecked = (lastCheckedPosition == position) + if (radio.isChecked || itemData.toShowNote.orFalse()) { + note.setSpannableString(itemData.note?.title) + note.background = getNaviDrawable( + radii = CornerRadius( + leftBottom = dpToPx(8), + rightBottom = dpToPx(8) + ), backgroundColor = itemData.note?.bgColor.parseColorSafe() + ) + } else { + note.isVisible = false + } + radio.setOnClickListener { + val tempLastCheckedPosition = lastCheckedPosition + lastCheckedPosition = position + notifyItemChanged(tempLastCheckedPosition) + notifyItemChanged(lastCheckedPosition) + listener?.invoke(itemData) + } + } + } + +} + +class PennyDropOptionsViewHolder(val binding: PennyDropOptionBinding) : + RecyclerView.ViewHolder(binding.root) diff --git a/navi-amc/src/main/java/com/navi/amc/common/fragment/PennyDropOptionsFragment.kt b/navi-amc/src/main/java/com/navi/amc/common/fragment/PennyDropOptionsFragment.kt new file mode 100644 index 0000000000..8e1aac922d --- /dev/null +++ b/navi-amc/src/main/java/com/navi/amc/common/fragment/PennyDropOptionsFragment.kt @@ -0,0 +1,244 @@ +package com.navi.amc.common.fragment + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import android.text.TextUtils +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.content.ContextCompat +import androidx.databinding.DataBindingUtil +import androidx.fragment.app.viewModels +import androidx.recyclerview.widget.DividerItemDecoration +import com.navi.amc.R +import com.navi.amc.common.activity.CheckerActivity +import com.navi.amc.common.adapter.PennyDropOptionsAdapter +import com.navi.amc.common.model.AdditionalDataAsyncResponse +import com.navi.amc.common.model.NextCtaResponse +import com.navi.amc.common.viewmodel.PennyDropOptionsViewModel +import com.navi.amc.databinding.PennyDropOptionsLayoutBinding +import com.navi.amc.utils.AmcAnalytics.BANK_VERIFICATION_OPTIONS +import com.navi.amc.utils.AmcAnalytics.CHANGE_BANK +import com.navi.amc.utils.AmcAnalytics.PENNY_DROP_OPTIONS +import com.navi.amc.utils.Constant.RPD +import com.navi.amc.utils.Constant.TOKEN +import com.navi.amc.utils.Constant.UPILINK +import com.navi.amc.utils.orValue +import com.navi.base.model.ActionData +import com.navi.base.model.CtaData +import com.navi.common.firebasedb.FirebaseStatusType +import com.navi.common.listeners.FragmentInterchangeListener +import com.navi.common.listeners.HeaderInteractionListener +import com.navi.common.model.RequestConfig +import com.navi.common.utils.ApiPollScheduler +import com.navi.common.utils.log +import com.navi.common.utils.observeNonNull +import com.navi.design.textview.model.NaviSpan +import com.navi.design.textview.model.TextWithStyle +import com.navi.design.utils.parseColorSafe +import com.navi.design.utils.setSpannableString +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class PennyDropOptionsFragment() : AmcBaseFragment() { + + private lateinit var binding: PennyDropOptionsLayoutBinding + private val viewModel by viewModels() + private lateinit var adapter: PennyDropOptionsAdapter + private var apiPollScheduler: ApiPollScheduler? = null + + override val screenName: String + get() = BANK_VERIFICATION_OPTIONS + + override fun onAttach(context: Context) { + super.onAttach(context) + headerInteractionListener = context as? HeaderInteractionListener + fragmentInterchangeListener = context as? FragmentInterchangeListener + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = + DataBindingUtil.inflate(inflater, R.layout.penny_drop_options_layout, container, false) + sendInitEvent() + initError(viewModel, showNewDesignSystemFragment = true, buttonListener = { + when(it){ + CHANGE_BANK -> { + fragmentInterchangeListener?.navigateToNextScreen(actionData = ActionData(url = "bank_details")) + } + else ->{ + + } + } + }) + initObservers() + fetchData() + return binding.root + } + + private fun sendInitEvent(){ + sendEvent(PENNY_DROP_OPTIONS) + } + + private fun initObservers() { + viewModel.asyncResponse.observeNonNull(viewLifecycleOwner){ + if (it != null) { + onPollingResponse(it) + } + } + + viewModel.requestConfig.observeNonNull(viewLifecycleOwner){ + hideLoader() + apiPollInit(it?.requestConfig) + } + viewModel.rpdPaymentDetails.observeNonNull(viewLifecycleOwner){ + it?.methodDetails?.get(UPILINK)?.let{ + upiIntent(it) + } + it?.tokenDetails?.requestId?.let{ + viewModel.requestId = it + } + } + viewModel.tokenExpire.observeNonNull(viewLifecycleOwner){ + activity?.finish() + } + viewModel.pennyDropOptionsData.observeNonNull(viewLifecycleOwner) { data -> + hideLoader() + data?.header?.let { + headerInteractionListener?.setProperties(it) + } + data?.content?.let { + binding.title.setSpannableString(it.title) + binding.subtitle.setSpannableString(it.subTitle) + } + data?.content?.items?.let { items -> + adapter = PennyDropOptionsAdapter( + items = items + ) { + viewModel.paymentMethod = it + } + val itemDecorator = DividerItemDecoration(context, DividerItemDecoration.VERTICAL) + context?.let { + ContextCompat.getDrawable(it, R.drawable.empty_space_divider)?.let { drawable -> + itemDecorator.setDrawable(drawable) + } + } + binding.options.addItemDecoration(itemDecorator) + binding.options.adapter = adapter + } + data?.footer?.let { + val termsText = it.title?.text + it.name?.text + val style: MutableList = ArrayList() + it.name?.style?.let { it1 -> style.addAll(it1) } + it.title?.style?.let { it1 -> style.addAll(it1) } + binding.terms.setSpannableString(TextWithStyle(text = termsText, style = style)) + binding.footer.btn.apply { + text = it.nextCta?.title + setTextColor(it.nextCta?.titleColor.parseColorSafe()) + setOnClickListener { + if (viewModel.paymentMethod?.id.orEmpty() == RPD) + arguments?.getString(TOKEN) + ?.let { it -> viewModel.fetchInitPaymentMethodDetails(token = it, methodId = RPD) } + else { + val action = viewModel.paymentMethod?.actionData + fragmentInterchangeListener?.navigateToNextScreen(action) + } + } + } + } + val method = data?.content?.autoSelectMethod + val list = data?.content?.items + if (method.isNullOrEmpty().not()) { + val item = list?.singleOrNull { it.id == method } + if (item?.id.orEmpty() == RPD) + arguments?.getString(TOKEN) + ?.let { it -> viewModel.fetchInitPaymentMethodDetails(token = it, methodId = RPD) } + else { + activity?.supportFragmentManager?.popBackStack() + fragmentInterchangeListener?.navigateToNextScreen(actionData = item?.actionData) + } + } + } + } + + private fun apiPollInit( + requestConfig : RequestConfig? + ) { + apiPollScheduler = + ApiPollScheduler( + initialDelay = + requestConfig?.initialDelay.orValue(0).toLong(), + numberOfRetry = requestConfig + ?.numOfRetries + .orValue(CheckerActivity.DEFAULT_RETRY_COUNT), + pollInterval = + requestConfig + ?.interval + .orValue(ApiPollScheduler.API_POLL_REPEAT_PERIOD_SECONDS.toInt()) + .toLong(), + doOnTimeout = { activity?.runOnUiThread { apiPollScheduler?.stopApiPoll() + } } + ) { + showLoader() + viewModel.checkApiPollStatus() + } + apiPollScheduler?.scheduleApiPoll() + } + + private fun upiIntent(link: String) { + try { + val intent = Intent().apply { + setAction(Intent.ACTION_VIEW) + setData(Uri.parse(link)) + } + startActivityForResult(intent, REQUEST_CODE) + } catch (ex: Exception) { + ex.log() + } + } + + private fun onPollingResponse(additonalData: AdditionalDataAsyncResponse) { + when { + TextUtils.equals(additonalData.status, FirebaseStatusType.SUCCESS) -> { + hideLoader() + apiPollScheduler?.stopApiPoll() + fragmentInterchangeListener?.navigateToNextScreen(additonalData.data?.nextCTA) + } + TextUtils.equals(additonalData.status, FirebaseStatusType.FAILURE) -> { + hideLoader() + apiPollScheduler?.stopApiPoll() + } + } + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + showLoader() + viewModel.getRequestConfig() + arguments?.getString(TOKEN)?.let{ + viewModel.postPaymentSignal(it, RPD) + } + } + + private fun fetchData() { + showLoader() + arguments?.getString(TOKEN)?.let{ + viewModel.fetchData(it) + } + } + + companion object { + const val REQUEST_CODE = 5001 + fun newInstance(bundle: Bundle): PennyDropOptionsFragment { + return PennyDropOptionsFragment().apply { + arguments = bundle + } + } + } +} \ No newline at end of file diff --git a/navi-amc/src/main/java/com/navi/amc/common/fragment/RpdSuccessFragment.kt b/navi-amc/src/main/java/com/navi/amc/common/fragment/RpdSuccessFragment.kt new file mode 100644 index 0000000000..3c9207782f --- /dev/null +++ b/navi-amc/src/main/java/com/navi/amc/common/fragment/RpdSuccessFragment.kt @@ -0,0 +1,137 @@ +package com.navi.amc.common.fragment + +import android.content.Context +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.databinding.DataBindingUtil +import androidx.fragment.app.viewModels +import com.navi.amc.R +import com.navi.amc.common.listener.FooterInteractionListener +import com.navi.amc.common.view.ShimmerNoteView +import com.navi.amc.common.viewmodel.RpdSuccessViewModel +import com.navi.amc.databinding.KeyValueItemLayoutBinding +import com.navi.amc.databinding.RpdSuccessLayoutBinding +import com.navi.amc.utils.AmcAnalytics.RPD_SUCCESS_SCREEN +import com.navi.amc.utils.Constant.CHANGE_BANK +import com.navi.amc.utils.Constant.CONFIRMED +import com.navi.amc.utils.SubPageStatusType.RPD_SUCCESS +import com.navi.base.model.ActionData +import com.navi.common.listeners.FragmentInterchangeListener +import com.navi.common.listeners.HeaderInteractionListener +import com.navi.common.utils.observeNonNull +import com.navi.design.utils.dpToPxInInt +import com.navi.design.utils.setSpannableString +import com.navi.naviwidgets.extensions.showWhenDataIsAvailable +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class RpdSuccessFragment() : AmcBaseFragment(), FooterInteractionListener { + private lateinit var binding: RpdSuccessLayoutBinding + private val viewModel by viewModels() + + override val screenName: String + get() = RPD_SUCCESS + + override fun onAttach(context: Context) { + super.onAttach(context) + headerInteractionListener = context as HeaderInteractionListener? + fragmentInterchangeListener = context as FragmentInterchangeListener? + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = DataBindingUtil.inflate(inflater, R.layout.rpd_success_layout, container, false) + sendInitEvent() + initError(viewModel, showNewDesignSystemFragment = true) + initObservers() + fetchData() + return binding.root + } + + private fun fetchData() { + showLoader() + viewModel.fetchData() + } + + private fun sendInitEvent(){ + sendEvent(RPD_SUCCESS_SCREEN) + } + + private fun initObservers() { + viewModel.rpdSuccessData.observeNonNull(viewLifecycleOwner) { + hideLoader() + it.header?.let { + headerInteractionListener?.setProperties(it) + headerInteractionListener?.hideDivider() + } + it.content?.let { + binding.icon.showWhenDataIsAvailable(it.iconCode) + binding.title.setSpannableString(it.title) + binding.subtitle.setSpannableString(it.subtitle) + binding.container.removeAllViews() + it.detailsData?.items?.forEach { data -> + val inflater = LayoutInflater.from(context) + val view = inflater.inflate( + R.layout.key_value_item_layout, + binding.container, + false + ) + view.layoutParams = ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) + val childBinding = DataBindingUtil.bind(view) + childBinding?.key?.setSpannableString(data.key) + childBinding?.value?.setSpannableString(data.value) + binding.container.addView(view) + } + val view = View(context) + view.layoutParams = ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + dpToPxInInt(24) + ) + binding.container.addView(view) + it.detailsData?.note?.let { note -> + val view = context?.let { it -> ShimmerNoteView(it) } + view?.setProperties(note) + binding.container.addView(view) + } + it.detailsData?.dataSafeWidget?.let { + binding.dataSafe.data = it + } + val space = View(context) + space.layoutParams = ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + dpToPxInInt(16) + ) + binding.container.addView(space) + } + it.footer?.let { + binding.footer.setProperties(it, this) + } + } + } + + companion object { + fun newInstance(bundle: Bundle): RpdSuccessFragment { + return RpdSuccessFragment().apply { + arguments = bundle + } + } + } + + override fun onFooterBackPress(actionData: ActionData?) { + viewModel.postStatus(CHANGE_BANK) + fragmentInterchangeListener?.navigateToNextScreen(actionData) + } + + override fun onFooterNextPress(actionData: ActionData?, skipValidation: Boolean?) { + viewModel.postStatus(CONFIRMED) + fragmentInterchangeListener?.navigateToNextScreen(actionData) + } +} \ No newline at end of file diff --git a/navi-amc/src/main/java/com/navi/amc/common/model/Footer.kt b/navi-amc/src/main/java/com/navi/amc/common/model/Footer.kt index 4cf03f09ae..146fb38342 100644 --- a/navi-amc/src/main/java/com/navi/amc/common/model/Footer.kt +++ b/navi-amc/src/main/java/com/navi/amc/common/model/Footer.kt @@ -15,6 +15,7 @@ import kotlinx.parcelize.Parcelize @Parcelize data class Footer( + @SerializedName("customerName") var name: TextWithStyle? = null, @SerializedName("title") var title: TextWithStyle? = null, @SerializedName("nextCta") var nextCta: ActionData? = null, @SerializedName("backCta") var backCta: ActionData? = null, diff --git a/navi-amc/src/main/java/com/navi/amc/common/model/PennyDropOptionsScreenData.kt b/navi-amc/src/main/java/com/navi/amc/common/model/PennyDropOptionsScreenData.kt new file mode 100644 index 0000000000..272b83d14c --- /dev/null +++ b/navi-amc/src/main/java/com/navi/amc/common/model/PennyDropOptionsScreenData.kt @@ -0,0 +1,47 @@ +package com.navi.amc.common.model + +import com.google.gson.annotations.SerializedName +import com.navi.amc.kyc.model.Note +import com.navi.base.model.ActionData +import com.navi.common.model.Header +import com.navi.common.model.LabelData +import com.navi.design.textview.model.TextWithStyle + +data class PennyDropOptionsScreenData( + @SerializedName("header") + val header: Header? = null, + @SerializedName("content") + val content: PennyDropOptionsContent? = null, + @SerializedName("footer") + val footer: Footer? = null +) + +data class PennyDropOptionsContent( + @SerializedName("title") + val title: TextWithStyle? = null, + @SerializedName("subTitle") + val subTitle: TextWithStyle? = null, + @SerializedName("items") + val items: List? = null, + @SerializedName("autoSelectMethod") + val autoSelectMethod: String? = null +) + +data class PennyDropOption( + @SerializedName("label") + val label: LabelData? = null, + @SerializedName("title") + val title: TextWithStyle? = null, + @SerializedName("isSelected") + val isSelected: Boolean? = null, + @SerializedName("id") + val id: String? = null, + @SerializedName("subTitle") + val subTitle: TextWithStyle? = null, + @SerializedName("toShowNote") + val toShowNote: Boolean? = null, + @SerializedName("note") + val note: Note? = null, + @SerializedName("actionData") + val actionData : ActionData ?= null +) diff --git a/navi-amc/src/main/java/com/navi/amc/common/model/RpdPaymentDetails.kt b/navi-amc/src/main/java/com/navi/amc/common/model/RpdPaymentDetails.kt new file mode 100644 index 0000000000..905beb5f10 --- /dev/null +++ b/navi-amc/src/main/java/com/navi/amc/common/model/RpdPaymentDetails.kt @@ -0,0 +1,15 @@ +package com.navi.amc.common.model + +import com.google.gson.annotations.SerializedName + +data class RpdPaymentDetails( + @SerializedName("tokenDetails") + val tokenDetails: TokenDetails? = null, + @SerializedName("methodDetails") + val methodDetails: Map? = null +) + +data class TokenDetails( + @SerializedName("requestId") + val requestId: String? = null +) \ No newline at end of file diff --git a/navi-amc/src/main/java/com/navi/amc/common/model/RpdSuccessScreenData.kt b/navi-amc/src/main/java/com/navi/amc/common/model/RpdSuccessScreenData.kt new file mode 100644 index 0000000000..126f7d089f --- /dev/null +++ b/navi-amc/src/main/java/com/navi/amc/common/model/RpdSuccessScreenData.kt @@ -0,0 +1,43 @@ +package com.navi.amc.common.model + +import com.google.gson.annotations.SerializedName +import com.navi.amc.kyc.model.Note +import com.navi.common.model.Header +import com.navi.design.textview.model.TextWithStyle +import com.navi.naviwidgets.models.response.DataSafeWidget + +data class RpdSuccessScreenData( + @SerializedName("header") + val header: Header? = null, + @SerializedName("content") + val content: RpdSuccessContentData? = null, + @SerializedName("footer") + val footer: Footer? = null +) + +data class RpdSuccessContentData( + @SerializedName("iconCode") + val iconCode: String? = null, + @SerializedName("title") + val title: TextWithStyle? = null, + @SerializedName("subTitle") + val subtitle: TextWithStyle? = null, + @SerializedName("details") + val detailsData: DetailsData? =null +) + +data class DetailsData( + @SerializedName("items") + val items: List? = null, + @SerializedName("note") + val note: Note? = null, + @SerializedName("dataSafeWidget") + val dataSafeWidget: DataSafeWidget? = null +) + +data class KeyValueData( + @SerializedName("key") + val key: TextWithStyle? = null, + @SerializedName("value") + val value: TextWithStyle? = null +) \ No newline at end of file diff --git a/navi-amc/src/main/java/com/navi/amc/common/repo/PennyDropOptionsRepository.kt b/navi-amc/src/main/java/com/navi/amc/common/repo/PennyDropOptionsRepository.kt new file mode 100644 index 0000000000..b5feb74ece --- /dev/null +++ b/navi-amc/src/main/java/com/navi/amc/common/repo/PennyDropOptionsRepository.kt @@ -0,0 +1,50 @@ +package com.navi.amc.common.repo + +import com.google.gson.reflect.TypeToken +import com.navi.amc.common.model.PennyDropOptionsScreenData +import com.navi.amc.network.retrofit.RetrofitService +import com.navi.amc.utils.Constant.PAYMENTS +import com.navi.amc.utils.mockApiResponse +import com.navi.common.network.models.RepoResult +import com.navi.common.network.retrofit.ResponseCallback +import javax.inject.Inject + + +class PennyDropOptionsRepository @Inject constructor(private val retrofitService: RetrofitService) : + ResponseCallback() { + + /*suspend fun fetchPennyDropOptions( + token: String, + xTarget: String = PAYMENTS + ): RepoResult { + val type = object : TypeToken() {}.type + return mockApiResponse(type, "penny_drop") + }*/ + + + suspend fun fetchPennyDropOptions( + token: String, + xTarget: String = PAYMENTS + ) = apiResponseCallback(retrofitService.fetchPennyDropOptions(token, xTarget)) + + /*suspend fun fetchInitPaymentMethodDetails( + token:String, + xTarget: String = PAYMENTS + ) :RepoResult<>{ + val type = object : TypeToken() {}.type + return mockApiResponse(type, "penny_drop") + }*/ + suspend fun fetchInitPaymentMethodDetails( + methodId: String, + token: String, + xTarget: String = PAYMENTS + ) = apiResponseCallback(retrofitService.fetchInitPaymentMethodDetails(methodId, token, xTarget)) + + suspend fun postSignal(token: String, method: String, xTarget: String = PAYMENTS) = + retrofitService.postPaymentStatus(token, method, xTarget) + + suspend fun getRequestConfig(requestId: String?) = + apiResponseCallback(retrofitService.fetchRequestConfig(requestId)) + + suspend fun checkApiPollStatus(requestId: String?) = apiResponseCallback(retrofitService.checkApiPollStatus(requestId)) +} \ No newline at end of file diff --git a/navi-amc/src/main/java/com/navi/amc/common/repo/RpdSuccessRepository.kt b/navi-amc/src/main/java/com/navi/amc/common/repo/RpdSuccessRepository.kt new file mode 100644 index 0000000000..9ebbcafeba --- /dev/null +++ b/navi-amc/src/main/java/com/navi/amc/common/repo/RpdSuccessRepository.kt @@ -0,0 +1,18 @@ +package com.navi.amc.common.repo + +import com.navi.amc.network.retrofit.RetrofitService +import com.navi.common.network.retrofit.ResponseCallback +import javax.inject.Inject + +class RpdSuccessRepository @Inject constructor(private val retrofitService: RetrofitService) : + ResponseCallback() { + + /*suspend fun fetchRpdSuccess() : RepoResult { + val type = object : TypeToken() {}.type + return mockApiResponse(type, "penny_success") + }*/ + + suspend fun fetchRpdSuccess() = apiResponseCallback(retrofitService.fetchRpdConfirmDetails()) + + suspend fun postStatus(status : String) = apiResponseCallback(retrofitService.postStatus(status)) +} \ No newline at end of file diff --git a/navi-amc/src/main/java/com/navi/amc/common/view/ShimmerNoteView.kt b/navi-amc/src/main/java/com/navi/amc/common/view/ShimmerNoteView.kt new file mode 100644 index 0000000000..a9072964cc --- /dev/null +++ b/navi-amc/src/main/java/com/navi/amc/common/view/ShimmerNoteView.kt @@ -0,0 +1,45 @@ +package com.navi.amc.common.view + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.view.isVisible +import androidx.databinding.DataBindingUtil +import com.navi.amc.R +import com.navi.amc.databinding.ShimmerNoteViewBinding +import com.navi.amc.kyc.model.Note +import com.navi.base.utils.orFalse +import com.navi.design.utils.* +import com.navi.naviwidgets.utils.REWARDS_TOOLTIP_ANIMATION_DELAY + +class ShimmerNoteView(context: Context, attributes: AttributeSet? = null) : + ConstraintLayout(context, attributes) { + private var binding: ShimmerNoteViewBinding + + init { + val inflater = LayoutInflater.from(context) + layoutParams = LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT) + binding = DataBindingUtil.inflate(inflater, R.layout.shimmer_note_view, this, true) + } + + fun setProperties(data: Note) { + binding.title.setSpannableString(data.title) + if (data.toShowShimmer.orFalse()) { + binding.shimmer.isVisible = true + binding.root.post { + moveViewWithDistance( + binding.shimmer, + binding.root.width, + REWARDS_TOOLTIP_ANIMATION_DELAY + ) + } + } + binding.root.background = getNaviDrawable( + cornerRadius = dpToPxInInt(8), + backgroundColor = data.bgColor.parseColorSafe() + ) + } + +} \ No newline at end of file diff --git a/navi-amc/src/main/java/com/navi/amc/common/viewmodel/PennyDropOptionsViewModel.kt b/navi-amc/src/main/java/com/navi/amc/common/viewmodel/PennyDropOptionsViewModel.kt new file mode 100644 index 0000000000..eb6c79a6ab --- /dev/null +++ b/navi-amc/src/main/java/com/navi/amc/common/viewmodel/PennyDropOptionsViewModel.kt @@ -0,0 +1,99 @@ +package com.navi.amc.common.viewmodel + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.viewModelScope +import com.navi.amc.common.model.* +import com.navi.amc.common.repo.PennyDropOptionsRepository +import com.navi.amc.utils.Constant.INVALID_TOKEN +import com.navi.common.model.UploadDataAsyncResponse +import com.navi.common.network.models.RepoResult +import com.navi.common.viewmodel.BaseVM +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class PennyDropOptionsViewModel @Inject constructor(private val repository: PennyDropOptionsRepository) : + BaseVM() { + + var requestId: String? = null + + private val _pennyDropOptionsData = MutableLiveData() + val pennyDropOptionsData: LiveData + get() = _pennyDropOptionsData + + private val _rpdPaymentDetails = MutableLiveData() + val rpdPaymentDetails: LiveData + get() = _rpdPaymentDetails + + private val _tokenExpire = MutableLiveData() + val tokenExpire: LiveData + get() = _tokenExpire + + private val _requestConfig = MutableLiveData() + val requestConfig: LiveData + get() = _requestConfig + + private val _asyncResponse = MutableLiveData?>() + val asyncResponse: LiveData?> + get() = _asyncResponse + + var paymentMethod: PennyDropOption? = null + + + fun fetchData(token: String) { + viewModelScope.launch { + val response = repository.fetchPennyDropOptions(token = token) + if (response.error == null && response.errors.isNullOrEmpty()) + _pennyDropOptionsData.value = response.data + else { + if (response.errors?.singleOrNull { it.code == INVALID_TOKEN } != null) { + _tokenExpire.value = true + } else { + setErrorData(response.errors, response.error) + } + } + } + } + + fun fetchInitPaymentMethodDetails(token: String, methodId: String) { + viewModelScope.launch { + val response = + repository.fetchInitPaymentMethodDetails(token = token, methodId = methodId) + if (response.error == null && response.errors.isNullOrEmpty()) { + _rpdPaymentDetails.value = response.data + } else { + setErrorData(response.errors, response.error) + } + } + } + + fun postPaymentSignal(token: String, method: String) { + viewModelScope.launch { + repository.postSignal(token, method) + } + } + + fun getRequestConfig() { + viewModelScope.launch { + val response = repository.getRequestConfig(requestId) + if (response.error == null && response.errors.isNullOrEmpty()) { + _requestConfig.value = response.data + } else { + setErrorData(response.errors, response.error) + } + } + } + + fun checkApiPollStatus() { + viewModelScope.launch { + val response = repository.checkApiPollStatus(requestId) + if (response.error == null && response.errors.isNullOrEmpty()) { + _asyncResponse.value = response.data + } else { + setErrorData(response.errors, response.error) + } + } + } +} \ No newline at end of file diff --git a/navi-amc/src/main/java/com/navi/amc/common/viewmodel/RpdSuccessViewModel.kt b/navi-amc/src/main/java/com/navi/amc/common/viewmodel/RpdSuccessViewModel.kt new file mode 100644 index 0000000000..0f87b8d86a --- /dev/null +++ b/navi-amc/src/main/java/com/navi/amc/common/viewmodel/RpdSuccessViewModel.kt @@ -0,0 +1,45 @@ +package com.navi.amc.common.viewmodel + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.viewModelScope +import com.navi.amc.common.model.RpdSuccessScreenData +import com.navi.amc.common.repo.RpdSuccessRepository +import com.navi.common.constants.API_SUCCESS_CODE +import com.navi.common.viewmodel.BaseVM +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class RpdSuccessViewModel @Inject constructor(private val repository: RpdSuccessRepository) : + BaseVM() { + + private val _rpdSuccessData = MutableLiveData() + val rpdSuccessData: LiveData + get() = _rpdSuccessData + + private val _success = MutableLiveData() + val success: LiveData + get() = _success + + fun fetchData() { + viewModelScope.launch { + val response = repository.fetchRpdSuccess() + if (response.error == null && response.errors.isNullOrEmpty()) { + _rpdSuccessData.value = response.data!! + } else { + setErrorData(response.errors, response.error) + } + } + } + + fun postStatus(status : String){ + viewModelScope.launch{ + val response = repository.postStatus(status) + if(response.statusCode == API_SUCCESS_CODE){ + _success.value = true + } + } + } +} \ No newline at end of file diff --git a/navi-amc/src/main/java/com/navi/amc/fundbuy/fragments/NfoDetailsFragment.kt b/navi-amc/src/main/java/com/navi/amc/fundbuy/fragments/NfoDetailsFragment.kt index 3fbf7a548d..5713d9531d 100644 --- a/navi-amc/src/main/java/com/navi/amc/fundbuy/fragments/NfoDetailsFragment.kt +++ b/navi-amc/src/main/java/com/navi/amc/fundbuy/fragments/NfoDetailsFragment.kt @@ -7,12 +7,6 @@ package com.navi.amc.fundbuy.fragments -/* - * - * * Copyright © 2022 by Navi Technologies Limited - * * All rights reserved. Strictly confidential - * - */ import android.content.Context import android.net.Uri diff --git a/navi-amc/src/main/java/com/navi/amc/kyc/fragment/BankDetailsFragment.kt b/navi-amc/src/main/java/com/navi/amc/kyc/fragment/BankDetailsFragment.kt index 3b2689a42d..eeae196feb 100644 --- a/navi-amc/src/main/java/com/navi/amc/kyc/fragment/BankDetailsFragment.kt +++ b/navi-amc/src/main/java/com/navi/amc/kyc/fragment/BankDetailsFragment.kt @@ -27,6 +27,7 @@ import com.navi.amc.kyc.model.BankDetailsResponse import com.navi.amc.kyc.viewmodel.BankDetailsVM import com.navi.amc.utils.* import com.navi.base.model.ActionData +import com.navi.base.model.LineItem import com.navi.base.model.NaviClickAction import com.navi.base.model.NaviWidgetClickWithActionData import com.navi.common.listeners.FragmentInterchangeListener diff --git a/navi-amc/src/main/java/com/navi/amc/kyc/model/BankDetailsResponse.kt b/navi-amc/src/main/java/com/navi/amc/kyc/model/BankDetailsResponse.kt index 401674b763..f26da7826a 100644 --- a/navi-amc/src/main/java/com/navi/amc/kyc/model/BankDetailsResponse.kt +++ b/navi-amc/src/main/java/com/navi/amc/kyc/model/BankDetailsResponse.kt @@ -34,4 +34,5 @@ data class Note( @SerializedName("bgColor") val bgColor: String? = null, @SerializedName("iconCode") val iconCode: String? = null, @SerializedName("title") val title: TextWithStyle? = null, + @SerializedName("toShowShimmer") val toShowShimmer: Boolean? = null ) : Parcelable diff --git a/navi-amc/src/main/java/com/navi/amc/network/retrofit/NaviHttpClient.kt b/navi-amc/src/main/java/com/navi/amc/network/retrofit/NaviHttpClient.kt index 4c8a29b066..3916d01dc8 100644 --- a/navi-amc/src/main/java/com/navi/amc/network/retrofit/NaviHttpClient.kt +++ b/navi-amc/src/main/java/com/navi/amc/network/retrofit/NaviHttpClient.kt @@ -33,6 +33,27 @@ class NaviHttpClient(networkInfo: NetworkInfo, private val context: Context) : ) } } + + + okHttpClientBuilder.addInterceptor { + + val oldRequest = it.request() + val newRequest = oldRequest.newBuilder() + val newHeaders = oldRequest.headers.newBuilder() + + val multiMap = oldRequest.headers.toMultimap() + + for (pair in multiMap) { + if (pair.value.size > 1) { + newHeaders.removeAll(pair.key) + newHeaders.add(pair.key, pair.value.first()) + } + } + + newRequest.headers(newHeaders.build()) + it.proceed(newRequest.build()) + } + return okHttpClientBuilder } diff --git a/navi-amc/src/main/java/com/navi/amc/network/retrofit/RetrofitService.kt b/navi-amc/src/main/java/com/navi/amc/network/retrofit/RetrofitService.kt index ab0e99adbe..7568f34d5f 100644 --- a/navi-amc/src/main/java/com/navi/amc/network/retrofit/RetrofitService.kt +++ b/navi-amc/src/main/java/com/navi/amc/network/retrofit/RetrofitService.kt @@ -368,4 +368,36 @@ interface RetrofitService { @GET("/fund/fund-details-nfo") suspend fun fetchNfoDetails(@Query("isin") isin: String): Response> + + @GET("/payment-method") + suspend fun fetchPennyDropOptions( + @Header("X-Payments-SDK-Token") token: String, + @Header("X-Target") header: String + ): Response> + + @GET("/payment-method/details/{methodId}") + suspend fun fetchInitPaymentMethodDetails( + @Path("methodId") methodId: String, + @Header("X-Payments-SDK-Token") token: String, + @Header("X-Target") header: String + ): Response> + + @POST("payment-method/signal/{method}") + suspend fun postPaymentStatus( + @Header("X-Payments-SDK-Token") token: String, + @Path("method") method: String, + @Header("X-Target") xTarget: String + ): Response + + @GET("/kyc/rpd-confirm-page") + suspend fun fetchRpdConfirmDetails(): Response> + + @GET("/requests/{requestId}/request-details-config") + suspend fun fetchRequestConfig(@Path("requestId") requestId: String?): Response> + + @GET("/requests/{requestId}") + suspend fun checkApiPollStatus(@Path("requestId") requestId: String?): Response?>> + + @POST("/kyc/rpd/bank-status/{status}") + suspend fun postStatus(@Path("status") status : String):Response> } diff --git a/navi-amc/src/main/java/com/navi/amc/utils/AmcAnalytics.kt b/navi-amc/src/main/java/com/navi/amc/utils/AmcAnalytics.kt index 6c4895ee17..1462a577af 100644 --- a/navi-amc/src/main/java/com/navi/amc/utils/AmcAnalytics.kt +++ b/navi-amc/src/main/java/com/navi/amc/utils/AmcAnalytics.kt @@ -156,6 +156,10 @@ object AmcAnalytics { const val COARSE_LOCATION_PERMISSION_ALLOWED = "amc_btn_permission_coarse_location_allow" const val COARSE_LOCATION_PERMISSION_DENIED = "amc_btn_permission_coarse_location_deny" const val NFO_SCREEN = "nfoScreen" + const val BANK_VERIFICATION_OPTIONS = "bank_verification_options" + const val PENNY_DROP_OPTIONS = "amc_init_kyc_bank_verify" + const val CHANGE_BANK = "changeBank" + const val RPD_SUCCESS_SCREEN = "amc_init_kyc_bank_verify_success" const val AMC_INIT_SIMPLIFIED_LUMPSUM = "amc_init_simplified_lumpsum" const val AMC_INIT_SIMPLIFIED_SIP = "amc_init_simplified_sip" diff --git a/navi-amc/src/main/java/com/navi/amc/utils/Constant.kt b/navi-amc/src/main/java/com/navi/amc/utils/Constant.kt index 442d8d9992..c4cbce3da3 100644 --- a/navi-amc/src/main/java/com/navi/amc/utils/Constant.kt +++ b/navi-amc/src/main/java/com/navi/amc/utils/Constant.kt @@ -81,6 +81,13 @@ object Constant { const val DOWNLOADING = "Downloading" const val V2 ="v2" const val PACKAGE = "package:" + const val TOKEN = "token" + const val PAYMENTS = "PAYMENTS" + const val RPD = "RPD" + const val UPILINK= "upiLink" + const val INVALID_TOKEN = "INVALID_TOKEN" + const val CHANGE_BANK = "CHANGE_BANK" + const val CONFIRMED = "CONFIRMED" const val BUSINESS_VERTICAL_AMC = "AMC" const val UPI = "UPI" } diff --git a/navi-amc/src/main/java/com/navi/amc/utils/Ext.kt b/navi-amc/src/main/java/com/navi/amc/utils/Ext.kt index 7715ad5a1c..87e642302b 100644 --- a/navi-amc/src/main/java/com/navi/amc/utils/Ext.kt +++ b/navi-amc/src/main/java/com/navi/amc/utils/Ext.kt @@ -197,6 +197,8 @@ fun getFragment(screen: String, bundle: Bundle): Fragment? { SubPageStatusType.SIP_DETAILS_MODIFY -> ModifySipDetailsFragment.newInstance(bundle) SubPageStatusType.SIP_MODIFICATION -> SipModifyFragment.newInstance(bundle) SubPageStatusType.NFO_DETAILS -> NfoDetailsFragment.newInstance(bundle) + SubPageStatusType.BANK_ADDITION_OPTIONS -> PennyDropOptionsFragment.newInstance(bundle) + SubPageStatusType.RPD_SUCCESS -> RpdSuccessFragment.newInstance(bundle) else -> null } } diff --git a/navi-amc/src/main/java/com/navi/amc/utils/SubPageStatusType.kt b/navi-amc/src/main/java/com/navi/amc/utils/SubPageStatusType.kt index 3202ee3907..cf4fb8c628 100644 --- a/navi-amc/src/main/java/com/navi/amc/utils/SubPageStatusType.kt +++ b/navi-amc/src/main/java/com/navi/amc/utils/SubPageStatusType.kt @@ -53,4 +53,6 @@ object SubPageStatusType { const val AMC_COMMON_BOTTOMSHEET = "AMC_COMMON_BOTTOMSHEET" const val BANNER_HORIZONTAL_BOTTOMSHEET = "BANNER_HORIZONTAL_BOTTOMSHEET" const val NFO_DETAILS = "nfo_details" + const val BANK_ADDITION_OPTIONS = "bank_addition_options" + const val RPD_SUCCESS = "rpd_success" } \ No newline at end of file diff --git a/navi-amc/src/main/res/layout/fund_detail_screen_layout.xml b/navi-amc/src/main/res/layout/fund_detail_screen_layout.xml index 63e70b8252..e2b392b413 100644 --- a/navi-amc/src/main/res/layout/fund_detail_screen_layout.xml +++ b/navi-amc/src/main/res/layout/fund_detail_screen_layout.xml @@ -27,7 +27,7 @@ layout="@layout/amc_fund_list_reward" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginBottom="@dimen/dp_8"/> + android:layout_marginBottom="@dimen/dp_8" /> + + + + + + + + + + \ No newline at end of file diff --git a/navi-amc/src/main/res/layout/penny_drop_option.xml b/navi-amc/src/main/res/layout/penny_drop_option.xml new file mode 100644 index 0000000000..717eb7a60f --- /dev/null +++ b/navi-amc/src/main/res/layout/penny_drop_option.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/navi-amc/src/main/res/layout/penny_drop_options_layout.xml b/navi-amc/src/main/res/layout/penny_drop_options_layout.xml new file mode 100644 index 0000000000..61035a1d27 --- /dev/null +++ b/navi-amc/src/main/res/layout/penny_drop_options_layout.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/navi-amc/src/main/res/layout/rpd_success_layout.xml b/navi-amc/src/main/res/layout/rpd_success_layout.xml new file mode 100644 index 0000000000..16c2f98eb6 --- /dev/null +++ b/navi-amc/src/main/res/layout/rpd_success_layout.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/navi-amc/src/main/res/layout/shimmer_note_view.xml b/navi-amc/src/main/res/layout/shimmer_note_view.xml new file mode 100644 index 0000000000..d5d5a4c654 --- /dev/null +++ b/navi-amc/src/main/res/layout/shimmer_note_view.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/navi-common/src/main/java/com/navi/common/listeners/ApiCallListener.kt b/navi-common/src/main/java/com/navi/common/listeners/ApiCallListener.kt index 2c1f23aa17..a76d34d5f0 100644 --- a/navi-common/src/main/java/com/navi/common/listeners/ApiCallListener.kt +++ b/navi-common/src/main/java/com/navi/common/listeners/ApiCallListener.kt @@ -37,7 +37,8 @@ interface ApiCallListener { showFullScreenError: Boolean = false, container: Int? = null, showNewDesignSystemFragment: Boolean = false, - actionErrorV2Enabled: Boolean = false + actionErrorV2Enabled: Boolean = false, + buttonListener: ((String) -> Unit)? = null ) fun onWarning( diff --git a/navi-common/src/main/java/com/navi/common/ui/activity/BaseActivity.kt b/navi-common/src/main/java/com/navi/common/ui/activity/BaseActivity.kt index 4560af41cd..95be4bb50e 100644 --- a/navi-common/src/main/java/com/navi/common/ui/activity/BaseActivity.kt +++ b/navi-common/src/main/java/com/navi/common/ui/activity/BaseActivity.kt @@ -170,7 +170,8 @@ abstract class BaseActivity : showFullScreenError: Boolean = false, container: Int? = null, showNewDesignSystemFragment: Boolean = false, - actionErrorV2Enabled: Boolean = false + actionErrorV2Enabled: Boolean = false, + buttonListener: ((String) -> Unit)? = null ) { if (viewModel.errorResponse.hasActiveObservers()) return viewModel.errorResponse.observeNonNull(this) { response -> @@ -218,8 +219,9 @@ abstract class BaseActivity : sourceScreenName = eventTrackingScreenName ?: getCurrentFragmentScreenName(), secondaryAction = actions?.getOrNull(1)?.first, - moduleName = getCurrentModuleName() - ) + moduleName = getCurrentModuleName(), + buttonListener = buttonListener + ) ft.add(errorFragment, NewActionErrorFragment.TAG) ft.commitAllowingStateLoss() } else { @@ -442,7 +444,8 @@ abstract class BaseActivity : showFullScreenError: Boolean, container: Int?, showNewDesignSystemFragment: Boolean, - actionErrorV2Enabled: Boolean + actionErrorV2Enabled: Boolean, + buttonListener: ((String) -> Unit)? ) { initError( viewModel, @@ -453,7 +456,8 @@ abstract class BaseActivity : showFullScreenError, container, showNewDesignSystemFragment, - actionErrorV2Enabled + actionErrorV2Enabled, + buttonListener ) } diff --git a/navi-common/src/main/java/com/navi/common/ui/fragment/BaseFragment.kt b/navi-common/src/main/java/com/navi/common/ui/fragment/BaseFragment.kt index a55564d5f9..87e3f514ed 100644 --- a/navi-common/src/main/java/com/navi/common/ui/fragment/BaseFragment.kt +++ b/navi-common/src/main/java/com/navi/common/ui/fragment/BaseFragment.kt @@ -76,7 +76,8 @@ abstract class BaseFragment : Fragment() { showFullScreenError: Boolean = false, container: Int? = null, showNewDesignSystemFragment: Boolean = false, - actionErrorV2Enabled: Boolean = false + actionErrorV2Enabled: Boolean = false , + buttonListener: ((String) -> Unit)? = null ) { apiCallListener?.onError( viewModel, @@ -87,7 +88,8 @@ abstract class BaseFragment : Fragment() { showFullScreenError, container, showNewDesignSystemFragment, - actionErrorV2Enabled + actionErrorV2Enabled, + buttonListener ) } diff --git a/navi-common/src/main/java/com/navi/common/ui/fragment/NewActionErrorFragment.kt b/navi-common/src/main/java/com/navi/common/ui/fragment/NewActionErrorFragment.kt index 60960a562b..c78ed20c3c 100644 --- a/navi-common/src/main/java/com/navi/common/ui/fragment/NewActionErrorFragment.kt +++ b/navi-common/src/main/java/com/navi/common/ui/fragment/NewActionErrorFragment.kt @@ -37,6 +37,7 @@ class NewActionErrorFragment : BaseBottomSheet() { private var cancelableDialog: Boolean = true private var error: GenericErrorResponse? = null private val analyticsTracker = CommonNaviAnalytics.naviAnalytics.ErrorBottomSheet() + private var buttonListener: ((String) -> Unit)? = null override fun setContainerView(viewStub: ViewStub) { viewStub.layoutResource = R.layout.new_action_error_fragment @@ -108,12 +109,18 @@ class NewActionErrorFragment : BaseBottomSheet() { view.tag = error?.code action?.let { action?.onClick(view) } ?: kotlin.run { openErrorCta(error?.actions?.firstOrNull()) } + error?.actions?.getOrNull(0)?.url?.let { + buttonListener?.invoke(it) + } dismissDialog() } if (error?.actions?.size.orZero() > 1) { binding.secondaryAbv.visibility = View.VISIBLE binding.secondaryAbv.setOnClickListener { view -> view.tag = error?.code + error?.actions?.getOrNull(1)?.url?.let{ + buttonListener?.invoke(it) + } secondaryAction?.let { secondaryAction?.onClick(view) } ?: kotlin.run { openErrorCta(error?.actions?.getOrNull(1)) } dismissDialog() @@ -205,11 +212,13 @@ class NewActionErrorFragment : BaseBottomSheet() { cancelable: Boolean = true, sourceScreenName: String?, secondaryAction: View.OnClickListener? = null, - moduleName: String? = null + moduleName: String? = null, + buttonListener: ((String) -> Unit)? = null ) = NewActionErrorFragment().apply { this.action = action this.secondaryAction = secondaryAction + this.buttonListener = buttonListener arguments = Bundle().apply { error?.let { putParcelable(ERROR_DATA, it) }