From a5dbbe5eda82c1d352397fc49dffd629a5f06985 Mon Sep 17 00:00:00 2001 From: Ayushman Sharma Date: Mon, 23 Oct 2023 15:03:45 +0530 Subject: [PATCH] TP-44454| slide to act button with property and rederer (#217) --- app/src/main/res/raw/mock.json | 126 ++++++ .../ComposePropertyDeserializer.kt | 3 + .../deserializer/UiTronDataDeserializer.kt | 3 + .../uitron/model/data/SlideToActButtonData.kt | 19 + .../model/ui/SlideToActButtonProperty.kt | 52 +++ .../com/navi/uitron/model/ui/UiTronView.kt | 1 + .../uitron/render/SlideToActButtonRenderer.kt | 420 ++++++++++++++++++ .../com/navi/uitron/render/UiTronRenderer.kt | 19 + .../java/com/navi/uitron/utils/Constants.kt | 5 +- .../main/java/com/navi/uitron/utils/Ext.kt | 3 + 10 files changed, 650 insertions(+), 1 deletion(-) create mode 100644 navi-uitron/src/main/java/com/navi/uitron/model/data/SlideToActButtonData.kt create mode 100644 navi-uitron/src/main/java/com/navi/uitron/model/ui/SlideToActButtonProperty.kt create mode 100644 navi-uitron/src/main/java/com/navi/uitron/render/SlideToActButtonRenderer.kt diff --git a/app/src/main/res/raw/mock.json b/app/src/main/res/raw/mock.json index 120608a..daae628 100644 --- a/app/src/main/res/raw/mock.json +++ b/app/src/main/res/raw/mock.json @@ -13468,5 +13468,131 @@ } } ] + }, + "slideToActMock": { + "parentComposeView": [ + { + "property": { + "layoutId": "slideToActButton", + "viewType": "SlideToActButton", + "hint": "Here is some hint", + "height": "48", + "circleButtonSize": 40, + "margin": { + "start": 16, + "end": 16 + }, + "shimmerColors": [ + "#F0C1FF", + "#F0C1FF", + "#FFFFFF", + "#F0C1FF", + "#F0C1FF" + ], + "buttonStateIcon": { + "invalidIcon": + { + "property": { + "layoutId": "invalidStateIcon", + "viewType": "Image", + "height": "24", + "width": "24" + } + }, + "validIcon": + { + "property": { + "layoutId": "validStateIcon", + "viewType": "Image", + "height": "24", + "width": "24" + } + }, + "completedIcon": + { + "property": { + "layoutId": "completedStateIcon", + "viewType": "Image", + "height": "24", + "width": "24" + } + } + + }, + "backgroundColorWithState": { + "invalidColor": "#B5ACB9", + "validColor": "#1F002A", + "loadingColor": "#6E5876" + }, + "loadingStateLottie": { + "property": { + "layoutId": "loadingLottie", + "viewType": "Lottie", + "height": "WRAP_CONTENT", + "width": "MATCH_PARENT" + } + }, + "slideButtonBgColor": { + "invalidColor" : "#E9E8EA", + "validColor" : "#FFFFFF" + }, + "clipData": { + "shapeType": "RoundedCornerShape", + "size": 50 + }, + "slideBoxProperty": { + "margin": { + "start": 4, + "end": 4 + }, + "clipData": { + "shapeType": "RoundedCornerShape", + "size": 50 + }, + "contentAlignment": "Center", + "shape": { + "shapeType": "RoundedCornerShape", + "size": 50 + } + }, + "textProperty": { + "layoutId": "hintText", + "height": "WRAP_CONTENT", + "width": "MATCH_PARENT", + "fontSize": 14, + "textAlign": "Center", + "fontFamily": "ttComposeFontFamily", + "fontWeight": "TT_SEMI_BOLD" + } + } + } + ], + "data": { + "invalidStateIcon": { + "viewType": "Image", + "iconUrl": "https://firebasestorage.googleapis.com/v0/b/getslot-ccda7.appspot.com/o/chevron_right_white.jpg?alt=media&token=7deed556-d68b-4167-982c-29003db6c21a&_gl=1*kbkn65*_ga*NTM4NDA3Njc0LjE2OTA4Nzg5NTQ.*_ga_CW55HF8NVT*MTY5NzEwNzQwMS4xNS4xLjE2OTcxMDc0OTcuNjAuMC4w" + }, + "validStateIcon": { + "viewType": "Image", + "iconUrl": "https://firebasestorage.googleapis.com/v0/b/getslot-ccda7.appspot.com/o/chevron_right.png?alt=media&token=db8918be-0434-4381-a22f-e86115d9366d&_gl=1*1tuyy3i*_ga*NTM4NDA3Njc0LjE2OTA4Nzg5NTQ.*_ga_CW55HF8NVT*MTY5NzEwNzQwMS4xNS4xLjE2OTcxMDc2MDIuMzEuMC4w" + }, + "completedStateIcon": { + "viewType": "Image", + "iconUrl": "https://firebasestorage.googleapis.com/v0/b/getslot-ccda7.appspot.com/o/tickcheck.png?alt=media&token=9a48ce76-4a97-4067-9ce7-e4614736e293&_gl=1*am0qkb*_ga*NTM4NDA3Njc0LjE2OTA4Nzg5NTQ.*_ga_CW55HF8NVT*MTY5NzEwNzQwMS4xNS4xLjE2OTcxMDc1NzMuNjAuMC4w" + }, + "loadingLottie": { + "viewType": "Lottie", + "lottieUrl": "https://firebasestorage.googleapis.com/v0/b/getslot-ccda7.appspot.com/o/cta_loader.json?alt=media&token=8984e674-5e9a-48bd-a58f-0ea01f2784ce&_gl=1*9jntik*_ga*NTM4NDA3Njc0LjE2OTA4Nzg5NTQ.*_ga_CW55HF8NVT*MTY5NzE3ODE2MC4xNi4xLjE2OTcxNzgxODkuMzEuMC4w", + "placeholderIcon": "test_icon" + }, + "hintText": { + "viewType": "Text", + "text": "Slide to confirm purchase" + }, + "slideToActButton": { + "viewType": "SlideToActButton", + "text": "UITron data is here" + } + } } } \ No newline at end of file diff --git a/navi-uitron/src/main/java/com/navi/uitron/deserializer/ComposePropertyDeserializer.kt b/navi-uitron/src/main/java/com/navi/uitron/deserializer/ComposePropertyDeserializer.kt index 7ed658e..92095e6 100644 --- a/navi-uitron/src/main/java/com/navi/uitron/deserializer/ComposePropertyDeserializer.kt +++ b/navi-uitron/src/main/java/com/navi/uitron/deserializer/ComposePropertyDeserializer.kt @@ -136,6 +136,9 @@ class ComposePropertyDeserializer : JsonDeserializer { ComposeViewType.OtpBox.name -> { context?.deserialize(jsonObject, OtpBoxProperty::class.java) } + ComposeViewType.SlideToActButton.name -> { + context?.deserialize(jsonObject, SlideToActButtonProperty::class.java) + } else -> null } } diff --git a/navi-uitron/src/main/java/com/navi/uitron/deserializer/UiTronDataDeserializer.kt b/navi-uitron/src/main/java/com/navi/uitron/deserializer/UiTronDataDeserializer.kt index ad29087..835aa0a 100644 --- a/navi-uitron/src/main/java/com/navi/uitron/deserializer/UiTronDataDeserializer.kt +++ b/navi-uitron/src/main/java/com/navi/uitron/deserializer/UiTronDataDeserializer.kt @@ -105,6 +105,9 @@ open class UiTronDataDeserializer : JsonDeserializer { ComposeViewType.JackpotTextV2.name -> { context?.deserialize(jsonObject, JackpotTextData::class.java) } + ComposeViewType.SlideToActButton.name -> { + context?.deserialize(jsonObject, SlideToActButtonData::class.java) + } else -> null } } diff --git a/navi-uitron/src/main/java/com/navi/uitron/model/data/SlideToActButtonData.kt b/navi-uitron/src/main/java/com/navi/uitron/model/data/SlideToActButtonData.kt new file mode 100644 index 0000000..62319a7 --- /dev/null +++ b/navi-uitron/src/main/java/com/navi/uitron/model/data/SlideToActButtonData.kt @@ -0,0 +1,19 @@ +package com.navi.uitron.model.data + +/** + * Copyright © 2022 by Navi Technologies Private Limited + * All rights reserved. Strictly confidential + */ +data class SlideToActButtonData( + var text: String? = null, + var onSlideComplete: UiTronActionData? = null, + var onInitiateButton: UiTronActionData? = null +) : UiTronData() { + + fun copyNonNull(data: SlideToActButtonData?): SlideToActButtonData { + text = data?.text + onSlideComplete = data?.onSlideComplete + onInitiateButton = data?.onInitiateButton + return this + } +} diff --git a/navi-uitron/src/main/java/com/navi/uitron/model/ui/SlideToActButtonProperty.kt b/navi-uitron/src/main/java/com/navi/uitron/model/ui/SlideToActButtonProperty.kt new file mode 100644 index 0000000..a98cbb8 --- /dev/null +++ b/navi-uitron/src/main/java/com/navi/uitron/model/ui/SlideToActButtonProperty.kt @@ -0,0 +1,52 @@ +/* + * + * * Copyright © 2023 by Navi Technologies Limited + * * All rights reserved. Strictly confidential + * + */ + + +package com.navi.uitron.model.ui + +import com.navi.uitron.render.SlideToActButtonRenderer + +data class SlideToActButtonProperty( + var buttonState: String? = SlideToActButtonRenderer.SlideToActState.INVALID.name, + var circleButtonSize: Int? = null, + var buttonStateIcon: SlideToActIconData? = null, + var backgroundColorWithState: SlideToActBackgroundColor? = null, + var fractionalThreshold: Float? = null, + var sliderButtonPadding: ComposePadding? = null, + var shimmerColors: List? = null, + var loadingStateLottie: UiTronView? = null, + var hintContent: UiTronView? = null, + var slideButtonBgColor: SlideButtonBackgroundColor? = null, + var slideBoxProperty: BoxProperty? = null, + var textProperty: TextProperty? = null +) : BaseProperty(){ + override fun copyNonNullFrom(property: BaseProperty?) { + super.copyNonNullFrom(property) + val slideToActButtonProperty = property as? SlideToActButtonProperty + slideToActButtonProperty?.buttonStateIcon?.let { buttonStateIcon = it } + slideToActButtonProperty?.backgroundColorWithState?.let { backgroundColorWithState = it } + slideToActButtonProperty?.fractionalThreshold?.let { fractionalThreshold = it } + slideToActButtonProperty?.sliderButtonPadding?.let { sliderButtonPadding = it } + slideToActButtonProperty?.shimmerColors?.let { shimmerColors = it } + slideToActButtonProperty?.loadingStateLottie?.let { loadingStateLottie = it } + slideToActButtonProperty?.hintContent?.let { hintContent = it } + slideToActButtonProperty?.slideButtonBgColor?.let { slideButtonBgColor = it } + slideToActButtonProperty?.slideBoxProperty?.let { slideBoxProperty = it } + slideToActButtonProperty?.textProperty?.let { textProperty = it } + slideToActButtonProperty?.buttonState?.let { buttonState = it } + } + + companion object { + const val SLIDER_STATE_SUFFIX = "_slider_state" + } +} + +data class SlideToActIconData(val invalidIcon: UiTronView, val validIcon: UiTronView, val completedIcon: UiTronView) + +data class SlideToActBackgroundColor(val invalidColor: String?, val validColor: String?, val loadingColor: String?) + +data class SlideButtonBackgroundColor(val invalidColor: String?, val validColor: String?) \ No newline at end of file diff --git a/navi-uitron/src/main/java/com/navi/uitron/model/ui/UiTronView.kt b/navi-uitron/src/main/java/com/navi/uitron/model/ui/UiTronView.kt index c163417..2587e38 100644 --- a/navi-uitron/src/main/java/com/navi/uitron/model/ui/UiTronView.kt +++ b/navi-uitron/src/main/java/com/navi/uitron/model/ui/UiTronView.kt @@ -229,6 +229,7 @@ enum class ComposeViewType { AutoScrollView, CustomTextField, OtpBox, + SlideToActButton } enum class HorizontalArrangementType { diff --git a/navi-uitron/src/main/java/com/navi/uitron/render/SlideToActButtonRenderer.kt b/navi-uitron/src/main/java/com/navi/uitron/render/SlideToActButtonRenderer.kt new file mode 100644 index 0000000..aecfe06 --- /dev/null +++ b/navi-uitron/src/main/java/com/navi/uitron/render/SlideToActButtonRenderer.kt @@ -0,0 +1,420 @@ +package com.navi.uitron.render + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.animateColorAsState +import androidx.compose.animation.core.RepeatMode +import androidx.compose.animation.core.animateFloat +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.animation.core.infiniteRepeatable +import androidx.compose.animation.core.rememberInfiniteTransition +import androidx.compose.animation.core.tween +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.foundation.background +import androidx.compose.foundation.gestures.Orientation +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.FractionalThreshold +import androidx.compose.material.SwipeableState +import androidx.compose.material.Text +import androidx.compose.material.rememberSwipeableState +import androidx.compose.material.swipeable +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.shadow +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.layoutId +import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.IntSize +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.compose.ui.zIndex +import clipContent +import com.navi.uitron.UiTronSdkManager +import com.navi.uitron.model.action.MvelAction +import com.navi.uitron.model.data.SlideToActButtonData +import com.navi.uitron.model.data.UiTronData +import com.navi.uitron.model.ui.SlideToActButtonProperty +import com.navi.uitron.model.ui.UiTronView +import com.navi.uitron.utils.DEFAULT_INVALID_COLOR +import com.navi.uitron.utils.DEFAULT_LOADING_COLOR +import com.navi.uitron.utils.DEFAULT_VALID_COLOR +import com.navi.uitron.utils.KEY_MVEL_ACTION +import com.navi.uitron.utils.KEY_PROPERTY +import com.navi.uitron.utils.KEY_UI_TRON_DATA +import com.navi.uitron.utils.UI_TRON_VM +import com.navi.uitron.viewmodel.UiTronViewModel +import getContentAlignment +import getSliderStateId +import getTextAlignment +import hexToComposeColor +import orFalse +import orTrue +import setBackground +import setHeight +import setPadding +import kotlin.math.roundToInt + +class SlideToActButtonRenderer( + private val childrenComposeViews: List, + private val uiTronData: UiTronData?, + private val uiTronRenderer: UiTronRenderer +) : Renderer { + private val defaultFontSize = 14.sp + private val defaultButtonSize = 40.dp + private val defaultTargetValue = 2000f + private val defaultFractionalThreshold = 0.5F + private val defaultShadowElevation = 10.dp + private val defaultShimmerAnimationTime = 4000 + private val defaultFullVisibility = 1f + private val defaultPartialVisibility = 0.3f + private val defaultNoVisibility = 0f + private val defaultVisibilityTimeOfText = 1000 + private val defaultBackgroundColorVisibilityTimer = 500 + + @Composable + override fun Render( + property: SlideToActButtonProperty, + uiTronData: UiTronData?, + uiTronViewModel: UiTronViewModel, + modifier: Modifier? + ){ + super.Render(property, uiTronData, uiTronViewModel, modifier) + var uiTronSlideToActButtonData = uiTronData as? SlideToActButtonData + if (property.isStateFul.orFalse()) { + uiTronViewModel.addKeyToSavedStateHandle(property.layoutId.orEmpty()) + + val viewState = uiTronViewModel.handle.getStateFlow( + property.getPropertyId(), + null + ).collectAsState() + property.copyNonNullFrom(property.statesMap?.get(viewState.value)) + } + + if(property.isDataMutable.orFalse()){ + val dataState = uiTronViewModel.handle.getStateFlow( + property.getDataId(), + null + ).collectAsState() + uiTronSlideToActButtonData = uiTronSlideToActButtonData?.copyNonNull(dataState.value) ?: dataState.value + } + val extrasState = uiTronViewModel.handle.getStateFlow?>( + property.getExtrasId(), + null + ).collectAsState() + + handleMvelExtra(property.layoutId, extrasState.value, mapOf( + Pair(KEY_PROPERTY, property), + Pair(KEY_UI_TRON_DATA, uiTronSlideToActButtonData), + Pair(KEY_MVEL_ACTION, extrasState.value?.get(MvelAction.KEY_MVEL) as? MvelAction), + Pair(UI_TRON_VM, uiTronViewModel) + )) + + SwipeButton( + modifier= Modifier + .fillMaxWidth() + .setHeight(property.height) + .setPadding(property.margin) + .clipContent(property.clipData) + , + onSwipe = { + uiTronViewModel.handleActions(uiTronSlideToActButtonData?.onSlideComplete) + }, + property = property, + uiTronData = uiTronSlideToActButtonData, + uiTronViewModel = uiTronViewModel + ) + } + + @OptIn(ExperimentalMaterialApi::class) + @Composable + fun SwipeButton( + modifier: Modifier = Modifier, + onSwipe: () -> Unit, + property: SlideToActButtonProperty, + uiTronData: SlideToActButtonData?, + uiTronViewModel: UiTronViewModel, + ) { + var size by remember { + mutableStateOf(IntSize.Zero) + } + + val density = LocalDensity.current.density + + val widthInPx = with(LocalDensity.current) { + ( + (size.width/density).dp - + (((property.circleButtonSize ?: defaultButtonSize.value.roundToInt())) + + ((property.slideBoxProperty?.margin?.start?: 0)*2)).dp).toPx() + } + + val anchors = mapOf( + 0F to 0, + widthInPx to 1, + ) + val swipeableState = rememberSwipeableState(0) + val (swipeComplete, setSwipeComplete) = remember { + mutableStateOf(false) + } + + val sliderState = uiTronViewModel.handle.getStateFlow( + property.getPropertyId().getSliderStateId(), + SlideToActState.INVALID.name + ).collectAsState() + + var showShimmer by remember { + mutableStateOf(false) + } + + when(property.buttonState){ + SlideToActState.INVALID.name -> { + uiTronViewModel.handle[property.getPropertyId().getSliderStateId()] = SlideToActState.INVALID.name + showShimmer = false + } + SlideToActState.READY.name -> { + uiTronViewModel.handle[property.getPropertyId().getSliderStateId()] = SlideToActState.READY.name + showShimmer = true + } + SlideToActState.LOADING.name -> { + uiTronViewModel.handle[property.getPropertyId().getSliderStateId()] = SlideToActState.LOADING.name + } + SlideToActState.COMPLETED.name -> { + uiTronViewModel.handle[property.getPropertyId().getSliderStateId()] = SlideToActState.COMPLETED.name + } + else -> { + uiTronViewModel.handle[property.getPropertyId().getSliderStateId()] = SlideToActState.INVALID.name + showShimmer = false + } + } + + val visibility by animateFloatAsState( + targetValue = if(swipeableState.offset.value.roundToInt()<(widthInPx*0.4)){ + defaultFullVisibility + } else if(sliderState.value == SlideToActState.LOADING.name){ + defaultNoVisibility + } else{ + defaultPartialVisibility + }, + label = "", + animationSpec = tween( + durationMillis = defaultVisibilityTimeOfText) + ) + + val backgroundColor by animateColorAsState( + targetValue = when(sliderState.value) { + SlideToActState.INVALID.name -> property.backgroundColorWithState?.invalidColor?.hexToComposeColor ?: DEFAULT_INVALID_COLOR.hexToComposeColor + SlideToActState.READY.name -> property.backgroundColorWithState?.validColor?.hexToComposeColor?: DEFAULT_VALID_COLOR.hexToComposeColor + SlideToActState.LOADING.name -> property.backgroundColorWithState?.loadingColor?.hexToComposeColor ?: DEFAULT_LOADING_COLOR.hexToComposeColor + SlideToActState.COMPLETED.name -> property.backgroundColorWithState?.validColor?.hexToComposeColor?: DEFAULT_VALID_COLOR.hexToComposeColor + else -> property.backgroundColorWithState?.invalidColor?.hexToComposeColor ?: DEFAULT_INVALID_COLOR.hexToComposeColor + }, + label = "", + animationSpec = tween( + durationMillis = defaultBackgroundColorVisibilityTimer + ) + ) + + + LaunchedEffect( + key1 = swipeableState.currentValue, + ) { + if (swipeableState.currentValue == 1) { + setSwipeComplete(true) + onSwipe() + } + } + + Box( + contentAlignment = Alignment.Center, + modifier = modifier + .onGloballyPositioned { + size = it.size + } + .background(backgroundColor) + .setPadding(property.padding) + ) { + SwipeRight( + modifier = Modifier + .align(Alignment.CenterStart) + .offset { + IntOffset(swipeableState.offset.value.roundToInt(), 0) + } + .swipeable( + state = swipeableState, + anchors = anchors, + thresholds = { _, _ -> + FractionalThreshold(property.fractionalThreshold ?: defaultFractionalThreshold) + }, + orientation = Orientation.Horizontal, + enabled = (swipeableState.currentValue != 1 && sliderState.value != SlideToActState.INVALID.name) + ) + .setPadding(property.slideBoxProperty?.margin) + .zIndex(2f) + , + sliderState.value ?: SlideToActState.INVALID.name, + swipeableState, + widthInPx, + property + ) + Text( + text = uiTronData?.text.orEmpty(), + textAlign = getTextAlignment(property.textProperty?.textAlign), + fontFamily = UiTronSdkManager.getDependencyProvider() + .getFontFamily(property.textProperty?.fontFamily), + fontWeight = UiTronSdkManager.getDependencyProvider() + .getFontWeight(property.textProperty?.fontWeight), + fontSize = property.textProperty?.fontSize?.sp ?: defaultFontSize, + modifier = Modifier + .fillMaxWidth() + .setHeight(property.textProperty?.height) + .setPadding(property.textProperty?.margin) + .setBackground( + property.textProperty?.backgroundColor, + property.textProperty?.shape, + property.textProperty?.backGroundBrushData + ) + .setPadding(property.textProperty?.padding) + .layoutId(property.layoutId.orEmpty()) + .alpha(visibility), + style = TextStyle( + brush = shimmerBrush(showShimmer = showShimmer, property.shimmerColors) + ) + ) + AnimatedVisibility( + visible = (swipeComplete && property.buttonState?.equals(SlideToActState.COMPLETED.name).orTrue().not()), + enter = fadeIn(), + exit = fadeOut() + ) { + uiTronViewModel.handle[property.getPropertyId().getSliderStateId()] = SlideToActState.LOADING.name + property.loadingStateLottie?.let { + uiTronRenderer.Render(composeViews = listOf(it)) + } + } + } + } + + @OptIn(ExperimentalMaterialApi::class) + @Composable + fun SwipeRight( + modifier: Modifier = Modifier, + sliderState: String, + swipeableState: SwipeableState, + width: Float, + property: SlideToActButtonProperty, + + ) { + val fadeIn by animateFloatAsState( + targetValue = swipeableState.offset.value.roundToInt()/width + , label = "" + ) + + val fadeOut by animateFloatAsState( + targetValue = 1f - swipeableState.offset.value.roundToInt()/width + , label = "" + ) + Box( + contentAlignment = getContentAlignment(contentAlignment = property.slideBoxProperty?.contentAlignment), + modifier = modifier + .size(property.circleButtonSize?.dp ?: defaultButtonSize) + .shadow( + elevation = defaultShadowElevation, + shape = CircleShape, + spotColor = Color.Black + ) + .setBackground( + if (sliderState == SlideToActState.INVALID.name) { + property.slideButtonBgColor?.invalidColor + } else { + property.slideButtonBgColor?.validColor + }, + property.slideBoxProperty?.shape, + property.slideBoxProperty?.backGroundBrushData + ) + .clip(CircleShape) + .setPadding(property.slideBoxProperty?.padding), + ) { + AnimatedVisibility(visible = sliderState == SlideToActState.INVALID.name, + enter = fadeIn(), + exit = fadeOut() + ) { + property.buttonStateIcon?.invalidIcon?.let { + uiTronRenderer.Render(composeViews = listOf(it)) + } + } + + AnimatedVisibility(visible = sliderState == SlideToActState.READY.name, + enter = fadeIn(), + exit = fadeOut() + ) { + property.buttonStateIcon?.validIcon?.let { + it.property?.alpha = fadeOut + uiTronRenderer.Render(composeViews = listOf(it)) + } + } + + property.buttonStateIcon?.completedIcon?.let { + it.property?.alpha = fadeIn + uiTronRenderer.Render(composeViews = listOf(it)) + } + } + } + + @Composable + fun shimmerBrush(showShimmer: Boolean = true, shimmerColors: List?, targetValue:Float = defaultTargetValue): Brush { + return if (showShimmer && shimmerColors.isNullOrEmpty().not()) { + var shimmerColorList = listOf() + shimmerColors?.let { + shimmerColorList = shimmerColors.map { + it.hexToComposeColor + } + } + + val transition = rememberInfiniteTransition(label = "") + val translateAnimation = transition.animateFloat( + initialValue = 0f, + targetValue = targetValue, + animationSpec = infiniteRepeatable( + animation = tween(defaultShimmerAnimationTime), + repeatMode = RepeatMode.Restart + ), + label = "" + ) + Brush.linearGradient( + colors = shimmerColorList, + start = Offset.Zero, + end = Offset(x = translateAnimation.value, y = translateAnimation.value) + ) + } else { + Brush.linearGradient( + colors = listOf(Color.White, Color.White), + start = Offset.Zero, + end = Offset.Zero + ) + } + } + + enum class SlideToActState{ + INVALID, + READY, + LOADING, + COMPLETED + } +} \ No newline at end of file diff --git a/navi-uitron/src/main/java/com/navi/uitron/render/UiTronRenderer.kt b/navi-uitron/src/main/java/com/navi/uitron/render/UiTronRenderer.kt index c7fb45b..f0e9e73 100644 --- a/navi-uitron/src/main/java/com/navi/uitron/render/UiTronRenderer.kt +++ b/navi-uitron/src/main/java/com/navi/uitron/render/UiTronRenderer.kt @@ -489,6 +489,25 @@ class UiTronRenderer( ) } } + ComposeViewType.SlideToActButton.name -> { + (composeView.property as? SlideToActButtonProperty)?.let { + val initialData = dataMap?.getOrElse( + it.layoutId.orEmpty() + ){ null } + SlideToActButtonRenderer( + childrenComposeViews = composeView.childrenViews.orEmpty(), + uiTronData = dataMap?.getOrElse(it.layoutId.orEmpty()) { + null + }, + uiTronRenderer = this + ).Render( + property = it, + uiTronData = initialData, + uiTronViewModel = uiTronViewModel, + modifier = modifier + ) + } + } } } } diff --git a/navi-uitron/src/main/java/com/navi/uitron/utils/Constants.kt b/navi-uitron/src/main/java/com/navi/uitron/utils/Constants.kt index 0ee0a46..157e9c9 100644 --- a/navi-uitron/src/main/java/com/navi/uitron/utils/Constants.kt +++ b/navi-uitron/src/main/java/com/navi/uitron/utils/Constants.kt @@ -23,4 +23,7 @@ const val RESULT = "result" const val CUSTOM_TEXT_FIELD_DATA ="customTextFieldData" const val CUSTOM_TEXT_FIELD_PROPERTY ="customTextFieldProperty" const val UI_TRON_VM ="uiTronVM" -const val HIDE = "HIDE" \ No newline at end of file +const val HIDE = "HIDE" +const val DEFAULT_INVALID_COLOR = "#B5ACB9" +const val DEFAULT_VALID_COLOR = "#1F002A" +const val DEFAULT_LOADING_COLOR = "#6E5876" \ No newline at end of file diff --git a/navi-uitron/src/main/java/com/navi/uitron/utils/Ext.kt b/navi-uitron/src/main/java/com/navi/uitron/utils/Ext.kt index c8eb209..dc42c0d 100644 --- a/navi-uitron/src/main/java/com/navi/uitron/utils/Ext.kt +++ b/navi-uitron/src/main/java/com/navi/uitron/utils/Ext.kt @@ -54,6 +54,7 @@ import com.navi.uitron.model.ui.BaseProperty.Companion.DATA_SUFFIX import com.navi.uitron.model.ui.BaseProperty.Companion.EXTRAS_SUFFIX import com.navi.uitron.model.ui.BaseProperty.Companion.INPUT_SUFFIX import com.navi.uitron.model.ui.BaseProperty.Companion.PROPERTY_SUFFIX +import com.navi.uitron.model.ui.SlideToActButtonProperty.Companion.SLIDER_STATE_SUFFIX import com.navi.uitron.utils.* import com.navi.uitron.utils.transformations.* import kotlinx.coroutines.flow.MutableSharedFlow @@ -700,6 +701,8 @@ fun String?.getInputId() = this.orEmpty() + INPUT_SUFFIX fun String?.getExtrasId() = this.orEmpty() + EXTRAS_SUFFIX +fun String?.getSliderStateId() = this.orEmpty() + SLIDER_STATE_SUFFIX + fun Modifier.conditional(condition : Boolean, modifier : Modifier.() -> Modifier) : Modifier { return if (condition) {