Android Kotlin. У меня такая проблема, что мне необходимо, чтобы элемент AboutService, если он находится рядом с элементом Faq, то они вставали под один MaterialCardView, можно ли это реализовать? package ru.maxi.mobile.feature.maxiPay.ui.viewItems

import android.view.View
import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import by.kirich1409.viewbindingdelegate.viewBinding
import ru.maxi.mobile.R
import ru.maxi.mobile.databinding.ItemMaxiPayAboutHeaderBinding
import ru.maxi.mobile.databinding.ItemMaxiPayAdditionalInfoBinding
import ru.maxi.mobile.feature.maxiPay.model.MaxiPayAdditionalInfo

/**
 * [RecyclerView.ViewHolder] для отображения элемента дополнительной информации Макси Pay
 */
class MaxiPayAdditionalInfoViewHolder(
    view: View,
    private val onClick: (item: MaxiPayAdditionalInfo) -> Unit,
    private val onAboutServiceClick: () -> Unit,
) : RecyclerView.ViewHolder(view) {

    private val viewBinding by viewBinding(ItemMaxiPayAdditionalInfoBinding::bind)

    /**
     * Установить данные [data] для отображения
     */
    fun bind(data: MaxiPayAdditionalInfo) {
        when (data) {
            is MaxiPayAdditionalInfo.Faq -> {
                viewBinding.itemViewFaq.setTitleText(
                    itemView.resources.getString(R.string.maxi_pay_faq_title),
                )
                viewBinding.itemViewAboutService.setTitleText(
                    itemView.resources.getString(R.string.maxi_pay_about_service_title),
                )
                // viewBinding.itemViewAboutService.isVisible = false
                viewBinding.itemViewFaq.setOnClickListener { onClick(data) }
                viewBinding.itemViewAboutService.setOnClickListener { onAboutServiceClick() }
            }

            MaxiPayAdditionalInfo.AboutService -> {
                viewBinding.itemViewFaq.isVisible = false
                viewBinding.itemViewAboutService.setDividerVisibility(false)
                viewBinding.itemViewAboutService.setTitleText(
                    itemView.resources.getString(R.string.maxi_pay_about_service_title),
                )
                viewBinding.itemViewAboutService.setOnClickListener { onAboutServiceClick() }
            }

        }
        // viewBinding.root.setOnClickListener { onClick(data) }
    }
}
package ru.maxi.mobile.feature.maxiPay.model

/**
 * Данные дополнительной информации функционала Макси Pay
 */
sealed interface MaxiPayAdditionalInfo {

    /**
     * Вопросы и ответы
     */
    data object Faq : MaxiPayAdditionalInfo

    data object AboutService : MaxiPayAdditionalInfo
}
package ru.maxi.mobile.feature.maxiPay.ui.about

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onSubscription
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import ru.maxi.mobile.core.data.LoadableData
import ru.maxi.mobile.core.data.dataValue
import ru.maxi.mobile.core.data.emitError
import ru.maxi.mobile.core.data.emitLoading
import ru.maxi.mobile.core.data.emitSuccess
import ru.maxi.mobile.core.data.exception.BusinessLogicException
import ru.maxi.mobile.core.data.exception.BusinessLogicValidException
import ru.maxi.mobile.core.data.isLoading
import ru.maxi.mobile.core.data.isSuccess
import ru.maxi.mobile.core.data.mapped
import ru.maxi.mobile.feature.analytics.model.AnalyticType
import ru.maxi.mobile.feature.analytics.model.actions.AnalyticMaxiPayActions
import ru.maxi.mobile.feature.analytics.model.events.AnalyticsGeneralEvent
import ru.maxi.mobile.feature.analytics.usecase.AnalyticsUseCase
import ru.maxi.mobile.feature.maxiPay.domain.CreateMaxiPaySubscriptionUseCase
import ru.maxi.mobile.feature.maxiPay.domain.GetMaxiPayAboutDataUseCase
import ru.maxi.mobile.feature.maxiPay.domain.GetMaxiPayAvailableMerchantsListUseCase
import ru.maxi.mobile.feature.maxiPay.domain.MaxiPaySseUseCase
import ru.maxi.mobile.feature.maxiPay.model.MaxiPayAboutData
import ru.maxi.mobile.feature.maxiPay.model.MaxiPayAdditionalInfo
import ru.maxi.mobile.feature.maxiPay.model.MaxiPayAdvantageData
import ru.maxi.mobile.feature.maxiPay.model.MaxiPayMerchant
import ru.maxi.mobile.feature.maxiPay.ui.about.model.MaxiPayAboutNavigationEvent
import ru.maxi.mobile.feature.maxiPay.ui.about.model.MaxiPayAboutViewItem
import javax.inject.Inject

