NTP-3997, NTP-4001 || Projected Returns for Funds (#12630)

This commit is contained in:
A Shrihari Raju
2024-09-24 22:21:48 +05:30
committed by GitHub
parent bc6f8a9f5c
commit 2b92be5a65
19 changed files with 488 additions and 33 deletions

View File

@@ -68,6 +68,8 @@ import com.navi.base.utils.isNotNullAndNotEmpty
import com.navi.base.utils.isNull
import com.navi.base.utils.orFalse
import com.navi.base.utils.orZero
import com.navi.common.animation.slideInFromTop
import com.navi.common.animation.snapOut
import com.navi.common.listeners.FragmentInterchangeListener
import com.navi.common.listeners.HeaderInteractionListener
import com.navi.common.pushnotification.NotificationConstants
@@ -223,6 +225,7 @@ class FundBuyingFragmentV2 : AmcBaseFragment(), WidgetCallback {
binding.lumpsumTutorial.root.visibility = View.GONE
binding.lumpsumPaymentCard.isVisible = false
binding.cutoffBanner.isVisible = false
binding.lumpsumProjection.container.isVisible = false
setFooter()
}
@@ -248,6 +251,11 @@ class FundBuyingFragmentV2 : AmcBaseFragment(), WidgetCallback {
} ?: run { binding.lumpsumPaymentCard.isVisible = false }
binding.cutoffBanner.isVisible = true
if (viewModel.isFundReturnNegative || viewModel.isAmountErrorState) {
binding.lumpsumProjection.container.visibility = View.GONE
} else {
binding.lumpsumProjection.container.visibility = View.VISIBLE
}
setFooter()
}
@@ -290,6 +298,11 @@ class FundBuyingFragmentV2 : AmcBaseFragment(), WidgetCallback {
content.fundInvestmentType?.lumpsumData = viewModel.lumpsumData
}
content.fundInvestmentType?.simulationData?.let {
binding.lumpsumAmount.enableSuccessText = false
binding.lumpsumAmount.toggleSuccessStateVisibility(false)
}
viewModel.sipData = content.fundInvestmentType?.sipData
viewModel.lumpsumData = content.fundInvestmentType?.lumpsumData
@@ -338,23 +351,40 @@ class FundBuyingFragmentV2 : AmcBaseFragment(), WidgetCallback {
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
viewModel.lastLumpsumInteraction = AmcAnalytics.TYPE
binding.lumpsumAmount.getUserInput()?.let { it ->
binding.lumpsumRecommended.setSelectedAmount(it)
binding.lumpsumAmount.getUserInput()?.let { userInput ->
binding.lumpsumRecommended.setSelectedAmount(userInput)
val errorState = binding.lumpsumAmount.getUserInputPostValidation()
if (binding.paymentFooter.root.visibility == View.VISIBLE) {
val errorState =
binding.lumpsumAmount.getUserInputPostValidation()
binding.paymentFooter.amount.text =
CurrencyUtils.getNormalizedAmount(
if (errorState != null && it.isNotNullAndNotEmpty())
it.toBigDecimal()
if (
errorState != null &&
userInput.isNotNullAndNotEmpty()
)
userInput.toBigDecimal()
else BigDecimal(0)
)
}
if (errorState != null && userInput.isNotNullAndNotEmpty()) {
viewModel.isAmountErrorState = false
viewModel.projectedReturn(userInput).apply {
viewModel.projectedText?.let { projectedText ->
projectedText.text = this
binding.lumpsumProjection.projectionTitle
.setSpannableString(projectedText)
slideInFromTop(binding.lumpsumProjection.container) {}
}
}
} else {
binding.lumpsumProjection.container.visibility = View.GONE
snapOut(binding.lumpsumProjection.container) {}
viewModel.isAmountErrorState = true
}
(content.fundInvestmentType?.lumpsumData?.getOrNull(0)
as? LabeledTextInputFixedHintWidgetModel)
?.let { lumpSum ->
lumpSum.widgetData?.inputTextFixedHintItemData?.savedText =
it
userInput
}
}
}
@@ -362,6 +392,28 @@ class FundBuyingFragmentV2 : AmcBaseFragment(), WidgetCallback {
override fun afterTextChanged(p0: Editable?) {}
}
)
content.fundInvestmentType?.simulationData?.let { simulationData ->
viewModel.lumpSumReturnRate = simulationData.lumpsumReturnRate
viewModel.isFundReturnNegative = simulationData.lumpsumReturnRate == null
binding.lumpsumProjection.image.showWhenDataIsAvailable(simulationData.iconCode)
binding.lumpsumProjection.title.setSpannableString(
simulationData.simulationText
)
val defaultAmount = viewModel.projectedReturn(viewModel.lumpsumAmount)
defaultAmount?.let { amount ->
viewModel.projectedText = simulationData.projectedReturnsText
viewModel.projectedText?.text = amount
binding.lumpsumProjection.projectionTitle.setSpannableString(
viewModel.projectedText
)
binding.lumpsumProjection.container.isVisible = true
}
}
?: run {
viewModel.isFundReturnNegative = true
binding.lumpsumAmount.enableSuccessText = true
binding.lumpsumAmount.toggleSuccessStateVisibility(false)
}
content.fundInvestmentType?.lumpsumRecommendedAmount?.let { chipData ->
binding.lumpsumRecommended.setProperties(chipData) { lsRecommended, action ->
val updatedAmount =

View File

@@ -94,7 +94,16 @@ data class FundInvestmentType(
@SerializedName("lumpsumPaymentMode") val lumpsumPaymentMode: PaymentMode? = null,
@SerializedName("informationNote") val informationNote: InformationNoteData? = null,
@SerializedName("sipStartDateOffset") val sipStartDateOffset: SipStartDateOffset? = null,
@SerializedName("noteBanner") val noteBanner: NoteBannerData? = null
@SerializedName("noteBanner") val noteBanner: NoteBannerData? = null,
@SerializedName("simulationData") val simulationData: AmountSimulationData? = null
)
data class AmountSimulationData(
@SerializedName("iconCode") val iconCode: String? = null,
@SerializedName("lumpsumReturnRate") val lumpsumReturnRate: Double? = null,
@SerializedName("backgroundColor") val backgroundColor: String? = null,
@SerializedName("simulationText") val simulationText: TextWithStyle? = null,
@SerializedName("projectedReturnsText") val projectedReturnsText: TextWithStyle? = null
)
@Parcelize

View File

@@ -56,7 +56,13 @@ data class FundDuration(
@SerializedName("key") val key: String? = null,
@SerializedName("durationInMonths") val durationInMonths: Int? = null,
@SerializedName("selected") var selected: Boolean? = null,
@SerializedName("bgColorVariations") val bgColorVariations: Map<String, String>? = null
@SerializedName("bgColorVariations") val bgColorVariations: Map<String, String>? = null,
@SerializedName("returnsTooltip") val returnsTooltip: ReturnsTooltipData? = null
)
data class ReturnsTooltipData(
@SerializedName("imageUrl") val imageUrl: String? = null,
@SerializedName("title") val title: TextWithStyle? = null
)
data class XAxisLabelData(

View File

@@ -29,6 +29,7 @@ import com.navi.amc.utils.Constant.DAILY
import com.navi.amc.utils.Constant.FORTNIGHTLY
import com.navi.amc.utils.Constant.MONTHLY
import com.navi.amc.utils.Constant.WEEKLY
import com.navi.amc.utils.roundOffAmount
import com.navi.amc.utils.updateCheckerResponse
import com.navi.base.model.ActionData
import com.navi.common.utils.EMPTY
@@ -73,6 +74,10 @@ class FundBuyV2ViewModel @Inject constructor(private val repository: FundBuyRepo
var sipAmount: String? = null
var sipData: List<NaviWidget>? = null
var lumpsumData: List<NaviWidget>? = null
var lumpSumReturnRate: Double? = null
var projectedText: TextWithStyle? = null
var isFundReturnNegative: Boolean = false
var isAmountErrorState: Boolean = false
private val _paymentInitiateData =
MutableLiveData<AdditionalDataAsyncResponse<NextCtaResponse>?>()
@@ -312,6 +317,10 @@ class FundBuyV2ViewModel @Inject constructor(private val repository: FundBuyRepo
isAutoPaySelected = null
sipData = null
lumpsumData = null
lumpSumReturnRate = null
projectedText = null
isFundReturnNegative = false
isAmountErrorState = false
super.onCleared()
}
@@ -345,6 +354,14 @@ class FundBuyV2ViewModel @Inject constructor(private val repository: FundBuyRepo
return _fundBuyScreenData.value?.content?.isManualVsAutopay.orFalse()
}
fun projectedReturn(userInput: String?): String? {
val returnRate = lumpSumReturnRate
if (returnRate != null && userInput != null) {
return roundOffAmount(userInput.toDouble() * returnRate)
}
return null
}
companion object {
private const val DAY_IN_MILLIS = 24 * 60 * 60 * 1000L
}

View File

@@ -14,14 +14,20 @@ import android.os.Build
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.view.ViewTreeObserver
import android.widget.ImageView
import android.widget.LinearLayout
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.ViewCompat
import androidx.core.view.children
import androidx.core.view.forEach
import androidx.core.view.isVisible
import androidx.databinding.DataBindingUtil
import androidx.transition.Fade
import androidx.transition.TransitionManager
import com.github.mikephil.charting.data.Entry
import com.github.mikephil.charting.data.LineData
import com.github.mikephil.charting.data.LineDataSet
@@ -45,11 +51,14 @@ import com.navi.amc.utils.ColorUtils.FUND_GRAPH_CHIP_SELECTED_COLOR
import com.navi.base.model.ActionData
import com.navi.base.utils.orFalse
import com.navi.base.utils.orZero
import com.navi.common.animation.fadeOut
import com.navi.common.animation.slideInFromTop
import com.navi.design.utils.CornerRadius
import com.navi.design.utils.dpToPxInInt
import com.navi.design.utils.getNaviDrawable
import com.navi.design.utils.parseColorSafe
import com.navi.design.utils.setSpannableString
import com.navi.naviwidgets.extensions.showWhenDataIsAvailable
import com.navi.naviwidgets.widgets.InfoWithTimerWidgetLayout.Companion.COLOR_WHITE
class FundGraphView(context: Context, attributeSet: AttributeSet? = null) :
@@ -159,9 +168,26 @@ class FundGraphView(context: Context, attributeSet: AttributeSet? = null) :
object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
binding.options.viewTreeObserver.removeOnGlobalLayoutListener(this)
val chipSpacing = dpToPxInInt(8)
val chipSpacing = dpToPxInInt(CHIP_SPACING_DENSE)
binding.options.chipSpacingHorizontal = chipSpacing
binding.triangles.forEach { triangle ->
triangle.apply {
layoutParams =
LinearLayout.LayoutParams(dpToPxInInt(20), dpToPxInInt(12))
.apply {
setMargins(
dpToPxInInt(TRIANGLE_MARGIN_START),
0,
dpToPxInInt(
TRIANGLE_MARGIN_END + CHIP_SPACING_DENSE
),
0
)
}
}
}
binding.options.requestLayout()
binding.triangles.requestLayout()
}
}
binding.options.viewTreeObserver.addOnGlobalLayoutListener(globalLayoutListener)
@@ -326,6 +352,7 @@ class FundGraphView(context: Context, attributeSet: AttributeSet? = null) :
private fun addChipView(data: FundDuration) {
val itemId = ViewCompat.generateViewId()
val chipBinding =
DataBindingUtil.inflate<ChipBinding>(
LayoutInflater.from(context),
@@ -333,14 +360,32 @@ class FundGraphView(context: Context, attributeSet: AttributeSet? = null) :
binding.options,
false
)
chipBinding.chip.id = ViewCompat.generateViewId()
chipBinding.chip.id = itemId
chipIdToDataMap[chipBinding.chip.id] = data
chipBinding.chip.chipBackgroundColor = createChipBg(data)
chipBinding.chip.setSpannableString(data.title)
binding.options.addView(chipBinding.root)
val triangle =
ImageView(context).apply {
id = itemId
setImageResource(R.drawable.triangle)
visibility = View.GONE
layoutParams =
LinearLayout.LayoutParams(
dpToPxInInt(TRIANGLE_WIDTH),
dpToPxInInt(TRIANGLE_HEIGHT)
)
.apply {
setMargins(
dpToPxInInt(TRIANGLE_MARGIN_START),
0,
dpToPxInInt(TRIANGLE_MARGIN_END + CHIP_SPACING_NORMAL),
0
)
}
}
binding.triangles.addView(triangle)
}
private fun updateSelectedChips(group: ChipGroup, checkedIds: List<Int>) {
@@ -359,10 +404,13 @@ class FundGraphView(context: Context, attributeSet: AttributeSet? = null) :
if (checkedIds.contains(childId)) {
fundDurationSelectedListener?.invoke(childData, isChipCheckedByUser)
childView.setSpannableString(childData.title, ColorUtils.KEY_COLOR_SELECTED)
updateSelectedTooltip(childData, childId)
childData.key?.let { key -> updateGraph(key) }
childData.selected = true
} else {
childView.setSpannableString(childData.title)
val triangle = binding.triangles.findViewById<ImageView>(childId)
triangle.visibility = View.INVISIBLE
childData.selected = false
}
}
@@ -397,4 +445,40 @@ class FundGraphView(context: Context, attributeSet: AttributeSet? = null) :
intArrayOf(defaultColor.parseColorSafe(), selectedColor.parseColorSafe())
)
}
private fun updateSelectedTooltip(childData: FundDuration?, childId: Int) {
childData?.returnsTooltip?.let { tooltipData ->
val triangle = binding.triangles.findViewById<ImageView>(childId)
binding.triangles.visibility = View.VISIBLE
triangle.visibility = View.VISIBLE
binding.chipTooltipContent.root.visibility = View.VISIBLE
slideInFromTop(binding.chipTooltipContent.root) {}
binding.chipTooltipContent.tooltipImage.showWhenDataIsAvailable(tooltipData.imageUrl)
binding.chipTooltipContent.title.setSpannableString(tooltipData.title)
}
?: run {
val triangle = binding.triangles.findViewById<ImageView>(childId)
TransitionManager.beginDelayedTransition(
binding.root as ViewGroup,
Fade().apply { duration = FADE_OUT_DURATION }
)
fadeOut(binding.chipTooltipContent.root) {
triangle.visibility = View.GONE
binding.triangles.visibility = View.GONE
}
}
}
companion object {
const val TAG = "FundGraphView"
const val TRIANGLE_WIDTH = 20
const val TRIANGLE_HEIGHT = 12
const val TRIANGLE_MARGIN_START = 16
const val TRIANGLE_MARGIN_TOP = 8
const val TRIANGLE_MARGIN_END = 16
const val CHIP_SPACING_NORMAL = 16
const val CHIP_SPACING_DENSE = 8
const val FADE_OUT_DURATION = 1000L
}
}

