[PS] AE-7458 | Show top / recommended funds like Dashboard on fund listing page (#4696)

* [PS] added fund listing v2 widgets

* [PS] implemented fund listing page changes

* [PS] fix design issues

* [PS] fix design issues

Co-authored-by: shankar yadav <shankar.yadav@navi.com>
This commit is contained in:
Prakhar Saxena
2022-12-27 15:56:41 +05:30
committed by GitHub Enterprise
parent f7b34333bf
commit 94aeff96ba
45 changed files with 1708 additions and 3 deletions

View File

@@ -25,6 +25,7 @@ import com.navi.amc.fundbuy.viewmodel.FundListViewModel
import com.navi.amc.utils.AmcAnalytics
import com.navi.amc.utils.AmcAnalytics.FUND_ID
import com.navi.amc.utils.AmcAnalytics.ISIN
import com.navi.amc.utils.Constant
import com.navi.amc.utils.Constant.FUND_CATEGORY
import com.navi.amc.utils.getValueFromActionData
import com.navi.base.model.ActionData
@@ -55,12 +56,20 @@ class FundListingFragment : AmcBaseFragment() {
binding =
DataBindingUtil.inflate(inflater, R.layout.fund_list_fragment_layout, container, false)
initError(viewModel, showNewDesignSystemFragment = true)
sendInitEvent()
initRecyclerView()
initObservers()
fetchData()
return binding.root
}
private fun sendInitEvent() {
sendEvent(
AmcAnalytics.FUND_LIST,
hashMapOf(Pair(FUND_CATEGORY, arguments?.getString(FUND_CATEGORY).orEmpty()))
)
}
private fun initObservers() {
viewModel.fundListData.observe(viewLifecycleOwner) {
hideLoader()

View File

@@ -0,0 +1,156 @@
package com.navi.amc.fundbuy.fragments
import android.content.Context
import android.graphics.Rect
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ItemDecoration
import com.navi.amc.R
import com.navi.amc.common.fragment.AmcBaseFragment
import com.navi.amc.databinding.FundListFragmentV2LayoutBinding
import com.navi.amc.fundbuy.viewmodel.FundListViewModel
import com.navi.amc.utils.AmcAnalytics
import com.navi.amc.utils.Constant
import com.navi.amc.utils.SubPageStatusType
import com.navi.amc.utils.getValueFromActionData
import com.navi.base.model.ActionData
import com.navi.base.model.NaviClickAction
import com.navi.base.model.NaviWidgetClickWithActionData
import com.navi.base.utils.isNotNullAndNotEmpty
import com.navi.common.listeners.FragmentInterchangeListener
import com.navi.common.listeners.HeaderInteractionListener
import com.navi.design.utils.dpToPx
import com.navi.naviwidgets.adapters.NaviAdapter
import com.navi.naviwidgets.callbacks.WidgetCallback
import com.navi.naviwidgets.extensions.setVisibilityState
import com.navi.naviwidgets.models.NaviWidget
import com.navi.naviwidgets.viewholder.DashboardHolderFactoryImpl
import com.navi.naviwidgets.viewholder.ViewHolderFactoryImpl
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class FundListingFragmentV2: AmcBaseFragment(), WidgetCallback {
private lateinit var binding: FundListFragmentV2LayoutBinding
private val viewModel by lazy { ViewModelProvider(this).get(FundListViewModel::class.java) }
private var naviAdapter: NaviAdapter<NaviWidget> =
NaviAdapter(widgetCallback = this, factory = ViewHolderFactoryImpl())
override val screenName: String
get() = AmcAnalytics.FUND_LIST
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = DataBindingUtil.inflate(inflater, R.layout.fund_list_fragment_v2_layout, container, false)
sendInitEvent()
initError(viewModel, showNewDesignSystemFragment = true)
initRecyclerView()
initObservers()
fetchData()
return binding.root
}
private fun sendInitEvent() {
val category = arguments?.getString(Constant.FUND_CATEGORY).orEmpty()
val initEventName =
if (category == SubPageStatusType.FUND_LANDING) AmcAnalytics.FUND_LANDING
else if (category.isNotNullAndNotEmpty()) AmcAnalytics.FUND_CATEOGRY_LIST
else AmcAnalytics.FUND_LIST
sendEvent(
initEventName,
hashMapOf(Pair(Constant.FUND_CATEGORY, arguments?.getString(Constant.FUND_CATEGORY).orEmpty()))
)
}
private fun initRecyclerView() {
binding.rvProducts.apply {
layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
adapter = naviAdapter
addItemDecoration(object : ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
super.getItemOffsets(outRect, view, parent, state)
parent.findContainingViewHolder(view)?.itemViewType?.let { itemViewType ->
if(itemViewType == R.layout.layout_list_overlapping_header) {
outRect.set(0, 0, 0, (-1 * dpToPx(50).toInt()))
}
}
}
})
}
}
private fun initObservers() {
viewModel.fundListDataV2.observe(viewLifecycleOwner) { response ->
hideLoader()
response?.listOfWidgets?.let { list ->
naviAdapter.setData(list)
binding.rvProducts.visibility = View.VISIBLE
} ?: run { binding.rvProducts.visibility = View.GONE }
response?.header?.let { header ->
headerInteractionListener?.setProperties(header)
}
}
}
private fun fetchData() {
showLoader()
val fundCategory = arguments?.getString(Constant.FUND_CATEGORY)
when(fundCategory) {
SubPageStatusType.FUND_LANDING -> viewModel.fetchFundLandingData()
else -> viewModel.fetchFundsDataV2(fundCategory)
}
}
override fun onClick(naviClickAction: NaviClickAction) {
when (naviClickAction) {
is NaviWidgetClickWithActionData -> {
navigateToNextScreen(naviClickAction.actionData)
}
is ActionData -> {
navigateToNextScreen(naviClickAction)
}
}
}
private fun navigateToNextScreen(actionData: ActionData?) {
sendEvent(
actionData?.metaData?.clickedData,
hashMapOf(
Pair(AmcAnalytics.FUND_ID, actionData?.getValueFromActionData(AmcAnalytics.ISIN).orEmpty()),
Pair(Constant.FUND_CATEGORY, actionData?.getValueFromActionData(Constant.FUND_CATEGORY).orEmpty())
)
)
fragmentInterchangeListener?.navigateToNextScreen(actionData)
}
override fun onAttach(context: Context) {
super.onAttach(context)
headerInteractionListener = context as? HeaderInteractionListener
fragmentInterchangeListener = context as? FragmentInterchangeListener
}
override fun onDestroyView() {
viewModel.clearData()
super.onDestroyView()
}
companion object {
fun newInstance(bundle: Bundle): FundListingFragmentV2 {
return FundListingFragmentV2().apply { arguments = bundle }
}
}
}

View File

@@ -0,0 +1,14 @@
package com.navi.amc.fundbuy.models
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
import com.navi.common.model.Header
import com.navi.naviwidgets.models.NaviWidget
import kotlinx.android.parcel.Parcelize
import java.io.Serializable
@Parcelize
data class FundListResponse(
@SerializedName("content") val listOfWidgets: List<NaviWidget>? = null,
@SerializedName("header") val header: Header? = null
) : Serializable, Parcelable

View File

@@ -9,4 +9,10 @@ class FundListRepository @Inject constructor(private val retrofitService: Retrof
suspend fun fetchFundsData(fundCategory: String?) =
apiResponseCallback(retrofitService.fetchFundListData(fundCategory))
suspend fun fetchFundsDataV2(fundCategory: String?) =
apiResponseCallback(retrofitService.fetchFundListDataV2(fundCategory))
suspend fun fetchFundLandingData() =
apiResponseCallback(retrofitService.fetchFundLandingData())
}

View File