/**
 * [ViewModel] экрана информации о функционале Макси Pay
 */
@HiltViewModel
class MaxiPayAboutViewModel @Inject constructor(
    private val getAboutData: GetMaxiPayAboutDataUseCase,
    private val getAvailableMerchantsList: GetMaxiPayAvailableMerchantsListUseCase,
    private val createSubscription: CreateMaxiPaySubscriptionUseCase,
    private val sseUseCase: MaxiPaySseUseCase,
    private val analyticsUseCase: AnalyticsUseCase,
) : ViewModel() {

    private val _navigationEvent = Channel<MaxiPayAboutNavigationEvent>(Channel.BUFFERED)
    private val _messageEvent = Channel<String>(Channel.BUFFERED)

    private val dataState = MutableStateFlow<LoadableData<MaxiPayAboutData>>(LoadableData.Loading())

    /** События навигации */
    val navigationEvent = _navigationEvent.receiveAsFlow()

    /** События сообщений **/
    val messageEvent = _messageEvent.receiveAsFlow()

    /** Состояние экрана */
    val viewState: Flow<LoadableData<List<MaxiPayAboutViewItem>>> = dataState.asStateFlow()
        .onSubscription { loadData() }
        .stateIn(viewModelScope, SharingStarted.Lazily, LoadableData.Loading())
        .map { it.mapped(MaxiPayAboutData::toViewItems) }

    /**
     * Обновить данные экрана
     */
    fun refreshData() {
        if (dataState.isLoading) return

        dataState.emitLoading(saveValue = false)
        viewModelScope.launch {
            loadData()
        }
    }

    private suspend fun loadData() {
        try {
            val aboutData = getAboutData()
            dataState.emitSuccess(aboutData)
        } catch (exception: Exception) {
            dataState.emitError(exception)
        }
    }

    /**
     * Обработать клик по кнопке привязки счёта
     */
    fun onCreateSubscriptionButtonClick() {
        if (!dataState.isSuccess) return
        val data = dataState.dataValue ?: return

        analyticsUseCase log AnalyticsGeneralEvent.SelectContent(
            analyticType = AnalyticType.MAXI_PAY,
            analyticAction = AnalyticMaxiPayActions.ADD_ACC_BUTTON_CLICK,
        )

        var connectedToSse = false
        viewModelScope.launch {
            try {
                dataState.emitLoading()
                val mainMerchant = getMainMerchant()

                sseUseCase.connectToSse(MaxiPaySseUseCase.WAITING_SCREEN_SSE_CLIENT_TAG)
                connectedToSse = true

                createSubscription(mainMerchant.code)
                _navigationEvent.send(MaxiPayAboutNavigationEvent.OpenWaitingScreen(mainMerchant.name))
            } catch (exception: Exception) {
                val errorMessage = (exception as? BusinessLogicValidException)?.message
                    ?: (exception as? BusinessLogicException)?.message
                    ?: exception.localizedMessage
                _messageEvent.send(errorMessage)

                if (connectedToSse) {
                    sseUseCase.disconnectFromSse(MaxiPaySseUseCase.WAITING_SCREEN_SSE_CLIENT_TAG)
                }
            } finally {
                dataState.emitSuccess(data)
            }
        }
    }

    /**
     * Обработать клик по элементу [item] дополнительной информации
     */
    fun onAdditionalInfoItemClick(item: MaxiPayAdditionalInfo) {
        when (item) {
            is MaxiPayAdditionalInfo.Faq -> _navigationEvent.trySend(
                MaxiPayAboutNavigationEvent.OpenFaqScreen,
            )

            is MaxiPayAdditionalInfo.AboutService -> {
                analyticsUseCase log AnalyticsGeneralEvent.SelectContent(
                    analyticType = AnalyticType.MAXI_PAY,
                    analyticAction = AnalyticMaxiPayActions.ABOUT_SERVICE_BUTTON_CLICK
                )
                // val url = "https://${BuildConfig.MAXI_RETAIL_HOST}/vologda/MAXIPAY}"
                // Log.d("TESTING", "MaxiPayAboutViewModel onAdditionalInfoItemClick uri = ${URI.create("https://maxi-retail.ru/vologda/MAXIPAY")}")
                _navigationEvent.trySend(MaxiPayAboutNavigationEvent.OpenAboutServiceScreen)
            }
        }
    }

    private suspend fun getMainMerchant(): MaxiPayMerchant {
        return getAvailableMerchantsList().firstOrNull()?.let { merchants ->
            merchants.firstOrNull { it.merchant.isMain }?.merchant
                ?: merchants.firstOrNull()?.merchant
        } ?: throw BusinessLogicException("Не найдено ни одного мерчанта")
    }
}