View File

@@ -20,6 +20,7 @@ import com.navi.amc.common.model.NextCtaResponse
import com.navi.amc.fundbuy.models.FundDuration
import com.navi.amc.fundbuy.models.NavInfo
import com.navi.amc.network.deserializer.FundListDeserializer
import com.navi.amc.utils.Constant.RUPEE_SYMBOL
import com.navi.amc.utils.Constant.UPI_APP_INTENT_URL
import com.navi.base.AppServiceManager
import com.navi.base.model.ActionData
@@ -40,8 +41,10 @@ import com.navi.naviwidgets.validations.ValidationJsonDeserializer
import com.navi.naviwidgets.widgets.NaviWidgetJsonDeserializer
import java.lang.reflect.Type
import java.net.URI
import java.text.DecimalFormat
import java.text.SimpleDateFormat
import java.util.TimeZone
import kotlin.math.floor
import kotlin.math.max
fun calculateInvestmentReturnsAmount(
@@ -200,3 +203,15 @@ fun getFundGraphItemCacheKey(key: String, fundName: String): String {
.plus(UNDERSCORE)
.plus("CACHE_KEY")
}
fun roundOffAmount(value: Double): String {
// function to return amount in K, L, Cr if greater than 10K and rounded up to 2 decimal places
val df = DecimalFormat("#.##")
return RUPEE_SYMBOL +
when {
value >= 1_00_00_000 -> df.format(floor(value / 1_00_00_000 * 100) / 100) + " Cr"
value >= 1_00_000 -> df.format(floor(value / 1_00_000 * 100) / 100) + " L"
value >= 10_000 -> df.format(floor(value / 1_000 * 100) / 100) + " K"
else -> df.format(floor(value * 100) / 100.0f)
}
}

View File

@@ -0,0 +1,23 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="328dp"
android:height="76dp"
android:viewportWidth="328"
android:viewportHeight="76"
>
<group>
<clip-path
android:pathData="M4 0H324C326.209 0 328 1.79086 328 4V72C328 74.2091 326.209 76 324 76H4C1.79086 76 0 74.2091 0 72V4C0 1.79086 1.79086 0 4 0Z"
/>
<path
android:pathData="M0 0V76H328V0"
android:fillColor="#FFFFFF"
/>
<path
android:pathData="M4 0H324C326.209 0 328 1.79086 328 4V72C328 74.2091 326.209 76 324 76H4C1.79086 76 0 74.2091 0 72V4C0 1.79086 1.79086 0 4 0Z"
android:strokeWidth="2"
android:strokeColor="#EBEBEB"
/>
</group>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="328dp"
android:height="40dp"
android:viewportWidth="328"
android:viewportHeight="40">
<path
android:pathData="M0,0V36C0,38.21 1.79,40 4,40H324C326.21,40 328,38.21 328,36V0C328,2.21 326.21,4 324,4H4C1.79,4 0,2.21 0,0Z"
android:fillColor="#F5F5F5"
android:fillType="evenOdd"/>
</vector>

View File

@@ -0,0 +1,14 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="12dp"
android:viewportWidth="20"
android:viewportHeight="12">
<path
android:pathData="M9.268,1L1,11.627L17.801,11.458L9.268,1Z"
android:fillColor="#ffffff"/>
<path
android:strokeWidth="1"
android:pathData="M19.355,10.68H17.909C16.955,10.68 16.058,10.227 15.493,9.458L9.268,1L3.396,9.402C2.834,10.205 1.916,10.684 0.937,10.684H0"
android:fillColor="#00000000"
android:strokeColor="#EBEBEB"/>
</vector>

View File

@@ -0,0 +1,80 @@
<?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:id="@+id/container"
android:layout_width="match_parent"
android:paddingHorizontal="@dimen/_8dp"
android:paddingTop="@dimen/_10dp"
android:background="@drawable/projection_background"
android:paddingBottom="@dimen/_8dp"
android:translationZ="-2dp"
android:layout_height="wrap_content">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/image"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:layout_width="@dimen/_22dp"
android:layout_height="@dimen/_22dp"/>
<LinearLayout
app:layout_constraintStart_toEndOf="@id/image"
android:layout_marginStart="@dimen/_4dp"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="@dimen/_3dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<com.navi.design.textview.NaviTextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:lineSpacingExtra="@dimen/_3dp"
android:maxLines="@integer/integer_1"
app:layout_constraintEnd_toStartOf="@id/title2"
tools:text="About SIPS" />
<com.navi.design.textview.NaviTextView
android:id="@+id/projection_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:lineSpacingExtra="@dimen/_3dp"
android:maxLines="@integer/integer_1"
app:layout_constraintStart_toEndOf="@id/title"
app:layout_constraintEnd_toEndOf="parent"
tools:text="About SIPS" />
</LinearLayout>
<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"
android:layout_marginBottom="@dimen/dp_16"
android:lineSpacingExtra="@dimen/dp_4"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/title"
app:layout_constraintTop_toBottomOf="@id/title"
tools:text="SIP allows you to invest a fixed amount of money at pre-defined intervals." />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/cross"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/dp_16"
android:layout_marginTop="@dimen/dp_16"
android:src="@drawable/small_cross"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -5,7 +5,7 @@
<com.google.android.material.chip.Chip
android:id="@+id/chip"
style="@style/ChipStyle"
android:layout_width="wrap_content"
android:layout_width="@dimen/dp_51"
android:layout_height="wrap_content"
android:checkable="true"
android:clickable="true"

View File

@@ -95,17 +95,31 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/social_validation" />
<include
android:id="@+id/lumpsum_projection"
layout="@layout/amc_amount_projection_fundbuy_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:paddingTop="@dimen/_4dp"
android:layout_marginTop="-4dp"
android:elevation="-2dp"
android:layout_marginHorizontal="@dimen/_16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/lumpsum_amount" />
<com.navi.amc.fundbuy.views.AmountChipGroupView
android:id="@+id/lumpsum_recommended"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_16"
android:layout_marginTop="@dimen/dp_24"
android:layout_marginTop="@dimen/dp_130"
android:layout_marginEnd="@dimen/dp_16"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/lumpsum_amount" />
app:layout_constraintTop_toTopOf="@id/lumpsum_amount" />
<com.navi.amc.fundbuy.views.AmountChipGroupView
android:id="@+id/sip_recommended"
@@ -318,7 +332,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="sip_frequency,sip_amount,date,lumpsum_amount" />
app:constraint_referenced_ids="sip_frequency,sip_amount,date,lumpsum_projection" />
<com.navi.naviwidgets.widgets.TimerWithImageWidgetLayout
android:id="@+id/cutoff_banner"

View File

@@ -166,26 +166,60 @@
android:id = "@+id/chip_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:translationZ="@dimen/dp_8"
android:gravity="center_horizontal"
android:paddingTop="@dimen/dp_8"
android:layout_marginTop="317dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/chart_label_barrier">
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.chip.ChipGroup
android:id="@+id/options"
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:paddingTop="@dimen/dp_8"
android:layout_marginHorizontal="@dimen/dp_8"
app:chipSpacingHorizontal="@dimen/dp_16"
app:selectionRequired="true"
app:singleLine="true"
app:singleSelection="true" />
android:orientation="vertical"
android:layout_height="wrap_content">
<com.google.android.material.chip.ChipGroup
android:id="@+id/options"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:paddingTop="@dimen/dp_8"
android:layout_marginHorizontal="@dimen/dp_8"
app:chipSpacingHorizontal="@dimen/dp_16"
app:selectionRequired="true"
app:singleLine="true"
app:singleSelection="true" />
<LinearLayout
android:id="@+id/triangles"
android:layout_width="match_parent"
android:layout_gravity="center"
android:showDividers="middle"
android:layout_marginTop="@dimen/_8dp"
android:elevation="@dimen/_8dp"
android:translationZ="@dimen/dp_8"
android:layout_marginHorizontal="@dimen/dp_8"
android:divider="@color/transparent"
android:layout_height="wrap_content"
android:orientation="horizontal" />
</LinearLayout>
</LinearLayout>
<include
android:id="@+id/chip_tooltip_content"
layout="@layout/generic_tooltip_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="-1dp"
android:translationZ="-8dp"
android:visibility="gone"
android:layout_marginTop="-2dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/chip_group" />
<com.navi.amc.common.view.FundInvestmentDetailsView
android:id="@+id/fund_investment_details"
@@ -195,6 +229,6 @@
android:layout_marginTop="@dimen/dp_30"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/chip_group" />
app:layout_constraintTop_toBottomOf="@id/chip_tooltip_content" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -0,0 +1,59 @@
<?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="match_parent"
android:padding="@dimen/_16dp"
android:background="@drawable/box_shadow_bottom_4"
android:layout_height="wrap_content">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/tooltip_image"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:layout_width="@dimen/_44dp"
android:layout_height="@dimen/_44dp"/>
<com.navi.design.textview.NaviTextView
android:id="@+id/title"
android:layout_width="@dimen/_0dp"
android:layout_height="wrap_content"
android:lineSpacingExtra="@dimen/_3dp"
android:layout_marginStart="@dimen/_12dp"
app:layout_constraintStart_toEndOf="@id/tooltip_image"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
tools:text="About SIPS" />
<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"
android:layout_marginBottom="@dimen/dp_16"
android:lineSpacingExtra="@dimen/dp_4"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/title"
app:layout_constraintTop_toBottomOf="@id/title"
tools:text="SIP allows you to invest a fixed amount of money at pre-defined intervals." />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/cross"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/dp_16"
android:layout_marginTop="@dimen/dp_16"
android:src="@drawable/small_cross"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -112,4 +112,5 @@
<dimen name="_dp55">55dp</dimen>
<dimen name="_dp33">33dp</dimen>
<dimen name="_dp125">125dp</dimen>
<dimen name="dp_51">51dp</dimen>
</resources>

View File

@@ -1,6 +1,6 @@
/*
*
* * Copyright © 2022-2023 by Navi Technologies Limited
* * Copyright © 2022-2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
@@ -47,6 +47,14 @@ fun slideOutBottom(view: View?, callback: () -> Unit = {}) {
outAnimation(R.anim.slide_out_bottom, view, callback)?.let { view?.startAnimation(it) }
}
fun snapOut(view: View?, callback: () -> Unit = {}) {
outAnimation(R.anim.snap_out, view, callback)?.let { view?.startAnimation(it) }
}
fun slideInFromTop(view: View?, callback: () -> Unit = {}) {
inAnimation(R.anim.snap_slide_in_top, view, callback)?.let { view?.startAnimation(it) }
}
fun shakeAnimation(view: View?) {
if (view == null || view.visibility == View.GONE || view.visibility == View.INVISIBLE) {
return

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="250"
android:fromYDelta="1%"
android:toYDelta="-100%" />
<alpha
android:duration="250"
android:fromAlpha="1.0"
android:toAlpha="0.0" />
</set>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="300"
android:fromYDelta="-100%"
android:toYDelta="1%" />
<alpha
android:duration="300"
android:fromAlpha="0.0"
android:toAlpha="1.0" />
</set>

View File

@@ -1,6 +1,6 @@
/*
*
* * Copyright © 2023 by Navi Technologies Limited
* * Copyright © 2023-2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
@@ -49,6 +49,7 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
var showInfoIcon: Boolean = false
var baseBorderDrawable: Int? = null
var isErrorTextNull = true
var isSuccessStateVisible = true
fun generateQueryParam(
widgetModel: InputWidgetModel,
@@ -249,7 +250,9 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
}
return if (isInputValid) {
setError(null)
setMoneySuccessText(tempInput)
if (isSuccessStateVisible) {
setMoneySuccessText(tempInput)
}
tempInput
} else {
null
@@ -521,5 +524,9 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
binding.info.isVisible = show && showInfoIcon
}
fun toggleSuccessStateVisibility(show: Boolean) {
isSuccessStateVisible = show
}
class EndTextClickAction(val baseInputWidget: BaseInputWidget) : NaviClickAction()
}