diff --git a/android/app/src/main/java/com/naviapp/home/compose/home/ui/content/FrontLayerContent.kt b/android/app/src/main/java/com/naviapp/home/compose/home/ui/content/FrontLayerContent.kt index 6a803cab59..861c732db9 100644 --- a/android/app/src/main/java/com/naviapp/home/compose/home/ui/content/FrontLayerContent.kt +++ b/android/app/src/main/java/com/naviapp/home/compose/home/ui/content/FrontLayerContent.kt @@ -14,6 +14,7 @@ import androidx.compose.animation.fadeOut import androidx.compose.animation.shrinkVertically import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.LocalOverscrollConfiguration +import androidx.compose.foundation.ScrollState import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -39,9 +40,8 @@ import com.airbnb.lottie.compose.LottieAnimation import com.airbnb.lottie.compose.LottieCompositionSpec import com.airbnb.lottie.compose.LottieConstants import com.airbnb.lottie.compose.rememberLottieComposition -import com.navi.common.uitron.render.CommonCustomUiTronRenderer import com.navi.naviwidgets.R -import com.navi.uitron.render.UiTronRenderer +import com.naviapp.home.compose.widgetfactory.HomeWidgetRenderer import com.naviapp.home.model.WidgetUiState import com.naviapp.home.utils.getHomeWidgetAnimationSpec import com.naviapp.home.viewmodel.HomeVM @@ -60,7 +60,12 @@ typealias WidgetId = String */ @Composable @OptIn(ExperimentalFoundationApi::class, ExperimentalComposeUiApi::class) -fun FrontLayerContent(modifier: Modifier, isShimmerVisible: Boolean, homeVM: HomeVM) { +fun FrontLayerContent( + modifier: Modifier, + isShimmerVisible: Boolean, + homeVM: HomeVM, + homeScrollState: () -> ScrollState +) { CompositionLocalProvider(LocalOverscrollConfiguration provides null) { Column( modifier.semantics { testTagsAsResourceId = true }.testTag("homeScreenWidgetsList") @@ -69,26 +74,33 @@ fun FrontLayerContent(modifier: Modifier, isShimmerVisible: Boolean, homeVM: Hom val widgetOrderList = homeVM.getHomeContentList() Column { UpperContent(homeVM) { - RenderUiTronContent(widgetOrderList.take(homeVM.upperContentSize), homeVM) + RenderUiTronContent( + widgetOrderList.take(homeVM.upperContentSize), + homeVM, + homeScrollState + ) } val middleAndLowerContent = widgetOrderList.drop(homeVM.upperContentSize) UpperMiddleContent(homeVM) { RenderUiTronContent( middleAndLowerContent.take(Constants.SECTION_CONTENT_SIZE), - homeVM + homeVM, + homeScrollState ) } val lowerContent = middleAndLowerContent.drop(Constants.SECTION_CONTENT_SIZE) LowerMiddleContent(homeVM) { RenderUiTronContent( lowerContent.take(Constants.SECTION_CONTENT_SIZE), - homeVM + homeVM, + homeScrollState ) } LowerContent(homeVM) { RenderUiTronContent( lowerContent.drop(Constants.SECTION_CONTENT_SIZE), - homeVM + homeVM, + homeScrollState ) } ContentLoaderLottie(homeVM = homeVM) @@ -201,7 +213,11 @@ fun ContentLoaderLottie(homeVM: HomeVM) { } @Composable -private fun RenderUiTronContent(elementList: List, homeVM: HomeVM) { +private fun RenderUiTronContent( + elementList: List, + homeVM: HomeVM, + homeScrollState: () -> ScrollState +) { elementList.forEach { element -> key(element) { val widgetUiMap = homeVM.getWidgetUiStateMap() @@ -217,12 +233,12 @@ private fun RenderUiTronContent(elementList: List, homeVM: HomeVM) { } AnimatedContainerForWidgets(isVisible = visible) { val response = homeVM.getWidgetUiStateMap()[element]?.widgetData - UiTronRenderer( - dataMap = response?.data, - uiTronViewModel = homeVM, - customUiTronRenderer = CommonCustomUiTronRenderer() - ) - .Render(composeViews = response?.parentComposeView.orEmpty()) + HomeWidgetRenderer( + widgetData = response?.data, + viewModel = homeVM, + composeView = response?.parentComposeView.orEmpty(), + homeScrollState = homeScrollState + ) } } } diff --git a/android/app/src/main/java/com/naviapp/home/compose/home/ui/screen/HomeScreen.kt b/android/app/src/main/java/com/naviapp/home/compose/home/ui/screen/HomeScreen.kt index 5c2bf32880..7ba32be1c4 100644 --- a/android/app/src/main/java/com/naviapp/home/compose/home/ui/screen/HomeScreen.kt +++ b/android/app/src/main/java/com/naviapp/home/compose/home/ui/screen/HomeScreen.kt @@ -162,7 +162,8 @@ private fun HomeScreenScaffoldRoot( .background(Color.White, frontLayerShape) .verticalScroll(homeScrollState), isShimmerVisible = widgetResponse.contentWidget.isNullOrEmpty(), - homeVM = homeVM + homeVM = homeVM, + homeScrollState = { homeScrollState } ) } ) diff --git a/android/app/src/main/java/com/naviapp/home/compose/uiTron/helper/RotatingViewHelper.kt b/android/app/src/main/java/com/naviapp/home/compose/uiTron/helper/RotatingViewHelper.kt new file mode 100644 index 0000000000..4c42e0b0a3 --- /dev/null +++ b/android/app/src/main/java/com/naviapp/home/compose/uiTron/helper/RotatingViewHelper.kt @@ -0,0 +1,84 @@ +/* + * + * * Copyright © 2024 by Navi Technologies Limited + * * All rights reserved. Strictly confidential + * + */ + +package com.naviapp.home.compose.uiTron.helper + +import androidx.compose.foundation.ScrollState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.MutableIntState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.platform.LocalLifecycleOwner +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleEventObserver +import javax.inject.Inject +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.withContext + +class RotatingViewHelper @Inject constructor() { + + private val mapOfRotatingViewIndexes = mutableMapOf() + + private fun getIndex(layoutId: String): Int = mapOfRotatingViewIndexes.getOrElse(layoutId) { 0 } + + private fun updateIndex(layoutId: String, index: Int) { + mapOfRotatingViewIndexes[layoutId] = index + } + + @Composable + fun currentIndexToDisplay( + layoutId: String, + numberOfChildren: Int, + delayDuration: Long, + homeScrollState: () -> ScrollState + ): MutableIntState { + return if (layoutId.isEmpty().not()) { + var shouldPlayOnLifecycle by remember { mutableStateOf(false) } + val currentIndex = remember { mutableIntStateOf(getIndex(layoutId)) } + val lifecycleOwner = LocalLifecycleOwner.current + if (homeScrollState().isScrollInProgress.not() && shouldPlayOnLifecycle) { + LaunchedEffect(Unit) { + while (true) { + withContext(Dispatchers.IO) { + delay(delayDuration.div(2)) + currentIndex.intValue = (currentIndex.intValue + 1) % numberOfChildren + updateIndex(layoutId, currentIndex.intValue) + delay(delayDuration.div(2)) + } + } + } + } + DisposableEffect(lifecycleOwner) { + val lifecycle = lifecycleOwner.lifecycle + val observer = LifecycleEventObserver { _, event -> + shouldPlayOnLifecycle = + when (event) { + Lifecycle.Event.ON_RESUME -> { + true + } + Lifecycle.Event.ON_STOP -> { + false + } + else -> { + false + } + } + } + lifecycle.addObserver(observer) + + onDispose { lifecycle.removeObserver(observer) } + } + currentIndex + } else remember { mutableIntStateOf(0) } + } +} diff --git a/android/app/src/main/java/com/naviapp/home/compose/uiTron/renderer/RotatingViewRenderer.kt b/android/app/src/main/java/com/naviapp/home/compose/uiTron/renderer/RotatingViewRenderer.kt index d323fa0bed..036a029339 100644 --- a/android/app/src/main/java/com/naviapp/home/compose/uiTron/renderer/RotatingViewRenderer.kt +++ b/android/app/src/main/java/com/naviapp/home/compose/uiTron/renderer/RotatingViewRenderer.kt @@ -13,10 +13,6 @@ import androidx.compose.animation.slideOutVertically import androidx.compose.animation.togetherWith import androidx.compose.foundation.ScrollState import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.MutableIntState -import androidx.compose.runtime.mutableIntStateOf -import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.layout.layoutId @@ -39,7 +35,8 @@ import com.navi.uitron.utils.setWidth import com.navi.uitron.utils.setWidthRange import com.navi.uitron.viewmodel.UiTronViewModel import com.naviapp.home.compose.uiTron.model.viewProperties.RotatingViewProperty -import kotlinx.coroutines.delay +import com.naviapp.home.viewmodel.HomeVM +import com.naviapp.utils.Constants.HomeCustomUitronWidgetConstants.ROTATING_VIEW_ANIMATION class RotatingViewRenderer( private val childrenComposeViews: List, @@ -87,10 +84,16 @@ class RotatingViewRenderer( RotatingView( modifier = viewModifier, indexValue = { - currentIndexToDisplay( - duration = { property.delay ?: 2000L }, - homeScrollState = homeScrollState - ) + (uiTronViewModel as HomeVM) + .rotatingViewHelper + .get() + .currentIndexToDisplay( + layoutId = property.layoutId.orEmpty(), + numberOfChildren = childrenComposeViews.size, + delayDuration = property.delay ?: 2000L, + homeScrollState = homeScrollState + ) + .intValue }, viewToRender = { uiTronRenderer.Render(composeViews = listOf(it)) } ) @@ -100,34 +103,16 @@ class RotatingViewRenderer( @Composable private fun RotatingView( modifier: Modifier = Modifier, - indexValue: @Composable () -> MutableIntState, + indexValue: @Composable () -> Int, viewToRender: @Composable (UiTronView) -> Unit ) { AnimatedContent( modifier = modifier, - targetState = childrenComposeViews[indexValue().intValue], + targetState = indexValue(), transitionSpec = { slideInVertically { it } togetherWith slideOutVertically { -it } }, - label = "" - ) { view -> - viewToRender(view) + label = ROTATING_VIEW_ANIMATION + ) { index -> + viewToRender(childrenComposeViews[index]) } } - - @Composable - private fun currentIndexToDisplay( - duration: () -> Long, - homeScrollState: () -> ScrollState - ): MutableIntState { - val currentIndex = remember { mutableIntStateOf(0) } - if (homeScrollState().isScrollInProgress.not()) { - LaunchedEffect(Unit) { - while (true) { - delay(duration().div(2)) - currentIndex.intValue = (currentIndex.intValue + 1) % childrenComposeViews.size - delay(duration().div(2)) - } - } - } - return remember { currentIndex } - } } diff --git a/android/app/src/main/java/com/naviapp/home/viewmodel/HomeVM.kt b/android/app/src/main/java/com/naviapp/home/viewmodel/HomeVM.kt index 941a24b986..f00fe21c02 100644 --- a/android/app/src/main/java/com/naviapp/home/viewmodel/HomeVM.kt +++ b/android/app/src/main/java/com/naviapp/home/viewmodel/HomeVM.kt @@ -88,6 +88,7 @@ import com.naviapp.home.compose.listener.HomeScreenCallbackListener import com.naviapp.home.compose.model.BottomStickyNudgeState import com.naviapp.home.compose.model.HomePageExtraData import com.naviapp.home.compose.model.InitiatePaymentFromComposeData +import com.naviapp.home.compose.uiTron.helper.RotatingViewHelper import com.naviapp.home.model.BottomBarTabType import com.naviapp.home.model.HomeCtaTypes import com.naviapp.home.respository.HomeRepository @@ -109,6 +110,7 @@ import com.naviapp.utils.Constants.OFFER_VIEWED import com.naviapp.utils.LOAN_ACCOUNT_NUMBER import com.naviapp.utils.NOTIFICATION_PERMISSION_SHOWN import com.naviapp.utils.naviAppSerializerGsonBuilder +import dagger.Lazy import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject import kotlinx.coroutines.CoroutineScope @@ -140,6 +142,7 @@ constructor( private val naviPayManager: NaviPayManager, private val selectiveRefreshHandler: SelectiveRefreshHandler, private val homePageDataUpdateHandler: HomePageDataUpdateHandler, + val rotatingViewHelper: Lazy, val nuxHandler: NewUserExperienceHandler ) : BaseVM() { diff --git a/android/app/src/main/java/com/naviapp/utils/Constants.kt b/android/app/src/main/java/com/naviapp/utils/Constants.kt index 3fd8900148..1be4f64eb9 100644 --- a/android/app/src/main/java/com/naviapp/utils/Constants.kt +++ b/android/app/src/main/java/com/naviapp/utils/Constants.kt @@ -513,6 +513,10 @@ object Constants { const val SCROLL_FADE = "scroll_fade" } + object HomeCustomUitronWidgetConstants { + const val ROTATING_VIEW_ANIMATION = "rotating view animation" + } + enum class JourneyType(val type: String) { POST_PURCHASE("post_purchase") }