@@ -3,6 +3,7 @@ package com.navi.amc.fundbuy.viewmodel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.navi.amc.fundbuy.models.FundListResponse
import com.navi.amc.fundbuy.models.FundListScreen
import com.navi.amc.fundbuy.repository.FundListRepository
import com.navi.common.viewmodel.BaseVM
@@ -17,6 +18,10 @@ class FundListViewModel @Inject constructor(private val repository: FundListRepo
val fundListData: LiveData<FundListScreen?>
get() = _fundListData
private val _fundListDataV2 = MutableLiveData<FundListResponse?>()
val fundListDataV2: LiveData<FundListResponse?>
get() = _fundListDataV2
fun fetchFundsData(fundCategory: String?) {
viewModelScope.launch {
val response = repository.fetchFundsData(fundCategory)
@@ -26,6 +31,24 @@ class FundListViewModel @Inject constructor(private val repository: FundListRepo
}
}
fun fetchFundsDataV2(fundCategory: String?) {
viewModelScope.launch {
val response = repository.fetchFundsDataV2(fundCategory)
if (response.error == null && response.errors.isNullOrEmpty()) {
_fundListDataV2.value = response.data
}
}
}
fun fetchFundLandingData() {
viewModelScope.launch {
val response = repository.fetchFundLandingData()
if (response.error == null && response.errors.isNullOrEmpty()) {
_fundListDataV2.value = response.data
}
}
}
fun clearData() {
errorResponse.value = null
_fundListData.value = null

View File

@@ -228,6 +228,14 @@ interface RetrofitService {
@Query("fundCategory") fundCategory: String?
): Response<GenericResponse<FundListScreen>>
@GET("/fund/fund-list/v2")
suspend fun fetchFundListDataV2(
@Query("fundCategory") fundCategory: String?
): Response<GenericResponse<FundListResponse>>
@GET("/fund/fund-landing")
suspend fun fetchFundLandingData(): Response<GenericResponse<FundListResponse>>
@GET("sip/sip-overview") suspend fun fetchSipDetails(): Response<GenericResponse<SipScreenData>>
@GET("/sip/sip-details")

View File

@@ -37,6 +37,8 @@ object AmcAnalytics {
const val CHECKER_ACTIVITY = "amc_checker_activity"
const val BUY_ACTIVITY = "amc_buy_activity"
const val FUND_LIST = "amc_init_fund_listing"
const val FUND_CATEOGRY_LIST = "amc_init_fund_listing_category"
const val FUND_LANDING = "amc_init_fund_landing"
const val FUND_DETAILS = "amc_init_mf"
const val AMC_RANDOM = "amc_random"
const val FUND_BUY_SIP_LUMPSUM_SCREEN = "fund_buy_sip_lumpsum_screen"

View File

@@ -169,6 +169,11 @@ fun getFragment(screen: String, bundle: Bundle): Fragment? {
SubPageStatusType.FUND_SELL -> FundSellFragment.newInstance(bundle)
SubPageStatusType.PORTFOLIO -> PortfolioFragment.newInstance(bundle)
SubPageStatusType.FUND_LIST -> FundListingFragment.newInstance(bundle)
SubPageStatusType.FUND_LIST_V2 -> FundListingFragmentV2.newInstance(bundle)
SubPageStatusType.FUND_LANDING -> {
bundle.putString(Constant.FUND_CATEGORY, SubPageStatusType.FUND_LANDING)
FundListingFragmentV2.newInstance(bundle)
}
SubPageStatusType.MY_INVESTMENTS -> MyInvestmentsFragment.newInstance(bundle)
SubPageStatusType.TRANSACTIONS_HISTORY -> TransactionHistoryFragment.newInstance(bundle)
SubPageStatusType.INVESTMENT_DETAILS -> InvestmentDetailsFragment.newInstance(bundle)
@@ -177,6 +182,7 @@ fun getFragment(screen: String, bundle: Bundle): Fragment? {
SubPageStatusType.AUTO_PAY_SUCCESS -> AutoPaySuccessFragment.newInstance(bundle)
SubPageStatusType.AUTO_PAY_MODIFY -> AutoPayModifyFragment.newInstance(bundle)
SubPageStatusType.FUND_CATEGORY_LIST -> FundListingFragment.newInstance(bundle)
SubPageStatusType.FUND_CATEGORY_LIST_V2 -> FundListingFragmentV2.newInstance(bundle)
SubPageStatusType.FUND_INFORMATION -> FundInformationFragment.newInstance(bundle)
SubPageStatusType.SIP_DETAILS_MODIFY -> ModifySipDetailsFragment.newInstance(bundle)
SubPageStatusType.SIP_MODIFICATION -> SipModifyFragment.newInstance(bundle)

View File

@@ -20,6 +20,8 @@ object SubPageStatusType {
const val OTP = "otp"
const val STATUS = "status"
const val FUND_LIST = "fund_list"
const val FUND_LANDING = "fund_landing"
const val FUND_LIST_V2 = "fund_list_v2"
const val FUND_DETAILS = "fund_details"
const val FUND_BUY_SIP_LUMPSUM = "fund_buy_sip_lumpsum"
const val ORDER_STATUS = "order_status"
@@ -35,6 +37,7 @@ object SubPageStatusType {
const val AUTO_PAY_SUCCESS = "auto_pay_success"
const val AUTO_PAY_MODIFY = "auto_pay_modify"
const val FUND_CATEGORY_LIST = "fund_category_list"
const val FUND_CATEGORY_LIST_V2 = "fund_category_list_v2"
const val FUND_INFORMATION = "fund_information"
const val SIP_DETAILS_MODIFY = "sip_details_modify"
const val CUTOFF_TIME_BOTTOMSHEET = "CUTOFF_TIME_BOTTOMSHEET"

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ /*
~ *
~ * * Copyright © 2022 by Navi Technologies Private Limited
~ * * All rights reserved. Strictly confidential
~ *
~ */
-->
<layout xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvProducts"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<FrameLayout
android:id="@+id/flError"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -113,9 +113,11 @@
<dimen name="dp_122">122dp</dimen>
<dimen name="dp_92">92dp</dimen>
<dimen name="dp_202">202dp</dimen>
<dimen name="dp_274">274dp</dimen>
<dimen name="dp_278">278dp</dimen>
<dimen name="dp_280">280dp</dimen>
<dimen name="dp_300">300dp</dimen>
<dimen name="dp_311">311dp</dimen>
<dimen name="dp_332">332dp</dimen>
<dimen name="dp_400">400dp</dimen>
<dimen name="dp_510">510dp</dimen>

View File

@@ -877,6 +877,9 @@ fun LottieAnimationView.showWhenDataIsAvailable(
LottieEnums.AMC_OFFER_LOTTIE.name -> {
setAnimation(R.raw.amc_offer_lottie)
}
LottieEnums.CONFETTI_FROM_ABOVE.name -> {
setAnimation(R.raw.confetti_from_above)
}
}
if (infiniteRepeat == true) {
repeatCount = LottieDrawable.INFINITE

View File

@@ -0,0 +1,50 @@
package com.navi.naviwidgets.models.response.amc
import com.google.gson.annotations.SerializedName
import com.navi.base.model.ActionData
import com.navi.design.textview.model.TextWithStyle
import com.navi.naviwidgets.models.NaviWidget
import com.navi.naviwidgets.models.response.Gradient
import com.navi.naviwidgets.models.response.WidgetError
data class FundCardWidget(
@SerializedName("widgetId") override val widgetId: String? = null,
@SerializedName("widgetName") override var widgetNameForBaseAdapter: String? = WIDGET_NAME,
@SerializedName("widgetData") val widgetData: FundCardWidgetData? = null,
@SerializedName("isDependentWidget") override val isDependentWidget: Boolean?,
@SerializedName("dependencyWidgetId") override val dependencyWidgetId: String?,
override var isDependencyWidgetShowing: Boolean? = null,
override var widgetError: WidgetError? = null
) : NaviWidget() {
companion object {
const val WIDGET_NAME = "FUND_CARD_WIDGET"
}
}
data class FundCardWidgetData(
@SerializedName("header") val header: FundCardWidgetHeader? = null,
@SerializedName("items") val items: List<FundCardData>? = null,
@SerializedName("gradient") val gradient: Gradient? = null
)
data class FundCardData(
@SerializedName("title") val title: TextWithStyle? = null,
@SerializedName("iconCode") val iconCode: String? = null,
@SerializedName("footerIconCode") val footerIconCode: String? = null,
@SerializedName("footerBgColor") val footerBgColor: String? = null,
@SerializedName("footerTitle") val footerTitle: TextWithStyle? = null,
@SerializedName("items") val items: List<SubItemData>? = null,
@SerializedName("actionData") val action: ActionData? = null
)
data class FundCardWidgetHeader(
@SerializedName("title") val title: TextWithStyle? = null,
@SerializedName("subtitle", alternate = ["subTitle"]) val subTitle: TextWithStyle? = null,
@SerializedName("iconCode") val iconCode: String? = null
)
data class SubItemData(
@SerializedName("title") val title: TextWithStyle? = null,
@SerializedName("subTitle", alternate = ["subtitle"]) val subtitle: TextWithStyle? = null,
@SerializedName("iconCode") val iconCode: String? = null
)

View File

@@ -0,0 +1,39 @@
package com.navi.naviwidgets.models.response.amc
import com.google.gson.annotations.SerializedName
import com.navi.base.model.ActionData
import com.navi.design.textview.model.TextWithStyle
import com.navi.naviwidgets.models.NaviWidget
import com.navi.naviwidgets.models.response.WidgetError
data class FundOptionsHorizontalListWidget(
@SerializedName("widgetId") override val widgetId: String? = null,
@SerializedName("widgetName") override var widgetNameForBaseAdapter: String? = WIDGET_NAME,
@SerializedName("widgetData") val widgetData: IconHorizontalScrollData? = null,
@SerializedName("isDependentWidget") override val isDependentWidget: Boolean?,
@SerializedName("dependencyWidgetId") override val dependencyWidgetId: String?,
override var isDependencyWidgetShowing: Boolean? = null,
override var widgetError: WidgetError? = null
): NaviWidget() {
companion object {
const val WIDGET_NAME = "FUND_OPTIONS_HORIZONTAL_LIST_WIDGET"
}
}
data class IconHorizontalScrollData(
@SerializedName("title")
val title: TextWithStyle? = null,
@SerializedName("items")
val items: List<IconTitleData>? = null,
@SerializedName("bgColor")
val bgColor: String? = null
)
data class IconTitleData(
@SerializedName("iconCode")
val iconCode: String? = null,
@SerializedName("title")
val title: TextWithStyle? = null,
@SerializedName("actionData")
val action: ActionData? = null
)

View File

@@ -0,0 +1,32 @@
package com.navi.naviwidgets.models.response.amc
import com.google.gson.annotations.SerializedName
import com.navi.design.textview.model.TextWithStyle
import com.navi.naviwidgets.models.NaviWidget
import com.navi.naviwidgets.models.response.Gradient
import com.navi.naviwidgets.models.response.WidgetError
data class ListOverlappingHeaderWidget(
@SerializedName("widgetId") override val widgetId: String? = null,
@SerializedName("widgetName") override var widgetNameForBaseAdapter: String? = WIDGET_NAME,
@SerializedName("widgetData") val widgetData: ListOverlappingHeaderData? = null,
@SerializedName("isDependentWidget") override val isDependentWidget: Boolean?,
@SerializedName("dependencyWidgetId") override val dependencyWidgetId: String?,
override var isDependencyWidgetShowing: Boolean? = null,
override var widgetError: WidgetError? = null
): NaviWidget() {
companion object {
const val WIDGET_NAME = "LIST_OVERLAPPING_HEADER_WIDGET"
}
}
data class ListOverlappingHeaderData(
@SerializedName("title")
val title: TextWithStyle? = null,
@SerializedName("subtitle")
val subTitle: TextWithStyle? = null,
@SerializedName("iconCode")
val iconCode: String? = null,
@SerializedName("gradient")
val gradient: Gradient? = null
)

View File

@@ -0,0 +1,31 @@
package com.navi.naviwidgets.models.response.amc
import com.google.gson.annotations.SerializedName
import com.navi.design.textview.model.TextWithStyle
import com.navi.naviwidgets.models.NaviWidget
import com.navi.naviwidgets.models.response.WidgetError
data class RewardsDetailWidget(
@SerializedName("widgetId") override val widgetId: String? = null,
@SerializedName("widgetName") override var widgetNameForBaseAdapter: String? = WIDGET_NAME,
@SerializedName("widgetData") val widgetData: RewardsDetailWidgetData? = null,
@SerializedName("isDependentWidget") override val isDependentWidget: Boolean?,
@SerializedName("dependencyWidgetId") override val dependencyWidgetId: String?,
override var isDependencyWidgetShowing: Boolean? = null,
override var widgetError: WidgetError? = null
): NaviWidget() {
companion object {
const val WIDGET_NAME = "REWARDS_DETAIL_WIDGET"
}
}
data class RewardsDetailWidgetData(
@SerializedName("title")
val title: TextWithStyle? = null,
@SerializedName("label")
val label: LabelData? = null,
@SerializedName("icon")
val icon: String? = null,
@SerializedName("bgColor")
val bgColor: String? = null,
)

View File

@@ -0,0 +1,54 @@
package com.navi.naviwidgets.models.response.amc
import com.google.gson.annotations.SerializedName
import com.navi.base.model.ActionData
import com.navi.base.model.ImageDetails
import com.navi.design.textview.model.TextWithStyle
import com.navi.naviwidgets.models.NaviWidget
import com.navi.naviwidgets.models.response.Gradient
import com.navi.naviwidgets.models.response.WidgetError
class TopProductHorizontalListWidget (
@SerializedName("widgetId") override val widgetId: String? = null,
@SerializedName("widgetName") override var widgetNameForBaseAdapter: String? = WIDGET_NAME,
@SerializedName("widgetData") val widgetData: TopProductHorizontalListData? = null,
@SerializedName("isDependentWidget") override val isDependentWidget: Boolean?,
@SerializedName("dependencyWidgetId") override val dependencyWidgetId: String?,
override var isDependencyWidgetShowing: Boolean? = null,
override var widgetError: WidgetError? = null
): NaviWidget() {
companion object {
const val WIDGET_NAME = "TOP_PRODUCT_HORIZONTAL_LIST_WIDGET"
}
}
data class TopProductHorizontalListData(
@SerializedName("header") val header: TopProductHorizontalListHeader? = null,
@SerializedName("items") val items: List<TopProductItem>? = null,
)
data class TopProductHorizontalListHeader(
@SerializedName("title") val title: TextWithStyle? = null,
@SerializedName("iconCode") val iconCode: String? = null
)
data class TopProductItem(
@SerializedName("iconCode") val iconCode: String? = null,
@SerializedName("imageDetails") val imageDetails: ImageDetails?,
@SerializedName("gradient") val gradient: Gradient? = null,
@SerializedName("title") val title: TextWithStyle? = null,
@SerializedName("subTitle", alternate = ["subtitle"]) val subTitle: TextWithStyle? = null,
@SerializedName("rightArrowIconCode") val rightArrowIconCode: String? = null,
@SerializedName("footerIconCode") val footerIconCode: String? = null,
@SerializedName("footerTitle") val footerTitle: TextWithStyle? = null,
@SerializedName("footerBgColor") val footerBgColor: String? = null,
@SerializedName("lottieFileName") val lottieFileName: String? = null,
@SerializedName("label") val label: LabelData? = null,
@SerializedName("actionTitle") val actionTitle: TextWithStyle? = null,
@SerializedName("actionData") val actionData: ActionData? = null,
)
data class LabelData(
@SerializedName("title") val title: TextWithStyle? = null,
@SerializedName("bgColor") val bgColor: String? = null
)

View File

@@ -16,6 +16,7 @@ enum class LottieEnums {
CARD_SHIMMER,
ORANGE_GRADIENT_LOADER,
GIFT_REWARD_LOTTIE,
CONFETTI_FROM_ABOVE,
AMC_REWARD_LOTTIE,
SMALL_AMC_REWARD_LOTTIE,
AMC_OFFER_LOTTIE

View File

@@ -230,6 +230,8 @@ object NaviWidgetIconUtils {
private const val ICON_INFO_SIMPLE = "ICON_INFO_SIMPLE"
private const val ICON_OTHERS_SELECTED = "ICON_OTHERS_SELECTED"
private const val CHEVRON = "CHEVRON"
private const val CHEVRON_V2 = "CHEVRON_V2"
private const val CHEVRON_V3 = "CHEVRON_V3"
private const val CHEVRON_ICON_WITH_WHITE_BG = "CHEVRON_ICON_WITH_WHITE_BG"
private const val TRENDING_ICON = "TRENDING_ICON"
private const val ICON_BACK_ARROW = "ICON_BACK_ARROW"
@@ -678,6 +680,8 @@ object NaviWidgetIconUtils {
ICON_SECURITY_CHECK -> R.drawable.ic_security_check_svg
ICON_INFO_SIMPLE -> R.drawable.ic_info_icon_svg
CHEVRON -> R.drawable.ic_chevrons
CHEVRON_V2-> R.drawable.ic_chevrons_v2
CHEVRON_V3-> R.drawable.ic_chevron_v3
CHEVRON_ICON_WITH_WHITE_BG -> R.drawable.ic_chevrons_with_white_bg_svg
CHAT_NEW_ICON -> R.drawable.ic_new_chat_svg
CALL_NEW_ICON -> R.drawable.ic_new_phone_call_svg

View File

@@ -19,7 +19,9 @@ import com.navi.naviwidgets.interfaces.hospitalcard.HospitalCardWidgetInfo
import com.navi.naviwidgets.interfaces.hospitalcard.HospitalCardWidgetV2Info
import com.navi.naviwidgets.models.*
import com.navi.naviwidgets.models.response.*
import com.navi.naviwidgets.models.response.amc.*
import com.navi.naviwidgets.models.response.callout.CalloutWidget
import com.navi.naviwidgets.viewholder.amc.*
import com.navi.naviwidgets.viewholder.callout.CalloutWidgetVH
import com.navi.naviwidgets.viewholder.hospitalcard.HospitalCardV2VH
import com.navi.naviwidgets.viewholder.hospitalcard.HospitalCardVH
@@ -210,6 +212,11 @@ class ViewHolderFactoryImpl<T : NaviBaseAdapterModel> : ViewHolderTypeFactory<T>
private val ICON_TITLE_WITH_CONSENT = R.layout.layout_icon_title_with_consent_widget
private val AUTOPAY_STEPS_WIDGET = R.layout.widget_autopay_steps
private val TITLE_END_ICON_WITH_GRADIENT = R.layout.layout_title_end_icon_with_gradient_widget
private val FUND_CARD_WIDGET = R.layout.layout_fund_card_section
private val FUND_OPTIONS_HORIZONTAL_LIST_WIDGET = R.layout.layout_fund_options_horizontal_list
private val LIST_OVERLAPPING_HEADER_WIDGET = R.layout.layout_list_overlapping_header
private val TOP_PRODUCT_HORIZONTAL_LIST_WIDGET = R.layout.layout_top_product_horizontal_list
private val REWARDS_DETAIL_WIDGET = R.layout.layout_rewards_detail_widget
private val PRODUCT_CLICK_CARD_CAROUSAL_WIDGET = R.layout.product_click_card_carousal_widget
// Incase unknown/un-registered widget comes we need to handle that
private val GENERIC_WIDGET = R.layout.dummy_content_view
@@ -376,6 +383,11 @@ class ViewHolderFactoryImpl<T : NaviBaseAdapterModel> : ViewHolderTypeFactory<T>
is IconTitleWithConsentWidget -> ICON_TITLE_WITH_CONSENT
is AutopayStepsWidgetModel -> AUTOPAY_STEPS_WIDGET
is TitleEndIconWithGradientWidget -> TITLE_END_ICON_WITH_GRADIENT
is FundCardWidget -> FUND_CARD_WIDGET
is FundOptionsHorizontalListWidget -> FUND_OPTIONS_HORIZONTAL_LIST_WIDGET
is ListOverlappingHeaderWidget -> LIST_OVERLAPPING_HEADER_WIDGET
is TopProductHorizontalListWidget -> TOP_PRODUCT_HORIZONTAL_LIST_WIDGET
is RewardsDetailWidget -> REWARDS_DETAIL_WIDGET
is ProductClickCardCarousalWidget -> PRODUCT_CLICK_CARD_CAROUSAL_WIDGET
is BenefitExplainerWidgetBodyData -> BENEFIT_EXPLAINER_WIDGET
// Default case, add all widgets above this
@@ -553,6 +565,11 @@ class ViewHolderFactoryImpl<T : NaviBaseAdapterModel> : ViewHolderTypeFactory<T>
ICON_TITLE_WITH_CONSENT -> IconTitleWithConsentWidgetVH(parent)
AUTOPAY_STEPS_WIDGET -> AutopayStepWidgetVH(parent)
TITLE_END_ICON_WITH_GRADIENT -> TitleEndIconWithGradientWidgetVH(parent)
FUND_CARD_WIDGET -> FundCardWidgetVH(parent)
FUND_OPTIONS_HORIZONTAL_LIST_WIDGET -> FundOptionsHorizontalListVH(parent)
LIST_OVERLAPPING_HEADER_WIDGET -> ListOverlappingHeaderVH(parent)
TOP_PRODUCT_HORIZONTAL_LIST_WIDGET -> TopProductHorizontalListVH(parent)
REWARDS_DETAIL_WIDGET -> RewardsDetailWidgetVH(parent)
PRODUCT_CLICK_CARD_CAROUSAL_WIDGET -> ProductClickCardCarousalWidgetVH(parent)
CENTRE_TITLE_SUBTITLE_WIDGET -> CentreTitleSubtitleWidgetVH(parent)
PRODUCT_INFO_WIDGET_V2 -> ProductInfoWidgetV2VH(parent)

View File

@@ -0,0 +1,28 @@
package com.navi.naviwidgets.viewholder.amc
import androidx.databinding.ViewDataBinding
import com.navi.naviwidgets.callbacks.WidgetCallback
import com.navi.naviwidgets.databinding.LayoutFundCardSectionBinding
import com.navi.naviwidgets.models.response.amc.FundCardWidget
import com.navi.naviwidgets.viewholder.BaseViewHolder
import com.navi.naviwidgets.widgets.amc.FundCardWidgetLayout
class FundCardWidgetVH(private val viewBinding: ViewDataBinding):
BaseViewHolder<FundCardWidget>(view = viewBinding.root) {
override fun bind(
model: FundCardWidget,
widgetCallback: WidgetCallback,
position: Int,
totalItems: Int
) {
if(itemView is FundCardWidgetLayout) {
itemView.update(model, viewBinding as LayoutFundCardSectionBinding, widgetCallback, position)
}
}
override fun bindError(errorData: Any?) {
}
override fun bindWidgetStateChanged(payload: Any?) {
}
}

View File

@@ -0,0 +1,28 @@
package com.navi.naviwidgets.viewholder.amc
import androidx.databinding.ViewDataBinding
import com.navi.naviwidgets.callbacks.WidgetCallback
import com.navi.naviwidgets.databinding.LayoutFundOptionsHorizontalListBinding
import com.navi.naviwidgets.models.response.amc.FundOptionsHorizontalListWidget
import com.navi.naviwidgets.viewholder.BaseViewHolder
import com.navi.naviwidgets.widgets.amc.FundOptionsHorizontalListLayout
class FundOptionsHorizontalListVH(private val viewBinding: ViewDataBinding)
: BaseViewHolder<FundOptionsHorizontalListWidget>(view = viewBinding.root) {
override fun bind(
model: FundOptionsHorizontalListWidget,
widgetCallback: WidgetCallback,
position: Int,
totalItems: Int
) {
if(itemView is FundOptionsHorizontalListLayout) {
itemView.update(model, viewBinding as LayoutFundOptionsHorizontalListBinding, widgetCallback, position)
}
}
override fun bindError(errorData: Any?) {
}
override fun bindWidgetStateChanged(payload: Any?) {
}
}

View File

@@ -0,0 +1,29 @@
package com.navi.naviwidgets.viewholder.amc
import androidx.databinding.ViewDataBinding
import com.navi.naviwidgets.callbacks.WidgetCallback
import com.navi.naviwidgets.databinding.LayoutListOverlappingHeaderBinding
import com.navi.naviwidgets.models.response.amc.ListOverlappingHeaderWidget
import com.navi.naviwidgets.viewholder.BaseViewHolder
import com.navi.naviwidgets.widgets.amc.ListOverlappingHeaderLayout
class ListOverlappingHeaderVH(
private val viewBinding: ViewDataBinding
): BaseViewHolder<ListOverlappingHeaderWidget>(view = viewBinding.root) {
override fun bind(
model: ListOverlappingHeaderWidget,
widgetCallback: WidgetCallback,
position: Int,
totalItems: Int
) {
if(itemView is ListOverlappingHeaderLayout) {
itemView.update(model, viewBinding as LayoutListOverlappingHeaderBinding, widgetCallback, position)
}
}
override fun bindError(errorData: Any?) {
}
override fun bindWidgetStateChanged(payload: Any?) {
}
}

View File

@@ -0,0 +1,30 @@
package com.navi.naviwidgets.viewholder.amc
import androidx.databinding.ViewDataBinding
import com.navi.naviwidgets.callbacks.WidgetCallback
import com.navi.naviwidgets.databinding.LayoutRewardsDetailWidgetBinding
import com.navi.naviwidgets.models.response.amc.RewardsDetailWidget
import com.navi.naviwidgets.viewholder.BaseViewHolder
import com.navi.naviwidgets.widgets.amc.RewardsDetailWidgetLayout
class RewardsDetailWidgetVH(
private val viewBinding: ViewDataBinding
): BaseViewHolder<RewardsDetailWidget>(view = viewBinding.root) {
override fun bind(
model: RewardsDetailWidget,
widgetCallback: WidgetCallback,
position: Int,
totalItems: Int
) {
if(itemView is RewardsDetailWidgetLayout) {
itemView.update(model, viewBinding as LayoutRewardsDetailWidgetBinding, widgetCallback, position)
}
}
override fun bindError(errorData: Any?) {
}
override fun bindWidgetStateChanged(payload: Any?) {
}
}

View File

@@ -0,0 +1,28 @@
package com.navi.naviwidgets.viewholder.amc
import androidx.databinding.ViewDataBinding
import com.navi.naviwidgets.callbacks.WidgetCallback
import com.navi.naviwidgets.databinding.LayoutTopProductHorizontalListBinding
import com.navi.naviwidgets.models.response.amc.TopProductHorizontalListWidget
import com.navi.naviwidgets.viewholder.BaseViewHolder
import com.navi.naviwidgets.widgets.amc.TopProductHorizontalListLayout
class TopProductHorizontalListVH(private val viewBinding: ViewDataBinding):
BaseViewHolder<TopProductHorizontalListWidget>(view = viewBinding.root) {
override fun bind(
model: TopProductHorizontalListWidget,
widgetCallback: WidgetCallback,
position: Int,
totalItems: Int
) {
if(itemView is TopProductHorizontalListLayout) {
itemView.update(model, viewBinding as LayoutTopProductHorizontalListBinding, widgetCallback, position)
}
}
override fun bindError(errorData: Any?) {
}
override fun bindWidgetStateChanged(payload: Any?) {
}
}

View File

@@ -7,6 +7,9 @@ import android.view.LayoutInflater
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.databinding.DataBindingUtil
import com.navi.base.model.ActionData
import com.navi.design.textview.model.TextWithStyle
import com.navi.design.utils.parseColorSafe
import com.navi.design.utils.setSpannableString
import com.navi.naviwidgets.R
import com.navi.naviwidgets.callbacks.WidgetCallback
import com.navi.naviwidgets.databinding.ButtonViewBinding
@@ -22,9 +25,13 @@ class ButtonViewLayout(context: Context, attributeSet: AttributeSet) :
binding = DataBindingUtil.inflate(infater, R.layout.button_view, this, true)
}
fun setProperties(actionData: ActionData?, widgetCallback: WidgetCallback?=null) {
binding.title.text = actionData?.title
binding.title.setTextColor(Color.parseColor(actionData?.titleColor))
fun setProperties(actionData: ActionData?, widgetCallback: WidgetCallback?=null, title: TextWithStyle? = null) {
if(title != null) {
binding.title.setSpannableString(title)
} else {
binding.title.text = actionData?.title
binding.title.setTextColor(Color.parseColor(actionData?.titleColor))
}
post {
val radius = binding.rootLayout.height / 2.0
binding.rootLayout.setCornerRadius(radius)

View File

@@ -13,6 +13,7 @@ import com.google.gson.JsonElement
import com.navi.naviwidgets.models.*
import com.navi.naviwidgets.models.ActionButtonWidget
import com.navi.naviwidgets.models.response.*
import com.navi.naviwidgets.models.response.amc.*
import com.navi.naviwidgets.models.response.dashboard.*
import com.navi.naviwidgets.models.response.esign.ApplicantESignWidgetModel
import com.navi.naviwidgets.widgets.bottomsheetselector.LabeledOptionSelectorListWidgetModel
@@ -218,6 +219,11 @@ class NaviWidgetJsonDeserializer : JsonDeserializer<NaviWidget> {
IconTitleWithConsentWidget.WIDGET_NAME -> IconTitleWithConsentWidget::class.java
AutopayStepsWidgetModel.WIDGET_NAME -> AutopayStepsWidgetModel::class.java
TitleEndIconWithGradientWidget.WIDGET_NAME -> TitleEndIconWithGradientWidget::class.java
FundCardWidget.WIDGET_NAME -> FundCardWidget::class.java
FundOptionsHorizontalListWidget.WIDGET_NAME -> FundOptionsHorizontalListWidget::class.java
ListOverlappingHeaderWidget.WIDGET_NAME -> ListOverlappingHeaderWidget::class.java
TopProductHorizontalListWidget.WIDGET_NAME -> TopProductHorizontalListWidget::class.java
RewardsDetailWidget.WIDGET_NAME -> RewardsDetailWidget::class.java
else -> null
}
return if (className != null) {

View File

@@ -0,0 +1,179 @@
package com.navi.naviwidgets.widgets.amc
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.navi.base.utils.isNotNullAndNotEmpty
import com.navi.design.utils.*
import com.navi.naviwidgets.R
import com.navi.naviwidgets.callbacks.WidgetCallback
import com.navi.naviwidgets.databinding.LayoutFundCardBinding
import com.navi.naviwidgets.databinding.LayoutFundCardSectionBinding
import com.navi.naviwidgets.extensions.setGradient
import com.navi.naviwidgets.extensions.showWhenDataIsAvailable
import com.navi.naviwidgets.models.response.amc.FundCardData
import com.navi.naviwidgets.models.response.amc.FundCardWidget
class FundCardWidgetLayout @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr), LifecycleObserver {
private lateinit var binding: LayoutFundCardSectionBinding
private lateinit var widgetCallback: WidgetCallback
private var widgetPosition: Int = Int.MAX_VALUE
private lateinit var widgetData: FundCardWidget
private var lifecycle: Lifecycle? = null
fun update(
widgetData: FundCardWidget,
binding: LayoutFundCardSectionBinding,
widgetCallback: WidgetCallback,
position: Int
) {
this.widgetData = widgetData
this.binding = binding
this.widgetCallback = widgetCallback
this.widgetPosition = position
this.lifecycle = widgetCallback.getLifeCycle()
lifecycle?.addObserver(this)
setProperties()
}
private fun setProperties() {
binding.apply {
titleTv.setSpannableString(widgetData.widgetData?.header?.title)
subtitleTv.setSpannableString(widgetData.widgetData?.header?.subTitle)
iconIv.showWhenDataIsAvailable(widgetData.widgetData?.header?.iconCode)
widgetData.widgetData?.items?.let { list ->
fundCardList.apply {
layoutManager = LinearLayoutManager(context)
adapter = FundCardAdapter(list, widgetCallback)
}
} ?: run {
fundCardList.visibility = View.GONE
}
widgetData.widgetData?.gradient?.let {
root.setGradient(it)
}
}
}
}
class FundCardAdapter(
private val list: List<FundCardData> = emptyList(),
private val widgetCallback: WidgetCallback
) : RecyclerView.Adapter<CardViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CardViewHolder {
val view =
LayoutInflater.from(parent.context).inflate(R.layout.layout_fund_card, parent, false)
val itemBinding: LayoutFundCardBinding = DataBindingUtil.bind(view)!!
return CardViewHolder(itemBinding, widgetCallback)
}
override fun onBindViewHolder(holder: CardViewHolder, position: Int) {
holder.bind(list[position], position)
}
override fun getItemCount() =
list.size
}
class CardViewHolder(
private val viewBinding: LayoutFundCardBinding,
private val widgetCallback: WidgetCallback
) : RecyclerView.ViewHolder(viewBinding.root) {
fun bind(card: FundCardData, position: Int) {
viewBinding.apply {
fundCard.background = getNaviDrawable(
cornerRadius = dpToPx(8).toInt(),
strokeColor = ContextCompat.getColor(
itemView.context,
R.color.border_grey_color
),
strokeWidth = dpToPx(1).toInt(),
backgroundColor = ContextCompat.getColor(
itemView.context,
R.color.white
)
)
fundTitle.setSpannableString(card.title)
icon.showWhenDataIsAvailable(card.iconCode)
card.items?.forEachIndexed { index, subItemData ->
when(index) {
0 -> {
fundSize.title.setSpannableString(subItemData.title)
fundSize.subtitle.setSpannableString(subItemData.subtitle)
if(subItemData.title?.text.isNotNullAndNotEmpty()) {
fundSize.titleIcon.showWhenDataIsAvailable(subItemData.iconCode)
fundSize.icon.visibility = View.GONE
}
else {
fundSize.icon.showWhenDataIsAvailable(subItemData.iconCode)
fundSize.title.visibility = View.GONE
}
}
1 -> {
returns.title.setSpannableString(subItemData.title)
returns.subtitle.setSpannableString(subItemData.subtitle)
if(subItemData.title?.text.isNotNullAndNotEmpty()) {
returns.titleIcon.showWhenDataIsAvailable(subItemData.iconCode)
returns.icon.visibility = View.GONE
}
else {
returns.icon.showWhenDataIsAvailable(subItemData.iconCode)
returns.title.visibility = View.GONE
}
}
2 -> {
cta.title.setSpannableString(subItemData.title)
cta.subtitle.setSpannableString(subItemData.subtitle)
if(subItemData.title?.text.isNotNullAndNotEmpty()) {
cta.titleIcon.showWhenDataIsAvailable(subItemData.iconCode)
cta.icon.visibility = View.GONE
}
else {
cta.icon.showWhenDataIsAvailable(subItemData.iconCode)
cta.title.visibility = View.GONE
}
}
}
}
if(card.footerTitle?.text.isNotNullAndNotEmpty()) {
productStrategyContainer.background = getNaviDrawable(
radii = CornerRadius(
leftBottom = dpToPx(8),
rightBottom = dpToPx(8)
),
backgroundColor = card.footerBgColor.parseColorSafe()
)
productStrategyTv.setSpannableString(card.footerTitle)
productStrategyIv.showWhenDataIsAvailable(card.footerIconCode)
} else {
productStrategyContainer.visibility = View.GONE
}
fundCard.setOnClickListener {
card.action?.let { actionData ->
widgetCallback.onClick(actionData)
}
}
}
}
}

View File

@@ -0,0 +1,71 @@
package com.navi.naviwidgets.widgets.amc
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import com.navi.design.utils.parseColorSafe
import com.navi.design.utils.setSpannableString
import com.navi.naviwidgets.R
import com.navi.naviwidgets.callbacks.WidgetCallback
import com.navi.naviwidgets.databinding.LayoutFundOptionsHorizontalListBinding
import com.navi.naviwidgets.databinding.TitleWithImageIconBinding
import com.navi.naviwidgets.extensions.showWhenDataIsAvailable
import com.navi.naviwidgets.models.response.amc.FundOptionsHorizontalListWidget
class FundOptionsHorizontalListLayout @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr), LifecycleObserver {
private lateinit var binding: LayoutFundOptionsHorizontalListBinding
private lateinit var widgetCallback: WidgetCallback
private var widgetPosition: Int = Int.MAX_VALUE
private lateinit var widgetData: FundOptionsHorizontalListWidget
private var lifecycle: Lifecycle? = null
val inflater = LayoutInflater.from(context)
fun update(
widgetData: FundOptionsHorizontalListWidget,
binding: LayoutFundOptionsHorizontalListBinding,
widgetCallback: WidgetCallback,
position: Int
) {
this.widgetData = widgetData
this.binding = binding
this.widgetCallback = widgetCallback
this.widgetPosition = position
this.lifecycle = widgetCallback.getLifeCycle()
lifecycle?.addObserver(this)
setProperties()
}
private fun setProperties() {
binding.apply {
items.removeAllViews()
root.setBackgroundColor(widgetData.widgetData?.bgColor.parseColorSafe())
title.setSpannableString(widgetData.widgetData?.title)
widgetData.widgetData?.items?.forEach { item ->
val childBinding =
DataBindingUtil.inflate<TitleWithImageIconBinding>(
inflater,
R.layout.title_with_image_icon,
binding.container,
false
)
childBinding.icon.showWhenDataIsAvailable(item.iconCode)
childBinding.title.setSpannableString(item.title)
childBinding.root.setOnClickListener {
item.action?.let {
widgetCallback.onClick(it)
}
}
items.addView(childBinding.root)
}
}
}
}

View File

@@ -0,0 +1,52 @@
package com.navi.naviwidgets.widgets.amc
import android.content.Context
import android.util.AttributeSet
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import com.navi.design.utils.setSpannableString
import com.navi.naviwidgets.callbacks.WidgetCallback
import com.navi.naviwidgets.databinding.LayoutListOverlappingHeaderBinding
import com.navi.naviwidgets.extensions.setGradient
import com.navi.naviwidgets.extensions.showWhenDataIsAvailable
import com.navi.naviwidgets.models.response.amc.ListOverlappingHeaderWidget
class ListOverlappingHeaderLayout @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr), LifecycleObserver {
private lateinit var binding: LayoutListOverlappingHeaderBinding
private lateinit var widgetCallback: WidgetCallback
private var widgetPosition: Int = Int.MAX_VALUE
private lateinit var widgetData: ListOverlappingHeaderWidget
private var lifecycle: Lifecycle? = null
fun update(
widgetData: ListOverlappingHeaderWidget,
binding: LayoutListOverlappingHeaderBinding,
widgetCallback: WidgetCallback,
position: Int
) {
this.widgetData = widgetData
this.binding = binding
this.widgetCallback = widgetCallback
this.widgetPosition = position
this.lifecycle = widgetCallback.getLifeCycle()
lifecycle?.addObserver(this)
setProperties()
}
private fun setProperties() {
binding.apply {
icon.showWhenDataIsAvailable(widgetData.widgetData?.iconCode)
title.setSpannableString(widgetData.widgetData?.title)
subtitle.setSpannableString(widgetData.widgetData?.subTitle)
widgetData.widgetData?.gradient?.let { gradient ->
root.setGradient(gradient)
}
}
}
}

View File

@@ -0,0 +1,57 @@
package com.navi.naviwidgets.widgets.amc
import android.content.Context
import android.util.AttributeSet
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import com.navi.design.utils.dpToPx
import com.navi.design.utils.getNaviDrawable
import com.navi.design.utils.parseColorSafe
import com.navi.design.utils.setSpannableString
import com.navi.naviwidgets.callbacks.WidgetCallback
import com.navi.naviwidgets.databinding.LayoutRewardsDetailWidgetBinding
import com.navi.naviwidgets.extensions.showWhenDataIsAvailable
import com.navi.naviwidgets.models.response.amc.RewardsDetailWidget
class RewardsDetailWidgetLayout @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr), LifecycleObserver {
private lateinit var binding: LayoutRewardsDetailWidgetBinding
private lateinit var widgetCallback: WidgetCallback
private var widgetPosition: Int = Int.MAX_VALUE
private lateinit var widgetData: RewardsDetailWidget
private var lifecycle: Lifecycle? = null
fun update(
widgetData: RewardsDetailWidget,
binding: LayoutRewardsDetailWidgetBinding,
widgetCallback: WidgetCallback,
position: Int
) {
this.widgetData = widgetData
this.binding = binding
this.widgetCallback = widgetCallback
this.widgetPosition = position
this.lifecycle = widgetCallback.getLifeCycle()
lifecycle?.addObserver(this)
setProperties()
}
private fun setProperties() {
binding.apply {
title.setSpannableString(widgetData.widgetData?.title)
icon.showWhenDataIsAvailable(widgetData.widgetData?.icon)
label.setSpannableString(widgetData.widgetData?.label?.title)
label.background = getNaviDrawable(
radii = com.navi.design.utils.CornerRadius(
leftBottom = dpToPx(8)
), backgroundColor = widgetData.widgetData?.label?.bgColor.parseColorSafe()
)
root.setBackgroundColor(widgetData.widgetData?.bgColor.parseColorSafe())
}
}
}

View File

@@ -0,0 +1,133 @@
package com.navi.naviwidgets.widgets.amc
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.LinearLayout
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import com.navi.design.utils.*
import com.navi.naviwidgets.R
import com.navi.naviwidgets.callbacks.WidgetCallback
import com.navi.naviwidgets.databinding.LayoutTopProductHorizontalListBinding
import com.navi.naviwidgets.databinding.TopProductHorizontalListItemBinding
import com.navi.naviwidgets.extensions.setGradient
import com.navi.naviwidgets.extensions.showWhenDataIsAvailable
import com.navi.naviwidgets.models.response.amc.TopProductHorizontalListWidget
import com.navi.naviwidgets.models.response.amc.TopProductItem
class TopProductHorizontalListLayout @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr), LifecycleObserver {
private lateinit var binding: LayoutTopProductHorizontalListBinding
private lateinit var widgetCallback: WidgetCallback
private var widgetPosition: Int = Int.MAX_VALUE
private lateinit var widgetData: TopProductHorizontalListWidget
private var lifecycle: Lifecycle? = null
private val inflater = LayoutInflater.from(context)
fun update(
widgetData: TopProductHorizontalListWidget,
binding: LayoutTopProductHorizontalListBinding,
widgetCallback: WidgetCallback,
position: Int
) {
this.widgetData = widgetData
this.binding = binding
this.widgetCallback = widgetCallback
this.widgetPosition = position
this.lifecycle = widgetCallback.getLifeCycle()
lifecycle?.addObserver(this)
setProperties()
}
private fun setProperties() {
binding.apply {
widgetData.widgetData?.items?.forEachIndexed { index, topProductItem ->
bindItem(topProductItem, index, widgetData.widgetData?.items?.size ?: 0)
}
}
}
private fun bindItem(item: TopProductItem, position: Int, totalItems: Int) {
val view = inflater.inflate(R.layout.top_product_horizontal_list_item, binding.itemContainer, false)
val itemBinding: TopProductHorizontalListItemBinding = DataBindingUtil.bind(view)!!
itemBinding.productTitle.setSpannableString(item.title)
itemBinding.productSubtitle.setSpannableString(item.subTitle)
item.imageDetails?.let { imageDetails ->
imageDetails.height?.let {
itemBinding.productImage.layoutParams.height = dpToPxInInt(it)
}
imageDetails.width?.let {
itemBinding.productImage.layoutParams.width = dpToPxInInt(it)
}
itemBinding.productImage.requestLayout()
imageDetails.iconCode?.let {
itemBinding.productImage.showWhenDataIsAvailable(it)
}
} ?: kotlin.run {
itemBinding.productImage.showWhenDataIsAvailable(item.iconCode)
}
item.gradient?.let {
itemBinding.productCard.setGradient(it)
}
itemBinding.productLabel.apply {
background = getNaviDrawable(
radii = CornerRadius(
0f,
0f,
dpToPx(4),
dpToPx(4)
),
backgroundColor = item.label?.bgColor.parseColorSafe()
)
setSpannableString(item.label?.title)
}
itemBinding.productStrategyTv.setSpannableString(item.footerTitle)
itemBinding.productStrategyIv.showWhenDataIsAvailable(item.footerIconCode)
itemBinding.productStrategyContainer.setBackgroundColor(item.footerBgColor.parseColorSafe())
itemBinding.productLottie.showWhenDataIsAvailable(item.lottieFileName)
itemBinding.actionBtn.setProperties(item.actionData, widgetCallback, item.actionTitle)
if (totalItems == 1) {
val layoutParams = LinearLayout.LayoutParams(
deviceWidth() - resources.getDimension(R.dimen.dp_32).toInt(),
resources.getDimension(R.dimen.dp_274).toInt()
)
layoutParams.rightMargin = dpToPx(16).toInt()
layoutParams.leftMargin = dpToPx(16).toInt()
layoutParams.bottomMargin = dpToPx(16).toInt()
itemBinding.widgetContainer.layoutParams = layoutParams
} else if (position == totalItems - 1) {
val layoutParams = LinearLayout.LayoutParams(
(deviceWidth() * 0.86).toInt(),
resources.getDimension(R.dimen.dp_274).toInt()
)
layoutParams.rightMargin = dpToPx(16).toInt()
layoutParams.leftMargin = dpToPx(16).toInt()
layoutParams.bottomMargin = dpToPx(16).toInt()
itemBinding.widgetContainer.layoutParams = layoutParams
} else {
val layoutParams = LinearLayout.LayoutParams(
(deviceWidth() * 0.86).toInt(),
resources.getDimension(R.dimen.dp_274).toInt()
)
layoutParams.leftMargin = dpToPx(16).toInt()
layoutParams.bottomMargin = dpToPx(16).toInt()
itemBinding.widgetContainer.layoutParams = layoutParams
}
itemBinding.root.setOnClickListener {
item.actionData?.let { actionData ->
widgetCallback.onClick(actionData)
}
}
binding.itemContainer.addView(view)
}
}

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<size
android:height="@dimen/dp_0"
android:width="@dimen/dp_24"/>
</shape>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="16dp"
android:height="16dp"
android:viewportWidth="16"
android:viewportHeight="16">
<path
android:pathData="M5.529,3.529C5.789,3.268 6.211,3.268 6.471,3.529L10.471,7.529C10.732,7.789 10.732,8.211 10.471,8.471L6.471,12.471C6.211,12.732 5.789,12.732 5.529,12.471C5.268,12.211 5.268,11.789 5.529,11.529L9.057,8L5.529,4.471C5.268,4.211 5.268,3.789 5.529,3.529Z"
android:fillColor="#FF5732"
android:fillType="evenOdd"/>
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M16.465,12.019C16.465,12.368 16.316,12.667 16.066,12.867L10.529,17.806C10.13,18.105 9.531,18.055 9.232,17.656C8.932,17.257 8.932,16.708 9.331,16.409L14.22,12.118C14.27,12.068 14.27,12.019 14.22,11.919L9.331,7.628C8.932,7.279 8.882,6.73 9.232,6.331C9.581,5.932 10.13,5.882 10.529,6.232C10.529,6.232 10.529,6.232 10.579,6.281L16.116,11.17C16.316,11.37 16.465,11.719 16.465,12.019Z"
android:fillColor="#FF5732"/>
</vector>

View File

@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/title_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent">
<com.navi.design.textview.NaviTextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:gravity="start"
tools:text="Expense ratio" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/title_icon"
android:layout_width="@dimen/dp_16"
android:layout_height="@dimen/dp_16"
android:layout_marginStart="@dimen/dp_4"
android:layout_gravity="bottom"
tools:src="@drawable/arrow_forward_black" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/icon"
android:layout_width="@dimen/dp_24"
android:layout_height="@dimen/dp_24"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:src="@drawable/arrow_forward_black" />
<com.navi.design.textview.NaviTextView
android:id="@+id/subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_2"
android:gravity="start"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/title_container"
tools:text="0.50%" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -0,0 +1,110 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<androidx.cardview.widget.CardView
android:id="@+id/fund_card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/dp_16"
android:layout_marginBottom="@dimen/dp_16"
android:outlineSpotShadowColor="@color/outlineSpotShadowColor"
app:cardCornerRadius="@dimen/dp_8"
app:cardElevation="@dimen/dp_8"
app:layout_constraintBottom_toBottomOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/fund_card2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/dp_16"
app:layout_constraintBottom_toBottomOf="parent">
<com.navi.design.textview.NaviTextView
android:id="@+id/fund_title"
android:layout_width="@dimen/dp_0"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_16"
android:layout_marginEnd="@dimen/dp_16"
android:layout_marginBottom="@dimen/dp_24"
app:layout_constraintBottom_toTopOf="@id/fund_size"
app:layout_constraintEnd_toStartOf="@id/icon"
app:layout_constraintStart_toStartOf="parent"
tools:text="Nifty India Manufacturing Index Fund" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/icon"
android:layout_width="@dimen/dp_24"
android:layout_height="@dimen/dp_24"
android:layout_marginEnd="@dimen/dp_16"
app:layout_constraintBottom_toBottomOf="@id/fund_title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/fund_title"
tools:src="@drawable/arrow_forward_black" />
<include
android:id="@+id/fund_size"
layout="@layout/item_title_subtitle_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_16"
android:layout_marginBottom="@dimen/dp_16"
app:layout_constraintBottom_toTopOf="@id/product_strategy_container"
app:layout_constraintStart_toStartOf="parent" />
<include
android:id="@+id/returns"
layout="@layout/item_title_subtitle_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/dp_16"
app:layout_constraintBottom_toTopOf="@id/product_strategy_container"
app:layout_constraintEnd_toStartOf="@id/cta"
app:layout_constraintStart_toEndOf="@id/fund_size" />
<include
android:id="@+id/cta"
layout="@layout/item_title_subtitle_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/dp_16"
android:layout_marginBottom="@dimen/dp_16"
app:layout_constraintBottom_toTopOf="@id/product_strategy_container"
app:layout_constraintEnd_toEndOf="parent" />
<LinearLayout
android:id="@+id/product_strategy_container"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_32"
android:layout_marginHorizontal="@dimen/dp_1"
android:layout_marginBottom="@dimen/dp_1"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent">
<ImageView
android:id="@+id/product_strategy_iv"
android:layout_width="@dimen/dp_16"
android:layout_height="@dimen/dp_16"
android:layout_gravity="center_vertical"
android:layout_marginStart="@dimen/dp_16" />
<com.navi.design.textview.NaviTextView
android:id="@+id/product_strategy_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="@dimen/dp_8"
android:layout_marginEnd="@dimen/dp_16"
android:ellipsize="end"
android:gravity="center_vertical"
android:maxLines="1"
tools:text="20k+ investors have invested in this fund" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</layout>

View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<com.navi.naviwidgets.widgets.amc.FundCardWidgetLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/icon_iv"
android:layout_width="@dimen/dp_24"
android:layout_height="@dimen/dp_24"
android:layout_marginStart="@dimen/dp_16"
android:src="@drawable/ic_new_launch_svg"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.navi.design.textview.NaviTextView
android:id="@+id/title_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_8"
app:layout_constraintStart_toEndOf="@id/icon_iv"
app:layout_constraintTop_toTopOf="parent"
tools:text="Highest returns" />
<com.navi.design.textview.NaviTextView
android:id="@+id/subtitle_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_4"
android:layout_marginBottom="@dimen/dp_20"
app:layout_constraintStart_toStartOf="@id/title_tv"
app:layout_constraintTop_toBottomOf="@id/title_tv"
app:layout_constraintBottom_toTopOf="@id/fund_card_list"
tools:text="Start investing with ₹10" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/fund_card_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"/>
</com.navi.naviwidgets.widgets.amc.FundCardWidgetLayout>
</layout>

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<com.navi.naviwidgets.widgets.amc.FundOptionsHorizontalListLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/dp_16"
android:paddingBottom="@dimen/dp_24">
<com.navi.design.textview.NaviTextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_16"
android:layout_marginEnd="@dimen/dp_16"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Looking for specific funds?"/>
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_24"
android:clipToPadding="false"
android:paddingStart="@dimen/dp_16"
android:paddingEnd="@dimen/dp_16"
android:scrollbars="none"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/title">
<LinearLayout
android:id="@+id/items"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:divider="@drawable/divider_horizontal_24"
android:orientation="horizontal"
android:showDividers="middle" />
</HorizontalScrollView>
</com.navi.naviwidgets.widgets.amc.FundOptionsHorizontalListLayout>
</layout>

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<com.navi.naviwidgets.widgets.amc.ListOverlappingHeaderLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/dp_88">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/icon"
android:layout_width="@dimen/dp_56"
android:layout_height="@dimen/dp_56"
android:layout_marginStart="@dimen/dp_16"
android:layout_marginTop="@dimen/dp_36"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.navi.design.textview.NaviTextView
android:id="@+id/title"
android:layout_width="@dimen/dp_0"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_16"
android:layout_marginTop="@dimen/dp_32"
android:layout_marginEnd="@dimen/dp_16"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/icon"
app:layout_constraintTop_toTopOf="parent"
tools:text="Low cost Index Funds" />
<com.navi.design.textview.NaviTextView
android:id="@+id/subtitle"
android:layout_width="@dimen/dp_0"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_8"
android:layout_marginEnd="@dimen/dp_16"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/title"
app:layout_constraintTop_toBottomOf="@id/title"
tools:text="Funds that mimic financial market indices
like Nifty 50 Index subject to tracking error" />
</com.navi.naviwidgets.widgets.amc.ListOverlappingHeaderLayout>
</layout>

View File

@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<com.navi.naviwidgets.widgets.amc.RewardsDetailWidgetLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_16"
android:layout_marginTop="@dimen/dp_16"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.navi.design.textview.NaviTextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_16"
android:layout_marginTop="@dimen/dp_16"
android:layout_marginBottom="@dimen/dp_16"
android:lineSpacingExtra="@dimen/dp_4"
app:layout_constraintBottom_toTopOf="@id/divider"
app:layout_constraintStart_toEndOf="@id/icon"
app:layout_constraintTop_toTopOf="parent"
tools:text="Win instant ₹100 cashback\non completing KYC" />
<com.navi.design.textview.NaviTextView
android:id="@+id/label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="@dimen/dp_8"
android:paddingTop="@dimen/dp_2"
android:paddingEnd="@dimen/dp_8"
android:paddingBottom="@dimen/dp_2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="2 days left" />
<View
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_4"
android:background="@color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</com.navi.naviwidgets.widgets.amc.RewardsDetailWidgetLayout>
</layout>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<com.navi.naviwidgets.widgets.amc.TopProductHorizontalListLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_32"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<HorizontalScrollView
android:id="@+id/scroll_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="none"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:id="@+id/item_container"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal" />
</HorizontalScrollView>
</com.navi.naviwidgets.widgets.amc.TopProductHorizontalListLayout>
</layout>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/icon"
android:layout_width="@dimen/dp_56"
android:layout_height="@dimen/dp_56"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.navi.design.textview.NaviTextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginTop="@dimen/dp_8"
android:gravity="center"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/icon"
tools:text="Index" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -0,0 +1,127 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<androidx.cardview.widget.CardView
android:id="@+id/widget_container"
android:layout_width="@dimen/dp_311"
android:layout_height="@dimen/dp_274"
android:layout_marginStart="@dimen/dp_16"
android:outlineSpotShadowColor="@color/outlineSpotShadowColor"
app:cardCornerRadius="@dimen/dp_8"
app:cardElevation="@dimen/dp_8">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/product_card"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.navi.design.textview.NaviTextView
android:id="@+id/product_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/black"
android:textColor="@color/white"
android:layout_marginStart="@dimen/dp_16"
android:paddingHorizontal="@dimen/dp_10"
android:paddingVertical="@dimen/dp_4"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:text="Our recommendation" />
<com.navi.design.textview.NaviTextView
android:id="@+id/product_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_16"
android:layout_marginEnd="@dimen/dp_32"
android:layout_marginTop="@dimen/dp_72"
android:gravity="center_vertical"
android:lineSpacingExtra="@dimen/dp_4"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="US Total Stock Market Fund of Fund" />
<com.navi.design.textview.NaviTextView
android:id="@+id/product_subtitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_8"
android:gravity="center_vertical"
app:layout_constraintStart_toStartOf="@id/product_title"
app:layout_constraintTop_toBottomOf="@id/product_title"
app:layout_constraintEnd_toEndOf="@id/product_title"
tools:text="Invest in Indias 50 largest companies" />
<com.navi.naviwidgets.views.ButtonViewLayout
android:id="@+id/action_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_16"
android:layout_marginTop="@dimen/dp_32"
android:layout_marginBottom="@dimen/dp_16"
app:layout_constraintBottom_toTopOf="@id/product_strategy_container"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/product_subtitle"/>
<ImageView
android:id="@+id/product_image"
android:layout_width="@dimen/dp_100"
android:layout_height="@dimen/dp_82"
app:layout_constraintBottom_toTopOf="@id/divider"
app:layout_constraintEnd_toEndOf="parent" />
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/product_lottie"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:lottie_autoPlay="true"
app:lottie_loop="true" />
<View
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_2"
android:background="@color/white"
app:layout_constraintBottom_toTopOf="@+id/product_strategy_container"
app:layout_constraintStart_toStartOf="parent" />
<LinearLayout
android:id="@+id/product_strategy_container"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_40"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent">
<ImageView
android:id="@+id/product_strategy_iv"
android:layout_width="@dimen/dp_16"
android:layout_height="@dimen/dp_16"
android:layout_gravity="center_vertical"
android:layout_marginStart="@dimen/dp_16" />
<com.navi.design.textview.NaviTextView
android:id="@+id/product_strategy_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="@dimen/dp_8"
android:layout_marginEnd="@dimen/dp_16"
android:ellipsize="end"
android:maxLines="1"
android:baselineAligned="false"
android:gravity="center_vertical"
tools:text="20k+ investors have invested in this fund" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</layout>

File diff suppressed because one or more lines are too long