private fun MaxiPayAboutData.toViewItems(): List<MaxiPayAboutViewItem> = buildList {
    add(headerViewItem)
    add(additionalInfoTypeAboutService)
    advantagesList.map(MaxiPayAdvantageData::toViewItem)
        .takeIf(List<Any>::isNotEmpty)
        ?.let { advantagesItems ->
            add(advantagesTitleViewItem)
            addAll(advantagesItems)
        }
    add(appsDisclaimerViewItem)
    add(additionalInfoTypeAboutService)
    if (haveQuestions) {
        add(additionalInfoType)
    }
}

private val MaxiPayAboutData.headerViewItem: MaxiPayAboutViewItem.Header
    get() = MaxiPayAboutViewItem.Header(
        title = aboutTitle,
        description = aboutDescription,
    )

private fun MaxiPayAdvantageData.toViewItem() = MaxiPayAboutViewItem.Advantage(
    imageUrl = imageUrl,
    title = title,
    description = description,
)

private val MaxiPayAboutData.advantagesTitleViewItem: MaxiPayAboutViewItem.AdvantagesTitle
    get() = MaxiPayAboutViewItem.AdvantagesTitle(
        text = advantagesTitle,
    )

private val MaxiPayAboutData.appsDisclaimerViewItem: MaxiPayAboutViewItem.AppsDisclaimer
    get() = MaxiPayAboutViewItem.AppsDisclaimer(
        text = appsDisclaimer,
    )

private val MaxiPayAboutData.additionalInfoType: MaxiPayAboutViewItem.AdditionalInfoType
    get() = MaxiPayAboutViewItem.AdditionalInfoType.FAQ

private val MaxiPayAboutData.additionalInfoTypeAboutService: MaxiPayAboutViewItem.AdditionalInfoType
    get() = MaxiPayAboutViewItem.AdditionalInfoType.AboutService
package ru.maxi.mobile.feature.maxiPay.ui.about

import android.util.Log
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import ru.maxi.mobile.common.components.databinding.HolderListItemBinding
import ru.maxi.mobile.common.components.databinding.HolderListMessageBinding
import ru.maxi.mobile.common.components.itemlist.ListElement
import ru.maxi.mobile.common.components.itemlist.ListItemViewHolder
import ru.maxi.mobile.common.components.itemlist.ListMessageViewHolder
import ru.maxi.mobile.databinding.ItemMaxiPayAboutHeaderBinding
import ru.maxi.mobile.databinding.ItemMaxiPayAdditionalInfoBinding
import ru.maxi.mobile.databinding.ItemMaxiPayAdvantagesTitleBinding
import ru.maxi.mobile.feature.maxiPay.model.MaxiPayAdditionalInfo
import ru.maxi.mobile.feature.maxiPay.ui.about.model.MaxiPayAboutViewItem
import ru.maxi.mobile.feature.maxiPay.ui.viewItems.MaxiPayAdditionalInfoViewHolder
import ru.maxi.mobile.ui.common.utils.inflate
import ru.maxi.mobile.ui.common.utils.inflateAndGetRoot

/**
 * Адаптер экрана информации о функционале Макси Pay
 */
