NTP-71867 | Amc fund details revamp (#16660)

This commit is contained in:
Sayed Owais Ali
2025-06-23 18:16:22 +05:30
committed by GitHub
parent fd3d57c8da
commit 208f10e542
31 changed files with 1367 additions and 371 deletions

View File

@@ -0,0 +1,59 @@
/*
*
* * Copyright © 2025 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.amc.fundbuy.adapters
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.navi.amc.databinding.ItemFundTagBinding
import com.navi.amc.fundbuy.models.FundTagData
import com.navi.design.textview.model.TextWithStyle
import com.navi.design.utils.dpToPxInInt
import com.navi.design.utils.getNaviDrawable
import com.navi.design.utils.parseColorSafe
import com.navi.design.utils.setSpannableString
class FundTagsAdapter : RecyclerView.Adapter<FundTagsAdapter.TagViewHolder>() {
private val pills = mutableListOf<FundTagData>()
fun updatePills(newPills: List<FundTagData>) {
pills.clear()
pills.addAll(newPills)
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TagViewHolder {
val binding = ItemFundTagBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return TagViewHolder(binding)
}
override fun onBindViewHolder(holder: TagViewHolder, position: Int) {
holder.bind(pills[position])
}
override fun getItemCount(): Int = pills.size
class TagViewHolder(private val binding: ItemFundTagBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(pill: FundTagData) {
binding.apply {
tagText.setSpannableString(TextWithStyle(pill.text, pill.span))
root.background =
getNaviDrawable(
backgroundColor = pill.bgColor.parseColorSafe(),
cornerRadius = dpToPxInInt(16),
)
root.setPadding(dpToPxInInt(4), dpToPxInInt(2), dpToPxInInt(4), dpToPxInInt(2))
}
}
}
}

View File

@@ -8,12 +8,12 @@
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 android.view.ViewStub
import android.widget.ImageView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
@@ -34,14 +34,16 @@ import com.navi.amc.common.view.InformationView
import com.navi.amc.compose.feature.ftue.model.FundSelectionData
import com.navi.amc.databinding.DownloadDocumentLayoutBinding
import com.navi.amc.databinding.FundDetailScreenLayoutBinding
import com.navi.amc.databinding.FundManagerLayoutBinding
import com.navi.amc.databinding.InvestUspLayoutBinding
import com.navi.amc.fundbuy.models.AmcHeaderData
import com.navi.amc.fundbuy.models.FundReturn
import com.navi.amc.fundbuy.viewmodel.FundBuyFlowViewModel
import com.navi.amc.fundbuy.viewmodel.FundDetailViewModel
import com.navi.amc.fundbuy.views.FundDetailCarouselView
import com.navi.amc.fundbuy.views.FundDetailView
import com.navi.amc.fundbuy.views.FundGraphToolTipView
import com.navi.amc.fundbuy.views.FundGraphView
import com.navi.amc.fundbuy.views.FundManagerView
import com.navi.amc.fundbuy.views.ListItemProgressView
import com.navi.amc.navigator.NaviAmcDeeplinkNavigator
import com.navi.amc.utils.AmcAnalytics
@@ -64,7 +66,6 @@ import com.navi.base.model.Padding
import com.navi.base.utils.isNotNull
import com.navi.base.utils.isNull
import com.navi.base.utils.orFalse
import com.navi.base.utils.orTrue
import com.navi.base.utils.orZero
import com.navi.common.listeners.FragmentInterchangeListener
import com.navi.common.listeners.HeaderInteractionListener
@@ -148,27 +149,25 @@ class FundDetailsFragment : AmcBaseFragment(), FooterInteractionListener {
viewModel.fundDetailScreenData.observe(viewLifecycleOwner) { fundDetailScreenData ->
hideLoader()
binding.apply {
binding.fundOnscrollView.apply {
left.setSpannableString(viewModel.fundReturn.value?.leftText)
right.setSpannableString(viewModel.fundReturn.value?.rightText)
root.visibility = View.GONE
}
scroll.viewTreeObserver.addOnScrollChangedListener {
val rect = Rect()
scroll.getHitRect(rect)
val view = binding.container.findViewWithTag<FundGraphView>("graph")?.getTitle()
val returnView =
binding.container
.findViewWithTag<FundGraphView>("graph")
?.getReturnDetailsView()
if (
!view?.getLocalVisibleRect(rect).orTrue() &&
!returnView?.getLocalVisibleRect(rect).orTrue()
) {
binding.fundOnscrollView.apply {
left.setSpannableString(viewModel.fundReturn.value?.leftText)
right.setSpannableString(viewModel.fundReturn.value?.rightText)
val scrollY = scroll.scrollY
binding.fundOnscrollView.apply {
left.setSpannableString(viewModel.fundReturn.value?.leftText)
right.setSpannableString(viewModel.fundReturn.value?.rightText)
if (scrollY > 0) {
if (left.isVisible && right.isVisible) {
root.visibility = View.VISIBLE
}
} else {
root.visibility = View.GONE
}
} else {
binding.fundOnscrollView.root.visibility = View.GONE
}
}
scroll.updateLayoutParams<ConstraintLayout.LayoutParams> {
@@ -249,7 +248,36 @@ class FundDetailsFragment : AmcBaseFragment(), FooterInteractionListener {
fundDetailScreenData?.content?.amcHeaderData?.let {
viewModel.fundName = it.title?.text.orEmpty()
binding.header.setProperties(it)
binding.header.setProperties(
AmcHeaderData(
title = it.title,
subtitle = it.subtitle,
icon = it.icon,
bgColor = it.bgColor,
filter = it.filter,
label = it.label,
iconSize = it.iconSize,
)
)
}
fundDetailScreenData?.content?.amcHeaderData?.fundTags?.let { fundTags ->
if (binding.fundTags.adapter == null) {
val tagsAdapter = com.navi.amc.fundbuy.adapters.FundTagsAdapter()
binding.fundTags.adapter = tagsAdapter
binding.fundTags.layoutManager =
androidx.recyclerview.widget.LinearLayoutManager(
context,
androidx.recyclerview.widget.LinearLayoutManager.HORIZONTAL,
false,
)
}
(binding.fundTags.adapter as? com.navi.amc.fundbuy.adapters.FundTagsAdapter)
?.updatePills(fundTags)
binding.fundTags.visibility =
if (fundTags.isNotEmpty()) View.VISIBLE else View.GONE
}
}
binding.companies.isVisible =
@@ -257,6 +285,7 @@ class FundDetailsFragment : AmcBaseFragment(), FooterInteractionListener {
binding.companies.showWhenDataIsAvailable(it)
true
} ?: run { false }
container.apply {
removeAllViews()
val inflater = LayoutInflater.from(context)
@@ -271,6 +300,7 @@ class FundDetailsFragment : AmcBaseFragment(), FooterInteractionListener {
viewModel.chipIdToGraphDataMap[viewModel.selectedChipId]!!
)
} else FundGraphUiState.Loading
val view = FundGraphView(context)
view.setProperties(
data = fundDetailScreenData.content.fundGraphDetails,
@@ -283,9 +313,58 @@ class FundDetailsFragment : AmcBaseFragment(), FooterInteractionListener {
vmSelectedKey = viewModel.selectedChipId,
setRadioAction = ::setSelectedRadio,
vmSelectedRadio = viewModel.selectedRadio,
fundInfoCards = fundDetailScreenData.content.fundInfoCards,
)
view.tag = "graph"
view.setPadding(
view.paddingLeft,
view.paddingTop + dpToPxInInt(10),
view.paddingRight,
view.paddingBottom + dpToPxInInt(10),
)
addView(view)
val showGraphTextView = view.getShowGraphTextView()
val showGraphIconView = view.getShowGraphIconView()
val showGraphContainer = view.getShowGraphContainer()
fundDetailScreenData.content.fundGraphDetails.showGraphTag?.let {
showGraphTag ->
showGraphTextView.setSpannableString(showGraphTag.title)
showGraphTextView.isVisible = true
showGraphIconView.isVisible = true
val shouldShowGraph =
fundDetailScreenData.content.fundGraphDetails.shouldShowGraph ==
true
viewModel.setIsGraphVisible(shouldShowGraph)
val chartComponent = view.findViewById<View>(R.id.chart)
val xAxisLabel = view.findViewById<View>(R.id.x_axis_label)
val clErrorLoader = view.findViewById<View>(R.id.cl_error_loader)
if (!shouldShowGraph) {
chartComponent?.visibility = View.GONE
xAxisLabel?.visibility = View.GONE
clErrorLoader?.visibility = View.GONE
showGraphIconView.showWhenDataIsAvailable(
showGraphTag.unSelectedIconCode
)
}
showGraphContainer.setOnClickListener {
if (viewModel.isGraphVisible.value == true) {
viewModel.setIsGraphVisible(false)
} else {
viewModel.setIsGraphVisible(true)
}
}
}
?: run {
showGraphTextView.isVisible = false
showGraphIconView.isVisible = false
}
}
fundDetailScreenData?.content?.usp?.let {
@@ -326,8 +405,7 @@ class FundDetailsFragment : AmcBaseFragment(), FooterInteractionListener {
fundDetailScreenData?.content?.fundHoldingDetails?.let { data ->
val view = ListItemProgressView(context)
view.setProperties(data) {
val bundle =
Bundle().apply { putString(Constant.DATA, Gson().toJson(data)) }
val bundle = Bundle().apply { putString(DATA, Gson().toJson(data)) }
ListItemProgressBottomSheet.newInstance(bundle).let { bottomSheet ->
safelyShowBottomSheet(
bottomSheet,
@@ -337,21 +415,11 @@ class FundDetailsFragment : AmcBaseFragment(), FooterInteractionListener {
}
addView(view)
}
fundDetailScreenData?.content?.fundManagerDetailData?.let {
val childBinding =
DataBindingUtil.inflate<FundManagerLayoutBinding>(
inflater,
R.layout.fund_manager_layout,
container,
false,
)
childBinding.apply {
title.setSpannableString(it.title)
name.setSpannableString(it.name)
experience.setSpannableString(it.experience)
icon.showWhenDataIsAvailable(it.profile)
}
addView(childBinding.root)
fundDetailScreenData?.content?.fundManagerDetailData?.let { managerData ->
val fundManagerView = FundManagerView(requireContext())
fundManagerView.setProperties(managerData)
addView(fundManagerView)
}
fundDetailScreenData?.content?.documentData?.let {
val childBinding =
@@ -365,6 +433,11 @@ class FundDetailsFragment : AmcBaseFragment(), FooterInteractionListener {
title.setSpannableString(it.title)
subtitle.setSpannableString(it.subtitle)
icon.showWhenDataIsAvailable(it.icon?.iconCode)
subtitleLinearLayout.background =
getNaviDrawable(
backgroundColor = it.bgColor.parseColorSafe("#F5F5F5"),
cornerRadius = dpToPxInInt(4),
)
touch.setOnClickListener { view ->
it.icon?.downloadUrlList?.let { downloadUrlList ->
downloadUrlList.forEach { downloadUrl ->
@@ -393,6 +466,14 @@ class FundDetailsFragment : AmcBaseFragment(), FooterInteractionListener {
view.updateRootViewTopPadding(
Padding(startDp = 0f, topDp = 0f, endDp = 0f, bottomDp = 24f)
)
view
.findViewById<NaviTextView>(R.id.compliance_text)
.setSpannableString(fundDetailScreenData.content.regulatoryInfo?.title)
view
.findViewById<ImageView>(R.id.compliance_icon)
.showWhenDataIsAvailable(
fundDetailScreenData.content.regulatoryInfo?.iconCode
)
addView(view)
}
fundDetailScreenData?.footer?.backCta?.let {
@@ -424,12 +505,66 @@ class FundDetailsFragment : AmcBaseFragment(), FooterInteractionListener {
viewLifecycleOwner.lifecycleScope.launchWhenResumed {
viewModel.fundGraphData.collect { graphUiState -> updateGraph(graphUiState) }
}
viewModel.isGraphVisible.observeNonNull(viewLifecycleOwner) { isVisible ->
val graphView = binding.container.findViewWithTag<FundGraphView>("graph")
graphView?.let { view ->
val chartComponent = view.findViewById<View>(R.id.chart)
val xAxisLabel = view.findViewById<View>(R.id.x_axis_label)
val chipGroup = view.findViewById<View>(R.id.chip_group)
val clErrorLoader = view.findViewById<View>(R.id.cl_error_loader)
val chart =
view.findViewById<com.github.mikephil.charting.charts.LineChart>(R.id.chart)
val marker = chart?.marker as? FundGraphToolTipView
marker?.updateVisibility(isVisible)
if (isVisible) {
chartComponent?.visibility = View.VISIBLE
xAxisLabel?.visibility = View.VISIBLE
clErrorLoader?.visibility = View.GONE
chipGroup?.let { group ->
val params = group.layoutParams as? ConstraintLayout.LayoutParams
params?.let {
it.topMargin = dpToPxInInt(317)
group.layoutParams = it
}
}
} else {
chartComponent?.visibility = View.GONE
xAxisLabel?.visibility = View.GONE
clErrorLoader?.visibility = View.GONE
chipGroup?.let { group ->
val params = group.layoutParams as? ConstraintLayout.LayoutParams
params?.let {
it.topMargin = dpToPxInInt(24)
group.layoutParams = it
}
}
}
val showGraphIconView = view.getShowGraphIconView()
val showGraphTag =
viewModel.fundDetailScreenData.value?.content?.fundGraphDetails?.showGraphTag
if (isVisible) {
showGraphIconView.showWhenDataIsAvailable(showGraphTag?.selectedIconCode)
} else {
showGraphIconView.showWhenDataIsAvailable(showGraphTag?.unSelectedIconCode)
}
}
}
}
private fun updateGraph(graphUiState: FundGraphUiState) {
binding.container
.findViewWithTag<FundGraphView>("graph")
?.setProperties(
val graphView = binding.container.findViewWithTag<FundGraphView>("graph")
val wasGraphVisible = viewModel.isGraphVisible.value ?: true
graphView?.let { view ->
view.setProperties(
data = viewModel.fundDetailScreenData.value?.content?.fundGraphDetails,
graphUiState = graphUiState,
fundInvestmentDetailData =
@@ -441,6 +576,35 @@ class FundDetailsFragment : AmcBaseFragment(), FooterInteractionListener {
setRadioAction = ::setSelectedRadio,
vmSelectedRadio = viewModel.selectedRadio,
)
if (!wasGraphVisible) {
viewModel.setIsGraphVisible(false)
val chartComponent = view.findViewById<View>(R.id.chart)
val xAxisLabel = view.findViewById<View>(R.id.x_axis_label)
val clErrorLoader = view.findViewById<View>(R.id.cl_error_loader)
chartComponent?.visibility = View.GONE
xAxisLabel?.visibility = View.GONE
clErrorLoader?.visibility = View.GONE
val chipGroup = view.findViewById<View>(R.id.chip_group)
chipGroup?.let { group ->
val params = group.layoutParams as? ConstraintLayout.LayoutParams
params?.let {
it.topMargin = dpToPxInInt(24)
group.layoutParams = it
}
}
val showGraphIconView = view.getShowGraphIconView()
val showGraphTag =
viewModel.fundDetailScreenData.value?.content?.fundGraphDetails?.showGraphTag
showGraphTag?.unSelectedIconCode?.let { iconCode ->
showGraphIconView.showWhenDataIsAvailable(iconCode)
}
}
}
}
private fun navigate(action: ActionData) {
@@ -450,7 +614,7 @@ class FundDetailsFragment : AmcBaseFragment(), FooterInteractionListener {
private fun onActionIconClick(actionData: ActionData) {
val url = actionData?.url
sendEvent(actionData?.metaData?.clickedData)
if (url == Constant.SHOW_BOTTOMSHEET) {
if (url == SHOW_BOTTOMSHEET) {
val data = actionData.parameters?.getOrNull(0)?.value
val key = actionData.parameters?.getOrNull(0)?.key
val bundle = Bundle().apply { putString(Constant.DATA, data) }

View File

@@ -7,14 +7,18 @@
package com.navi.amc.fundbuy.models
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
import com.navi.amc.common.model.Footer
import com.navi.amc.common.model.FundInvestmentDetailData
import com.navi.amc.common.model.InformationCardData
import com.navi.common.model.Header
import com.navi.design.textview.model.NaviSpan
import com.navi.design.textview.model.TextWithStyle
import com.navi.naviwidgets.models.FundDetailData
import com.navi.naviwidgets.models.response.amc.TagData
import java.io.Serializable
import kotlinx.parcelize.Parcelize
data class FundDetails(
@SerializedName("header") val header: Header? = null,
@@ -23,7 +27,7 @@ data class FundDetails(
)
data class FundDetailScreenData(
@SerializedName("fundHeader") val amcHeaderData: AmcHeaderData? = null,
@SerializedName("fundHeader") val amcHeaderData: FundDetailsHeaderData? = null,
@SerializedName("fundDetails") val fundDetailData: FundDetailData? = null,
@SerializedName("fundManagerDetails") val fundManagerDetailData: FundManagerData? = null,
@SerializedName("fundHoldingDetails") val fundHoldingDetails: ListItemProgressData? = null,
@@ -36,11 +40,37 @@ data class FundDetailScreenData(
@SerializedName("companiesLogo") val companiesLogo: String? = null,
@SerializedName("usp") val usp: FundUspData? = null,
@SerializedName("disclaimerData") val disclaimerData: InformationCardData? = null,
@SerializedName("fundInfoCards") val fundInfoCards: FundInfoCardsData? = null,
@SerializedName("fundInvestmentDetails")
val fundInvestmentDetails: FundInvestmentDetailData? = null,
@SerializedName("regulatoryInfo") val regulatoryInfo: RegulatoryInfoData? = null,
)
@Parcelize
data class FundDetailsHeaderData(
@SerializedName("title") val title: TextWithStyle? = null,
@SerializedName("subtitle", alternate = ["subTitle"]) val subtitle: TextWithStyle? = null,
@SerializedName("filter") val filter: IconData? = null,
@SerializedName("label") val label: LabelData? = null,
@SerializedName("icon") val icon: ActionIcon? = null,
@SerializedName("bgColor") val bgColor: String? = null,
@SerializedName("iconSize") val iconSize: Double? = null,
@SerializedName("fundTags") val fundTags: List<FundTagData>? = null,
) : Parcelable
data class FundUspData(
@SerializedName("title") val title: TextWithStyle? = null,
@SerializedName("items") val items: List<SubItemData>? = null,
)
@Parcelize
data class FundTagData(
@SerializedName("bgColor") val bgColor: String? = null,
@SerializedName("text") var text: String? = null,
@SerializedName("span") val span: List<NaviSpan>? = null,
) : Serializable, Parcelable
data class RegulatoryInfoData(
@SerializedName("title") val title: TextWithStyle? = null,
@SerializedName("iconCode") val iconCode: String? = null,
)

View File

@@ -16,6 +16,8 @@ data class FundGraphDetails(
@SerializedName("items") val items: List<FundGraphData>? = null,
@SerializedName("fundDuration") val fundDuration: List<FundDuration>? = null,
@SerializedName("graphBgColor") val graphBgColor: String? = null,
@SerializedName("showGraphTag") val showGraphTag: ShowGraphTagData? = null,
@SerializedName("shouldShowGraph") val shouldShowGraph: Boolean? = null,
)
data class FundGraphData(
@@ -80,3 +82,9 @@ data class XAxisLabelData(
@SerializedName("mid") val mid: String? = null,
@SerializedName("end") val end: String? = null,
)
data class ShowGraphTagData(
@SerializedName("title") val title: TextWithStyle? = null,
@SerializedName("selectedIconCode") val selectedIconCode: String? = null,
@SerializedName("unSelectedIconCode") val unSelectedIconCode: String? = null,
)

View File

@@ -0,0 +1,20 @@
/*
*
* * Copyright © 2025 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.amc.fundbuy.models
import com.google.gson.annotations.SerializedName
import com.navi.design.textview.model.TextWithStyle
data class FundInfoCardsData(@SerializedName("items") val items: List<FundInfoCardItem>? = null)
data class FundInfoCardItem(
@SerializedName("title") val title: TextWithStyle? = null,
@SerializedName("subtitle") val subtitle: TextWithStyle? = null,
@SerializedName("topTag") val topTag: String? = null,
@SerializedName("bgColor") val bgColor: String? = null,
)

View File

@@ -15,4 +15,8 @@ data class FundManagerData(
@SerializedName("name") val name: TextWithStyle? = null,
@SerializedName("experience") val experience: TextWithStyle? = null,
@SerializedName("profile") val profile: String? = null,
@SerializedName("isExpanded") val isExpanded: Boolean? = null,
@SerializedName("rightChevronDown") val rightChevronDown: String? = null,
@SerializedName("rightChevronUp") val rightChevronUp: String? = null,
@SerializedName("assetsDetails") val assetDetails: TextWithStyle? = null,
)

View File

@@ -18,6 +18,9 @@ data class ListItemProgressData(
@SerializedName("listData") val listData: List<ListData>? = null,
@SerializedName("displayCount") val displayCount: Int? = null,
@SerializedName("action") val action: ActionData? = null,
@SerializedName("rightChevronDown") val rightChevronDown: String? = null,
@SerializedName("rightChevronUp") val rightChevronUp: String? = null,
@SerializedName("isExpanded") val isExpanded: Boolean? = null,
)
data class ListData(

View File

@@ -52,6 +52,14 @@ class FundDetailViewModel @Inject constructor(private val repository: FundDetail
var fundName: String? = null
var selectedRadio: String? = null
private val _isGraphVisible = MutableLiveData(true)
val isGraphVisible: LiveData<Boolean>
get() = _isGraphVisible
fun setIsGraphVisible(isVisible: Boolean) {
_isGraphVisible.value = isVisible
}
fun getFundScreenData(isin: String, source: String?, screenName: String) {
viewModelScope.launch {
val response =

View File

@@ -7,6 +7,7 @@
package com.navi.amc.fundbuy.views
import android.animation.LayoutTransition
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
@@ -18,6 +19,8 @@ import com.navi.amc.R
import com.navi.amc.common.model.InformationCardData
import com.navi.amc.databinding.FundDetailItemLayoutBinding
import com.navi.amc.databinding.FundDetailViewLayoutBinding
import com.navi.amc.utils.toggleContentVisibility
import com.navi.amc.utils.updateChevronImage
import com.navi.base.model.ActionData
import com.navi.base.utils.orZero
import com.navi.design.utils.setSpannableString
@@ -27,11 +30,37 @@ import com.navi.naviwidgets.models.FundDetailData
class FundDetailView(context: Context, attributeSet: AttributeSet? = null) :
LinearLayout(context, attributeSet) {
companion object {
private const val TAG = "FundDetailView"
}
private val binding: FundDetailViewLayoutBinding
private var isExpanded: Boolean = false
init {
val inflater = LayoutInflater.from(context)
binding = DataBindingUtil.inflate(inflater, R.layout.fund_detail_view_layout, this, true)
this.layoutTransition =
LayoutTransition().apply { setDuration(LayoutTransition.CHANGING, 300) }
}
private fun setupAccordion(data: FundDetailData) {
if (data.isExpanded != null) {
binding.titleBar.setOnClickListener {
isExpanded = !isExpanded
toggleContentVisibility(
isExpanded = isExpanded,
content = binding.itemContainer,
chevronImage = binding.chevronImage,
)
}
updateChevronImage(
isExpanded = isExpanded,
chevronImage = binding.chevronImage,
chevronDown = data.rightChevronDown,
)
}
}
fun setProperties(
@@ -40,8 +69,12 @@ class FundDetailView(context: Context, attributeSet: AttributeSet? = null) :
action: ((ActionData) -> Unit)? = null,
iconClickAction: ((ActionData) -> Unit)? = null,
) {
isExpanded = data.isExpanded ?: true
binding.title.setSpannableString(data.title)
setupAccordion(data)
binding.itemContainer.apply {
removeAllViews()
data.items?.forEachIndexed { index, itemData ->
val inflater = LayoutInflater.from(context)
val childBinding: FundDetailItemLayoutBinding =
@@ -78,5 +111,7 @@ class FundDetailView(context: Context, attributeSet: AttributeSet? = null) :
addView(childBinding.root)
}
}
binding.itemContainer.visibility = if (isExpanded) View.VISIBLE else View.GONE
}
}

View File

@@ -8,109 +8,244 @@
package com.navi.amc.fundbuy.views
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.TextView
import androidx.core.view.isVisible
import com.github.mikephil.charting.components.MarkerView
import com.github.mikephil.charting.data.Entry
import com.github.mikephil.charting.highlight.Highlight
import com.github.mikephil.charting.utils.MPPointF
import com.navi.amc.R
import com.navi.amc.databinding.FundGraphToolTipDetailsLayoutBinding
import com.navi.amc.utils.Constant.RUPEE_SYMBOL
import com.navi.design.textview.model.NaviSpan
import com.navi.design.utils.doAnimate
import com.navi.design.utils.dpToPx
import com.navi.design.utils.spannedText
import com.navi.naviwidgets.R
import timber.log.Timber
class FundGraphToolTipView(context: Context, layout: Int) : MarkerView(context, layout) {
private var uiScreenWidth = 0
private var title: TextView? = null
private var subtitle: TextView? = null
private var circleIndicatorView: ImageView? = null
private var pointerCircle: ImageView? = null
private var tooltipDetailsView: View? = null
private var tooltipBinding: FundGraphToolTipDetailsLayoutBinding? = null
private var verticalLineView: View? = null
private var duplicatePointerCircle: ImageView? = null
private val TOP_GAP = dpToPx(4)
private val BOTTOM_GAP = dpToPx(24)
private var chartParent: ViewGroup? = null
init {
title = findViewById(R.id.title)
subtitle = findViewById(R.id.subtitle)
circleIndicatorView = findViewById(R.id.scroll_indicator)
uiScreenWidth = resources.displayMetrics.widthPixels
pointerCircle = findViewById(R.id.pointer_circle)
tooltipBinding = FundGraphToolTipDetailsLayoutBinding.inflate(LayoutInflater.from(context))
tooltipDetailsView = tooltipBinding?.root
tooltipDetailsView?.visibility = View.INVISIBLE
}
private fun setupTooltipDetailsView() {
if (tooltipDetailsView?.parent == null && chartView != null) {
chartParent = chartView?.parent as? ViewGroup
chartParent?.let { parent ->
if (verticalLineView == null) {
val lineInflater = LayoutInflater.from(context)
verticalLineView =
lineInflater.inflate(R.layout.vertical_dotted_line_layout, parent, false)
val lineLayoutParams =
FrameLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
chartView?.height ?: ViewGroup.LayoutParams.MATCH_PARENT,
)
parent.addView(verticalLineView, lineLayoutParams)
}
if (duplicatePointerCircle == null) {
duplicatePointerCircle =
ImageView(context).apply {
layoutParams =
FrameLayout.LayoutParams(dpToPx(14).toInt(), dpToPx(14).toInt())
setImageResource(com.navi.naviwidgets.R.drawable.black_border_circle)
elevation = dpToPx(4).toFloat()
visibility = View.INVISIBLE
}
parent.addView(duplicatePointerCircle)
}
val layoutParams =
FrameLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
)
parent.addView(tooltipDetailsView, layoutParams)
}
}
}
override fun refreshContent(e: Entry?, highlight: Highlight?) {
try {
var nav = RUPEE_SYMBOL.plus(e?.y.toString())
val date = e?.data as? String
val titleText = "NAV: $nav"
title?.text =
titleText.spannedText(
setupTooltipDetailsView()
val nav = RUPEE_SYMBOL + (e?.y?.toString() ?: "0.0")
val date = e?.data as? String
val titleText = "NAV: $nav"
tooltipBinding?.title?.text =
titleText.spannedText(
context = context,
span =
listOf(
NaviSpan(
startSpan = 0,
endSpan = 4,
fontName = "NAVI_BODY_REGULAR",
fontSize = 12.0,
spanColor = "#6B6B6B",
),
NaviSpan(
startSpan = 4,
endSpan = titleText.length,
fontName = "NAVI_BODY_DEMI_BOLD",
fontSize = 12.0,
spanColor = "#191919",
),
),
)
date?.let {
tooltipBinding?.subtitle?.isVisible = true
val subtitleText = "$date"
tooltipBinding?.subtitle?.text =
subtitleText.spannedText(
context = context,
span =
listOf(
NaviSpan(
startSpan = 0,
endSpan = 4,
endSpan = subtitleText.length,
fontName = "NAVI_BODY_REGULAR",
fontSize = 12.0,
spanColor = "#6B6B6B",
),
NaviSpan(
startSpan = 4,
endSpan = 25,
fontName = "NAVI_BODY_DEMI_BOLD",
fontSize = 12.0,
spanColor = "#191919",
),
)
),
)
date?.let {
subtitle?.isVisible = true
val subtitleText = "on $date"
subtitle?.text =
subtitleText.spannedText(
context = context,
span =
listOf(
NaviSpan(
startSpan = 0,
endSpan = 30,
fontName = "NAVI_BODY_REGULAR",
fontSize = 12.0,
spanColor = "#191919",
)
),
)
}
} catch (e: Exception) {
Timber.e(e)
}
} ?: run { tooltipBinding?.subtitle?.isVisible = false }
tooltipDetailsView?.visibility = View.VISIBLE
super.refreshContent(e, highlight)
}
override fun getOffsetForDrawingAtPoint(posX: Float, posY: Float): MPPointF {
var offsetX = 0f
val leftBound = posX - (width.toFloat() / 2)
val rightBound = posX + (width.toFloat() / 2)
val indicatorHeight = circleIndicatorView?.measuredHeight?.toFloat()
val indicatorWidth = circleIndicatorView?.measuredWidth?.toFloat()
val indicatorOffset = if (indicatorHeight != null) indicatorHeight / 2 else 0f
val pointerOffsetX = -(pointerCircle?.width?.div(2) ?: 0).toFloat()
val pointerOffsetY = -(pointerCircle?.height?.div(2) ?: 0).toFloat()
if (leftBound <= 0f) {
offsetX = -dpToPx(6)
if (indicatorWidth != null) {
circleIndicatorView?.doAnimate(translationX = -(width.toFloat() / 2 - dpToPx(6)))
offset.x = pointerOffsetX
offset.y = pointerOffsetY
pointerCircle?.visibility = View.INVISIBLE
tooltipDetailsView?.let { tooltipView ->
val tooltipWidth = tooltipView.width.toFloat()
var tooltipX = chartView.left + posX - (tooltipWidth / 2)
val actualDataPointX = chartView.left + posX
val actualDataPointY = chartView.top + posY
if (tooltipX < chartView.left + dpToPx(16)) {
tooltipX = chartView.left.toFloat() + dpToPx(16)
} else if (tooltipX + tooltipWidth > chartView.right - dpToPx(16)) {
tooltipX = chartView.right.toFloat() - tooltipWidth - dpToPx(16)
}
} else if (rightBound > chartView.measuredWidth) {
offsetX = -(width.toFloat() - dpToPx(6))
if (indicatorWidth != null) {
circleIndicatorView?.doAnimate(translationX = width.toFloat() / 2 - dpToPx(6))
duplicatePointerCircle?.let { duplicateCircle ->
duplicateCircle.x = actualDataPointX - (duplicateCircle.width / 2)
duplicateCircle.y = actualDataPointY - (duplicateCircle.height / 2)
duplicateCircle.visibility = View.VISIBLE
}
} else {
offsetX = (-(width / 2)).toFloat()
circleIndicatorView?.doAnimate(translationX = 0f)
verticalLineView?.let { line ->
val legendHeight = dpToPx(20)
val chartAreaHeight = chartView.height - legendHeight
val layoutParams = line.layoutParams
layoutParams.height = chartAreaHeight.toInt()
line.x = actualDataPointX - (line.width / 2)
line.y = chartView.top.toFloat()
line.layoutParams = layoutParams
line.visibility = tooltipDetailsView?.visibility ?: View.INVISIBLE
}
val tooltipHeight = tooltipView.height.toFloat()
val upperPosition = chartView.top.toFloat() + TOP_GAP
val lowerPosition =
(chartView.top + chartView.height).toFloat() - tooltipHeight - BOTTOM_GAP
val upperTooltipBottom = upperPosition + tooltipHeight
val pointerY = chartView.top + posY
val yPosition = if (pointerY < upperTooltipBottom) lowerPosition else upperPosition
tooltipView.x = tooltipX
tooltipView.y = yPosition
tooltipView.visibility = View.VISIBLE
verticalLineView?.visibility = View.VISIBLE
return offset
}
offset.x = offsetX
offset.y = -(height.toFloat() - indicatorOffset - dpToPx(4))
return offset
}
fun hideTooltip() {
tooltipDetailsView?.visibility = View.INVISIBLE
verticalLineView?.visibility = View.INVISIBLE
duplicatePointerCircle?.visibility = View.INVISIBLE
pointerCircle?.visibility = View.VISIBLE
}
fun updateVisibility(visible: Boolean) {
if (!visible) {
hideTooltip()
val parent = tooltipDetailsView?.parent as? ViewGroup
parent?.removeView(tooltipDetailsView)
parent?.removeView(verticalLineView)
parent?.removeView(duplicatePointerCircle)
verticalLineView = null
duplicatePointerCircle = null
chartView?.highlightValues(null)
} else {
if (tooltipDetailsView?.parent == null && chartView != null) {
setupTooltipDetailsView()
}
hideTooltip()
}
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
cleanupTooltip()
}
private fun cleanupTooltip() {
val parent = tooltipDetailsView?.parent as? ViewGroup
parent?.removeView(tooltipDetailsView)
parent?.removeView(verticalLineView)
parent?.removeView(duplicatePointerCircle)
verticalLineView = null
duplicatePointerCircle = null
tooltipDetailsView = null
tooltipBinding = null
chartParent = null
}
}

View File

@@ -10,6 +10,7 @@ package com.navi.amc.fundbuy.views
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.drawable.GradientDrawable
import android.text.TextUtils
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
@@ -20,9 +21,11 @@ import android.view.ViewTreeObserver
import android.widget.ImageView
import android.widget.LinearLayout
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.graphics.toColorInt
import androidx.core.view.ViewCompat
import androidx.core.view.children
import androidx.core.view.forEach
import androidx.core.view.isNotEmpty
import androidx.core.view.isVisible
import androidx.databinding.DataBindingUtil
import androidx.transition.Fade
@@ -42,6 +45,7 @@ import com.navi.amc.databinding.FundGraphLayoutBinding
import com.navi.amc.fundbuy.models.FundDuration
import com.navi.amc.fundbuy.models.FundGraphData
import com.navi.amc.fundbuy.models.FundGraphDetails
import com.navi.amc.fundbuy.models.FundInfoCardsData
import com.navi.amc.fundbuy.models.FundReturn
import com.navi.amc.fundbuy.models.ReturnsData
import com.navi.amc.utils.ColorUtils
@@ -53,6 +57,7 @@ import com.navi.base.model.ActionData
import com.navi.base.utils.orFalse
import com.navi.base.utils.orZero
import com.navi.common.animation.fadeIn
import com.navi.design.textview.NaviTextView
import com.navi.design.textview.model.TextWithStyle
import com.navi.design.utils.CornerRadius
import com.navi.design.utils.dpToPxInInt
@@ -78,12 +83,22 @@ class FundGraphView(context: Context, attributeSet: AttributeSet? = null) :
private var setRadioAction: ((String) -> Unit)? = null
private var fundDurationData: FundDuration? = null
private var selectedId: Int = 0
private var hasDisplayedFundInfoCards = false
private val toolTipMarkerView: FundGraphToolTipView by lazy {
FundGraphToolTipView(context = context, R.layout.fund_graph_tool_tip_view_layout)
}
init {
val inflater = LayoutInflater.from(context)
binding = DataBindingUtil.inflate(inflater, R.layout.fund_graph_layout, this, true)
layoutParams = LayoutParams(MATCH_PARENT, WRAP_CONTENT)
tag = "graph"
binding.showGraphText.tag = "showGraphText"
binding.showGraphIcon.tag = "showGraphIcon"
binding.showGraphContainer.tag = "showGraphContainer"
binding.chipTooltipContent.radioGroup.setOnCheckedChangeListener { group, checkedId ->
when (checkedId) {
R.id.radio_option_one_time -> {
@@ -107,6 +122,7 @@ class FundGraphView(context: Context, attributeSet: AttributeSet? = null) :
vmSelectedKey: String? = null,
setRadioAction: ((String) -> Unit)? = null,
vmSelectedRadio: String? = null,
fundInfoCards: FundInfoCardsData? = null,
) {
this.apiClickAction = apiClickAction
this.setRadioAction = setRadioAction
@@ -115,6 +131,8 @@ class FundGraphView(context: Context, attributeSet: AttributeSet? = null) :
setFundGraphPillsData(data, vmSelectedKey)
}
setFundInfoCardsData(fundInfoCards)
when (graphUiState) {
is FundGraphUiState.Loading -> {
showLoader()
@@ -167,6 +185,92 @@ class FundGraphView(context: Context, attributeSet: AttributeSet? = null) :
}
}
private fun setFundInfoCardsData(fundInfoCards: FundInfoCardsData?) {
val cardsContainer = binding.fundInfoCards
if (fundInfoCards == null || fundInfoCards.items.isNullOrEmpty()) {
if (!hasDisplayedFundInfoCards) {
cardsContainer.visibility = View.GONE
}
return
}
if (hasDisplayedFundInfoCards && cardsContainer.isVisible && cardsContainer.isNotEmpty()) {
return
}
cardsContainer.visibility = View.VISIBLE
cardsContainer.removeAllViews()
val cardWeight = 1.0f / fundInfoCards.items.size
fundInfoCards.items.forEachIndexed { index, cardItem ->
val inflater = LayoutInflater.from(context)
val cardView =
inflater.inflate(R.layout.fund_info_card_item_layout, cardsContainer, false)
val layoutParams =
LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT).apply {
weight = cardWeight
val marginDp = dpToPxInInt(2)
marginStart = marginDp
marginEnd = marginDp
}
cardView.layoutParams = layoutParams
val title = cardView.findViewById<NaviTextView>(R.id.title)
val subtitle = cardView.findViewById<NaviTextView>(R.id.subtitle)
val infoTag = cardView.findViewById<ImageView>(R.id.info_tag)
cardItem.bgColor?.let { colorString ->
cardView.setBackgroundColor(colorString.parseColorSafe())
}
cardItem.title?.let { title.setSpannableString(it) }
title.maxLines = 1
title.ellipsize = TextUtils.TruncateAt.END
cardItem.subtitle?.let { subtitle.setSpannableString(it) }
cardItem.topTag?.let { infoTag.showWhenDataIsAvailable(it) }
if (index == 0) {
cardView.background =
getNaviDrawable(
radii =
CornerRadius(
leftTop = dpToPxInInt(4).toFloat(),
rightTop = 0f,
leftBottom = dpToPxInInt(4).toFloat(),
rightBottom = 0f,
),
backgroundColor = (cardItem.bgColor ?: COLOR_WHITE).toColorInt(),
)
}
if (index == fundInfoCards.items.size - 1) {
cardView.background =
getNaviDrawable(
radii =
CornerRadius(
leftTop = 0f,
rightTop = dpToPxInInt(4).toFloat(),
leftBottom = 0f,
rightBottom = dpToPxInInt(4).toFloat(),
),
backgroundColor = (cardItem.bgColor ?: COLOR_WHITE).toColorInt(),
)
}
cardsContainer.addView(cardView)
}
cardsContainer.visibility = View.VISIBLE
hasDisplayedFundInfoCards = true
}
private fun setFundGraphPillsData(data: FundGraphDetails?, vmSelectedKey: String?) {
isGraphPillUpdated = true
chipIdToDataMap.clear()
@@ -330,8 +434,6 @@ class FundGraphView(context: Context, attributeSet: AttributeSet? = null) :
fillDrawable = gradientDrawable
}
val toolTipMarkerView =
FundGraphToolTipView(context = context, R.layout.fund_graph_tool_tip_view_layout)
binding.chart.setOnChartValueSelectedListener(
object : OnChartValueSelectedListener {
override fun onValueSelected(e: Entry, h: Highlight?) {
@@ -341,7 +443,9 @@ class FundGraphView(context: Context, attributeSet: AttributeSet? = null) :
}
}
override fun onNothingSelected() {}
override fun onNothingSelected() {
toolTipMarkerView.hideTooltip()
}
}
)
val lineData = LineData(graphInitialize)
@@ -417,6 +521,14 @@ class FundGraphView(context: Context, attributeSet: AttributeSet? = null) :
}
private fun updateSelectedChips(group: ChipGroup, checkedIds: List<Int>) {
toolTipMarkerView.hideTooltip()
binding.chart.highlightValues(null)
if (binding.chart.data != null && binding.chart.data.entryCount > 0) {
binding.chart.marker = null
binding.chart.marker = toolTipMarkerView
}
for (childView in group.children) {
if (childView is Chip) {
val childId = childView.id
@@ -447,6 +559,18 @@ class FundGraphView(context: Context, attributeSet: AttributeSet? = null) :
return binding.fundReturnDetails
}
fun getShowGraphTextView(): NaviTextView {
return binding.showGraphText
}
fun getShowGraphIconView(): ImageView {
return binding.showGraphIcon
}
fun getShowGraphContainer(): LinearLayout {
return binding.showGraphContainer
}
private fun updateGraph(key: String) {
selectedKey = key
apiClickAction?.invoke(key, false)

View File

@@ -0,0 +1,78 @@
/*
*
* * Copyright © 2024-2025 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.amc.fundbuy.views
import android.animation.LayoutTransition
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.databinding.DataBindingUtil
import com.navi.amc.R
import com.navi.amc.databinding.FundManagerLayoutBinding
import com.navi.amc.fundbuy.models.FundManagerData
import com.navi.amc.utils.toggleContentVisibility
import com.navi.amc.utils.updateChevronImage
import com.navi.design.utils.setSpannableString
import com.navi.naviwidgets.extensions.showWhenDataIsAvailable
class FundManagerView(context: Context, attributeSet: AttributeSet? = null) :
ConstraintLayout(context, attributeSet) {
companion object {
private const val TAG = "FundManagerView"
}
private val binding: FundManagerLayoutBinding
private var isExpanded: Boolean = true
init {
val inflater = LayoutInflater.from(context)
binding = DataBindingUtil.inflate(inflater, R.layout.fund_manager_layout, this, true)
this.layoutTransition =
LayoutTransition().apply { setDuration(LayoutTransition.CHANGING, 300) }
}
private fun setupAccordion(data: FundManagerData) {
if (data.isExpanded != null) {
binding.titleContainer.setOnClickListener {
isExpanded = !isExpanded
toggleContentVisibility(
isExpanded = isExpanded,
content = binding.contentContainer,
chevronImage = binding.chevron,
)
}
updateChevronImage(
isExpanded = isExpanded,
chevronImage = binding.chevron,
chevronDown = data.rightChevronDown,
)
}
}
fun setProperties(data: FundManagerData) {
isExpanded = data.isExpanded ?: true
binding.apply {
title.setSpannableString(data.title)
name.setSpannableString(data.name)
experience.setSpannableString(data.experience)
icon.showWhenDataIsAvailable(data.profile)
assetDetails.setSpannableString(data.assetDetails)
contentContainer.visibility = if (isExpanded) View.VISIBLE else View.GONE
setupAccordion(data)
}
}
}

View File

@@ -7,10 +7,11 @@
package com.navi.amc.fundbuy.views
import android.animation.LayoutTransition
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.ViewGroup
import android.view.View
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.isVisible
import androidx.databinding.DataBindingUtil
@@ -18,6 +19,8 @@ import com.navi.amc.R
import com.navi.amc.databinding.ListItemProgressBinding
import com.navi.amc.fundbuy.adapters.ListItemProgressAdapter
import com.navi.amc.fundbuy.models.ListItemProgressData
import com.navi.amc.utils.toggleContentVisibility
import com.navi.amc.utils.updateChevronImage
import com.navi.base.utils.orZero
import com.navi.design.R as DesignR
import com.navi.design.utils.getNaviDrawable
@@ -26,32 +29,61 @@ import com.navi.design.utils.setSpannableString
class ListItemProgressView(context: Context, attributeSet: AttributeSet? = null) :
ConstraintLayout(context, attributeSet) {
companion object {
private const val TAG = "ListItemProgressView"
}
private val binding: ListItemProgressBinding
private val adapter = ListItemProgressAdapter()
var expandState: Boolean = false
private var expandListener: ((Boolean) -> Unit)? = null
private var isExpanded: Boolean = true
init {
val inflater = LayoutInflater.from(context)
binding = DataBindingUtil.inflate(inflater, R.layout.list_item_progress, this, true)
layoutParams =
ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
this.layoutTransition =
LayoutTransition().apply { setDuration(LayoutTransition.CHANGING, 300) }
}
private fun setupAccordion(data: ListItemProgressData) {
if (data.isExpanded != null) {
binding.titleContainer.setOnClickListener {
isExpanded = !isExpanded
toggleContentVisibility(
isExpanded = isExpanded,
content = binding.contentContainer,
chevronImage = binding.chevron,
)
}
updateChevronImage(
isExpanded = isExpanded,
chevronImage = binding.chevron,
chevronDown = data.rightChevronDown,
)
}
}
fun setProperties(data: ListItemProgressData, onClick: () -> Unit) {
isExpanded = data.isExpanded ?: true
binding.apply {
title.setSpannableString(data.title)
leftTitle.setSpannableString(data.leftHeader)
rightTitle.setSpannableString(data.rightHeader)
setupAccordion(data)
items.adapter =
adapter.apply {
data.listData?.let {
data.displayCount?.let { count -> update(it.take(count)) }
}
}
contentContainer.visibility = if (isExpanded) View.VISIBLE else View.GONE
button.root.isVisible =
if (data.listData?.size.orZero() > 3) {
button.apply {
@@ -63,10 +95,7 @@ class ListItemProgressView(context: Context, attributeSet: AttributeSet? = null)
cornerRadius = resources.getDimension(DesignR.dimen.dp_4).toInt(),
)
}
button.root.setOnClickListener {
onClick.invoke()
expandListener?.invoke(expandState)
}
button.root.setOnClickListener { onClick.invoke() }
true
} else {
false

View File

@@ -0,0 +1,77 @@
/*
*
* * Copyright © 2025 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.amc.utils
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import com.navi.naviwidgets.extensions.showWhenDataIsAvailable
fun expand(view: View) {
view.measure(
View.MeasureSpec.makeMeasureSpec((view.parent as View).width, View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
)
val targetHeight = view.measuredHeight
view.layoutParams.height = 0
view.visibility = View.VISIBLE
val animator = ValueAnimator.ofInt(0, targetHeight)
animator.addUpdateListener { valueAnimator ->
val value = valueAnimator.animatedValue as Int
view.layoutParams.height = value
view.requestLayout()
}
animator.duration = 300
animator.addListener(
object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
view.layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT
}
}
)
animator.start()
}
fun collapse(view: View) {
val initialHeight = view.measuredHeight
val animator = ValueAnimator.ofInt(initialHeight, 0)
animator.addUpdateListener { valueAnimator ->
val value = valueAnimator.animatedValue as Int
view.layoutParams.height = value
view.requestLayout()
}
animator.duration = 300
animator.addListener(
object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
view.visibility = View.GONE
}
}
)
animator.start()
}
fun toggleContentVisibility(isExpanded: Boolean, content: View, chevronImage: ImageView) {
if (isExpanded) {
expand(content)
} else {
collapse(content)
}
chevronImage.animate().rotation(if (isExpanded) 180f else 0f).setDuration(300).start()
}
fun updateChevronImage(isExpanded: Boolean, chevronImage: ImageView, chevronDown: String? = null) {
chevronImage.showWhenDataIsAvailable(chevronDown)
chevronImage.visibility = View.VISIBLE
chevronImage.rotation = if (isExpanded) 180f else 0f
}

View File

@@ -24,7 +24,7 @@ object ColorUtils {
const val KEY_COLOR_NEGATIVE = "negative"
const val DEFAULT_COLOR_VARIATION_SELECTED = "#22A940"
const val DEFAULT_COLOR_VARIATION_UNSELECTED = "#E3E5E5"
const val FUND_GRAPH_BG_START_GRADIENT_COLOR = "#64FFFCEC"
const val FUND_GRAPH_BG_START_GRADIENT_COLOR = "#F9F9FA"
const val FUND_GRAPH_BG_END_GRADIENT_COLOR = "#FFFFFF"
const val FUND_GRAPH_CHIP_SELECTED_COLOR = "#1F002A"

View File

@@ -1,75 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<!-- Drop Shadow Stack -->
<item>
<shape>
<padding
android:bottom="2dp"
android:left="1dp"
android:right="1dp" />
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/white" />
<solid android:color="#00CCCCCC" />
<corners android:radius="4dp" />
</shape>
</item>
<item>
<shape>
<padding
android:bottom="2dp"
android:left="1dp"
android:right="1dp" />
<solid android:color="#10CCCCCC" />
<corners android:radius="4dp" />
</shape>
</item>
<item>
<shape>
<padding
android:bottom="2dp"
android:left="1dp"
android:right="1dp" />
<solid android:color="#20CCCCCC" />
<corners android:radius="4dp" />
</shape>
</item>
<item>
<shape>
<padding
android:bottom="2dp"
android:left="1dp"
android:right="1dp" />
<solid android:color="#30CCCCCC" />
<corners android:radius="4dp" />
</shape>
</item>
<item>
<shape>
<padding
android:bottom="2dp"
android:left="1dp"
android:right="1dp" />
<solid android:color="#50CCCCCC" />
<corners android:radius="4dp" />
</shape>
</item>
<!-- Background -->
<item>
<shape>
<solid android:color="#F5F5F5" />
<size
android:width="117dp"
android:height="48dp" />
<corners android:radius="4dp" />
</shape>
</item>
</layer-list>
<corners android:radius="@dimen/_4dp" />
<stroke
android:width="@dimen/dp_1"
android:color="@color/border_grey_color" />
</shape>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#E8F5EB" />
<corners android:radius="4dp" />
</shape>

View File

@@ -7,8 +7,7 @@
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="@dimen/dp_16"
android:paddingEnd="@dimen/dp_16">
android:padding="@dimen/_16dp">
<com.navi.design.textview.NaviTextView
android:id="@+id/title"
@@ -18,31 +17,45 @@
app:layout_constraintTop_toTopOf="parent"
tools:text="Scheme Information and KIM" />
<com.navi.design.textview.NaviTextView
android:id="@+id/subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_16"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/title"
tools:text="Download documents" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/subtitle_linear_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="@dimen/dp_16"
android:paddingVertical="@dimen/_12dp"
android:paddingHorizontal="@dimen/_16dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/title">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/icon"
android:layout_width="@dimen/dp_16"
android:layout_height="@dimen/dp_16"
android:layout_marginStart="@dimen/dp_8"
app:layout_constraintBottom_toBottomOf="@id/subtitle"
app:layout_constraintStart_toEndOf="@id/subtitle"
app:layout_constraintTop_toTopOf="@id/subtitle" />
<com.navi.design.textview.NaviTextView
android:id="@+id/subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
tools:text="Download documents" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/icon"
android:layout_width="@dimen/dp_16"
android:layout_height="@dimen/dp_16"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<View
android:id="@+id/touch"
android:layout_width="@dimen/dp_0"
android:layout_height="@dimen/dp_48"
app:layout_constraintBottom_toBottomOf="@id/subtitle"
app:layout_constraintEnd_toEndOf="@id/icon"
app:layout_constraintStart_toStartOf="@id/subtitle"
app:layout_constraintTop_toTopOf="@id/subtitle" />
app:layout_constraintBottom_toBottomOf="@id/subtitle_linear_layout"
app:layout_constraintEnd_toEndOf="@id/subtitle_linear_layout"
app:layout_constraintStart_toStartOf="@id/subtitle_linear_layout"
app:layout_constraintTop_toTopOf="@id/subtitle_linear_layout" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -47,11 +47,24 @@
app:layout_goneMarginTop="@dimen/dp_0"
app:layout_constraintTop_toBottomOf="@id/tag_container"/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/fund_tags"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/header"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:paddingHorizontal="@dimen/_16dp"
android:orientation="horizontal"
android:overScrollMode="never"
android:visibility="gone" />
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/companies"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/header" />
android:layout_marginTop="@dimen/_16dp"
app:layout_constraintTop_toBottomOf="@id/fund_tags" />
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/container"
@@ -59,8 +72,8 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="@dimen/dp_16"
android:layout_marginTop="@dimen/dp_16"
app:divider="@drawable/divider_margin_32"
android:paddingTop="@dimen/_16dp"
app:divider="@drawable/divider_margin_4"
app:showDividers="middle"
app:layout_constraintTop_toBottomOf="@id/companies" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -9,20 +9,48 @@
android:orientation="vertical"
android:paddingHorizontal="@dimen/dp_16">
<com.navi.design.textview.NaviTextView
android:id="@+id/title"
android:layout_width="wrap_content"
<!-- Title bar with chevron for accordion functionality -->
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/title_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:lineSpacingExtra="@dimen/dp_4"
tools:text="Fund Overview" />
android:background="@color/white"
android:clickable="true"
android:focusable="true"
android:paddingVertical="@dimen/_20dp"
android:minHeight="@dimen/_24dp">
<com.navi.design.textview.NaviTextView
android:id="@+id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="start|center_vertical"
android:lineSpacingExtra="@dimen/dp_4"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/chevron_image"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Fund Overview" />
<ImageView
android:id="@+id/chevron_image"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="end|center_vertical"
android:contentDescription="Expand or collapse"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<!-- Content container that will be shown/hidden -->
<LinearLayout
android:id="@+id/item_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:divider="@drawable/divider_32"
android:layout_marginTop="@dimen/dp_16"
android:paddingBottom="@dimen/_20dp"
android:showDividers="middle" />
</LinearLayout>

View File

@@ -30,6 +30,31 @@
app:layout_constraintBottom_toBottomOf="@id/title"
tools:text="+ 200.75%" />
<LinearLayout
android:id="@+id/showGraphContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/title"
app:layout_constraintBottom_toBottomOf="@id/title">
<com.navi.design.textview.NaviTextView
android:id="@+id/showGraphText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/dp_6"
tools:text="Show graph" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/showGraphIcon"
android:layout_width="@dimen/_16dp"
android:layout_height="@dimen/_16dp"
android:src="@drawable/ic_alert_error_red"
/>
</LinearLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/fund_return_details"
android:layout_width="match_parent"
@@ -121,7 +146,7 @@
app:layout_constraintBottom_toTopOf="@id/chip_group"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:background="#FFFCEC"
android:background="#F9F9FA"
android:visibility="gone"
tools:visibility="visible">
@@ -168,7 +193,8 @@
android:layout_height="wrap_content"
android:translationZ="@dimen/dp_8"
android:gravity="center_horizontal"
android:paddingTop="@dimen/dp_8"
android:paddingTop="@dimen/_12dp"
android:paddingBottom="@dimen/_16dp"
android:layout_marginTop="317dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
@@ -220,6 +246,20 @@
app:layout_constraintTop_toTopOf="@id/chip_group" />
<LinearLayout
android:id="@+id/fund_info_cards"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/_16dp"
android:paddingBottom="@dimen/_20dp"
app:layout_constraintTop_toBottomOf="@id/chip_group"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:clipChildren="false"
android:clipToPadding="false"
android:orientation="horizontal"
android:visibility="visible"/>
<com.navi.amc.common.view.FundInvestmentDetailsView
android:id="@+id/fund_investment_details"
android:layout_width="match_parent"
@@ -229,6 +269,6 @@
android:layout_marginTop="@dimen/_28dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/chip_tooltip_content" />
app:layout_constraintTop_toBottomOf="@id/fund_info_cards" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -0,0 +1,51 @@
<?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"
android:background="@drawable/fund_graph_tooltip_bg"
android:padding="@dimen/_1dp"
android:elevation="@dimen/_0dp">
<androidx.cardview.widget.CardView
android:id="@+id/tooltip_card"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingHorizontal="@dimen/_6dp"
android:paddingVertical="@dimen/_4dp">
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textColor="#333333"
android:textSize="12sp"
android:textStyle="bold"
tools:text="NAV : ₹10.88" />
<TextView
android:id="@+id/subtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textColor="#666666"
android:textSize="12sp"
tools:text="9 Jun 2025" />
</LinearLayout>
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -1,70 +1,25 @@
<?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">
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false"
android:orientation="vertical"
android:paddingHorizontal="@dimen/dp_6"
android:paddingBottom="@dimen/dp_4">
android:translationZ="@dimen/_1dp"
android:elevation="@dimen/_1dp"
android:layout_height="wrap_content">
<View
android:id="@+id/background"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@drawable/fund_graph_tooltip_bg"
android:layout_marginBottom="@dimen/dp_6"
app:layout_constraintBottom_toBottomOf="@+id/scroll_indicator"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/title" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="left"
android:textSize="@dimen/sp_12"
android:minWidth="@dimen/dp_90"
android:paddingHorizontal="@dimen/dp_12"
android:paddingTop="@dimen/dp_6"
<ImageView
android:id="@+id/pointer_circle"
android:layout_width="14dp"
android:layout_height="14dp"
android:src="@drawable/black_border_circle"
android:elevation="@dimen/_1dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="NAV: ₹2,345.34"/>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/sp_12"
android:layout_gravity="center"
android:visibility="gone"
tools:visibility="visible"
android:gravity="left"
android:minWidth="@dimen/dp_90"
android:paddingHorizontal="@dimen/dp_12"
android:paddingBottom="@dimen/dp_4"
app:layout_constraintEnd_toEndOf="@id/title"
app:layout_constraintStart_toStartOf="@id/title"
app:layout_constraintTop_toBottomOf="@id/title"
tools:text="on 27 Sep 2023"/>
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/scroll_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="@dimen/dp_6"
android:src="@drawable/black_border_circle"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/subtitle" />
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="#F8F8FA"
android:clipChildren="false"
android:clipToPadding="false"
android:padding="@dimen/_16dp">
<ImageView
android:id="@+id/info_tag"
android:layout_width="@dimen/_36dp"
android:layout_height="@dimen/_18dp"
android:translationY="-25dp"
android:translationZ="@dimen/_2dp"
app:layout_constraintStart_toStartOf="@id/title"
app:layout_constraintTop_toTopOf="parent" />
<com.navi.design.textview.NaviTextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="NAV (9 Jun)"
tools:textColor="#6B6B6B" />
<com.navi.design.textview.NaviTextView
android:id="@+id/subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/title"
tools:text="₹10.88"
tools:textColor="#191919" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -3,45 +3,85 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<androidx.constraintlayout.widget.ConstraintLayout
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="@dimen/dp_16"
android:paddingEnd="@dimen/dp_16">
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/icon"
android:layout_width="@dimen/dp_48"
android:layout_height="@dimen/dp_48"
android:layout_marginTop="@dimen/dp_16"
<!-- Title Container with Chevron (Clickable Area) -->
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/title_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:paddingHorizontal="@dimen/dp_16"
android:paddingVertical="@dimen/_20dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/title" />
app:layout_constraintTop_toTopOf="parent">
<com.navi.design.textview.NaviTextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Fund manager" />
<com.navi.design.textview.NaviTextView
android:id="@+id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/chevron"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Fund Manager" />
<com.navi.design.textview.NaviTextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_16"
app:layout_constraintStart_toEndOf="@id/icon"
app:layout_constraintTop_toTopOf="@id/icon"
tools:text="Mr Ashutosh" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/chevron"
android:layout_width="24dp"
android:layout_height="24dp"
app:layout_constraintBottom_toBottomOf="@id/title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/title"
tools:src="@android:drawable/arrow_up_float" />
</androidx.constraintlayout.widget.ConstraintLayout>
<com.navi.design.textview.NaviTextView
android:id="@+id/experience"
android:layout_width="wrap_content"
<!-- Content container that will be shown/hidden -->
<LinearLayout
android:id="@+id/content_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_2"
app:layout_constraintStart_toStartOf="@id/name"
app:layout_constraintTop_toBottomOf="@id/name"
tools:text="5 years" />
</androidx.constraintlayout.widget.ConstraintLayout>
android:paddingBottom="@dimen/_20dp"
android:paddingHorizontal="@dimen/dp_16"
android:orientation="horizontal">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/icon"
android:layout_width="@dimen/dp_48"
android:layout_height="@dimen/dp_48" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="@dimen/dp_16"
android:orientation="vertical">
<com.navi.design.textview.NaviTextView
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:text="Mr Ashutosh" />
<com.navi.design.textview.NaviTextView
android:id="@+id/experience"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_2"
tools:text="5 years" />
<com.navi.design.textview.NaviTextView
android:id="@+id/asset_details"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_2"
tools:text="Asset details with long text that might wrap to multiple lines and require proper width constraints" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</layout>

View File

@@ -15,7 +15,7 @@
android:layout_width="@dimen/dp_24"
android:layout_height="@dimen/dp_24"
android:layout_marginStart="@dimen/dp_16"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintBottom_toBottomOf="@id/sub_title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/refund_image" />
@@ -85,5 +85,27 @@
app:layout_constraintTop_toTopOf="parent"
app:tilt_direction="forward" />
<com.navi.design.textview.NaviTextView
android:id="@+id/compliance_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/_56dp"
tools:text="Regulated by"
android:visibility="gone"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/sub_title"/>
<ImageView
android:id="@+id/compliance_icon"
android:layout_width="@dimen/_46dp"
android:layout_height="@dimen/_20dp"
android:layout_marginTop="@dimen/_8dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@id/compliance_text"
android:src="@drawable/navi_pay_sa_home_page_send_to_bank_or_upi_icon"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<com.navi.design.textview.NaviTextView
android:id="@+id/tag_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/_8dp"
android:gravity="center"
tools:text="Equity" />
</layout>

View File

@@ -3,59 +3,90 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<androidx.constraintlayout.widget.ConstraintLayout
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:orientation="vertical">
<com.navi.design.textview.NaviTextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/dp_16"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Fund Holdings - Sector" />
<com.navi.design.textview.NaviTextView
android:id="@+id/left_title"
android:layout_width="@dimen/dp_0"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_24"
android:layout_marginStart="@dimen/dp_16"
app:layout_constraintEnd_toStartOf="@id/right_title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/title"
tools:text="Sector" />
<com.navi.design.textview.NaviTextView
android:id="@+id/right_title"
android:layout_width="@dimen/dp_0"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="@dimen/dp_16"
app:layout_constraintStart_toEndOf="@id/left_title"
app:layout_constraintTop_toTopOf="@id/left_title"
android:gravity="right"
tools:text="Allocation" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/items"
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/title_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_16"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintEnd_toEndOf="parent"
android:background="@color/white"
android:paddingVertical="@dimen/_20dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/left_title" />
app:layout_constraintEnd_toEndOf="parent">
<include
android:id="@+id/button"
android:layout_width="wrap_content"
<com.navi.design.textview.NaviTextView
android:id="@+id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/dp_16"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/chevron"
tools:text="Fund Holdings - Sector" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/chevron"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="@dimen/dp_16"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/title"
app:layout_constraintBottom_toBottomOf="@id/title"
tools:src="@android:drawable/arrow_up_float" />
</androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout
android:id="@+id/content_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
layout="@layout/amc_button"
android:layout_marginHorizontal="@dimen/dp_16"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/items" />
</androidx.constraintlayout.widget.ConstraintLayout>
android:paddingBottom="@dimen/_20dp"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_8"
android:orientation="horizontal">
<com.navi.design.textview.NaviTextView
android:id="@+id/left_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="@dimen/dp_16"
tools:text="Sector" />
<com.navi.design.textview.NaviTextView
android:id="@+id/right_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginEnd="@dimen/dp_16"
android:gravity="right"
tools:text="Allocation" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/items"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_16"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
<include
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
layout="@layout/amc_button"
android:layout_gravity="center_horizontal"
android:layout_marginHorizontal="@dimen/dp_16"
android:layout_marginTop="@dimen/dp_8" />
</LinearLayout>
</LinearLayout>
</layout>

View File

@@ -0,0 +1,18 @@
<?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">
<FrameLayout
android:layout_width="1dp"
android:layout_height="match_parent"
android:elevation="@dimen/_0dp"
android:clipChildren="false">
<View
android:id="@+id/vertical_dotted_line"
android:layout_width="1dp"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal"
android:background="@drawable/dashed_line_2" />
</FrameLayout>
</layout>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:bottom="@dimen/dp_4"
android:top="@dimen/dp_4">
<shape android:shape="rectangle">
<size
android:width="@dimen/_1dp"
android:height="@dimen/_1dp" />
<solid android:color="@color/backgroundSecondaryColor" />
</shape>
</item>
</layer-list>

View File

@@ -30,6 +30,9 @@ data class FundDetailsWidget(
data class FundDetailData(
@SerializedName("title") val title: TextWithStyle? = null,
@SerializedName("items") val items: List<FundItemData>? = null,
@SerializedName("rightChevronUp") val rightChevronUp: String? = null,
@SerializedName("rightChevronDown") val rightChevronDown: String? = null,
@SerializedName("isExpanded") val isExpanded: Boolean? = null,
)
data class FundItemData(