class MaxiPayAboutAdapter(
    private val onAdditionalInfoItemClick: (item: MaxiPayAdditionalInfo) -> Unit,
) : ListAdapter<MaxiPayAboutViewItem, RecyclerView.ViewHolder>(DiffCallback()) {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        return when (ViewType.entries[viewType]) {
            ViewType.ADVANTAGE -> {
                val viewBinding = parent.inflate(HolderListItemBinding::inflate)
                ListItemViewHolder(viewBinding)
            }

            ViewType.ADVANTAGES_TITLE -> {
                val view = parent.inflateAndGetRoot(ItemMaxiPayAdvantagesTitleBinding::inflate)
                MaxiPayAdvantagesTitleViewHolder(view)
            }

            ViewType.APPS_DISCLAIMER -> {
                val viewBinding = parent.inflate(HolderListMessageBinding::inflate)
                ListMessageViewHolder(viewBinding)
            }

            ViewType.HEADER -> {
                val view = parent.inflateAndGetRoot(ItemMaxiPayAboutHeaderBinding::inflate)
                MaxiPayAboutHeaderViewHolder(view)
            }

            ViewType.ADDITIONAL_INFO -> {
                val view = parent.inflateAndGetRoot(ItemMaxiPayAdditionalInfoBinding::inflate)
                MaxiPayAdditionalInfoViewHolder(
                    view,
                    onClick = {
                        onAdditionalInfoItemClick(MaxiPayAdditionalInfo.Faq)
                        Log.d("TESTING", "MaxiPayAboutAdapter onClick")
                    },

                    onAboutServiceClick = {
                        Log.d("TESTING", "MaxiPayAboutAdapter OnAboutServiceClick")
                        onAdditionalInfoItemClick(MaxiPayAdditionalInfo.AboutService)

                    },
                )
            }
        }
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        when (val item = getItem(position)) {
            is MaxiPayAboutViewItem.Advantage ->
                (holder as? ListItemViewHolder)?.bind(item.toListItemElement())

            is MaxiPayAboutViewItem.AdvantagesTitle ->
                (holder as? MaxiPayAdvantagesTitleViewHolder)?.bind(item)

            is MaxiPayAboutViewItem.AppsDisclaimer ->
                (holder as? ListMessageViewHolder)?.bind(item.toListMessageElement())

            is MaxiPayAboutViewItem.Header ->
                (holder as? MaxiPayAboutHeaderViewHolder)?.bind(item)

            is MaxiPayAboutViewItem.AdditionalInfoType ->
                (holder as? MaxiPayAdditionalInfoViewHolder)?.bind(item.type)
        }
    }

    override fun getItemViewType(position: Int): Int = when (getItem(position)) {
        is MaxiPayAboutViewItem.Advantage -> ViewType.ADVANTAGE
        is MaxiPayAboutViewItem.AdvantagesTitle -> ViewType.ADVANTAGES_TITLE
        is MaxiPayAboutViewItem.AppsDisclaimer -> ViewType.APPS_DISCLAIMER
        is MaxiPayAboutViewItem.Header -> ViewType.HEADER
        is MaxiPayAboutViewItem.AdditionalInfoType -> ViewType.ADDITIONAL_INFO
    }.ordinal

    private companion object {

        enum class ViewType {
            ADVANTAGE,
            ADVANTAGES_TITLE,
            APPS_DISCLAIMER,
            HEADER,
            ADDITIONAL_INFO,
        }
    }
}

private fun MaxiPayAboutViewItem.Advantage.toListItemElement() = ListElement.Item(
    title = title,
    subtitle = description,
    trimTitle = false,
    trimSubtitle = false,
    hasDivider = false,
    leftIcon = ListElement.Item.Icon.FromUrl(imageUrl),
    leftIconTintColor = ListElement.Item.IconTint.Custom(null),
)

private fun MaxiPayAboutViewItem.AppsDisclaimer.toListMessageElement() = ListElement.Message(
    style = ListElement.Message.Style.WARNING,
    subtitle = text,
)

private val MaxiPayAboutViewItem.AdditionalInfoType.type
    get() = when (this) {
        MaxiPayAboutViewItem.AdditionalInfoType.FAQ -> MaxiPayAdditionalInfo.Faq
        MaxiPayAboutViewItem.AdditionalInfoType.AboutService -> MaxiPayAdditionalInfo.AboutService
    }

private class DiffCallback : DiffUtil.ItemCallback<MaxiPayAboutViewItem>() {

    override fun areItemsTheSame(oldItem: MaxiPayAboutViewItem, newItem: MaxiPayAboutViewItem): Boolean {
        return when (oldItem) {
            is MaxiPayAboutViewItem.Advantage -> oldItem == newItem
            is MaxiPayAboutViewItem.AdvantagesTitle -> true
            is MaxiPayAboutViewItem.AppsDisclaimer -> true
            is MaxiPayAboutViewItem.Header -> true
            is MaxiPayAboutViewItem.AdditionalInfoType -> oldItem == newItem
        }
    }

    override fun areContentsTheSame(oldItem: MaxiPayAboutViewItem, newItem: MaxiPayAboutViewItem): Boolean {
        return oldItem == newItem
    }
}