TP-66780 | Sohan Reddy | Implementation of custom timer. (#10837)

Co-authored-by: kishan kumar <kishan.kumar@navi.com>
This commit is contained in:
Sohan Reddy Atukula
2024-05-20 20:14:21 +05:30
committed by GitHub
parent d7711a3a42
commit bc70f73c0d
14 changed files with 467 additions and 17 deletions

View File

@@ -5,11 +5,3 @@ import javax.inject.Qualifier
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class CoinRetrofit
@Qualifier
@Target(
AnnotationTarget.VALUE_PARAMETER,
AnnotationTarget.FIELD,
AnnotationTarget.FUNCTION
)
annotation class CountDownHelperQualifier

View File

@@ -13,7 +13,7 @@ import androidx.paging.PagingConfig
import androidx.paging.cachedIn
import androidx.paging.compose.LazyPagingItems
import com.navi.base.utils.orZero
import com.navi.coin.di.qualifiers.CountDownHelperQualifier
import com.navi.rr.di.qualifiers.CountDownHelperQualifier
import com.navi.coin.models.model.ScratchCard
import com.navi.coin.models.states.ScratchCardHistoryScreenState
import com.navi.coin.models.states.ScratchCardStatus
@@ -21,7 +21,7 @@ import com.navi.coin.navigator.CoinNavigationActions
import com.navi.coin.repo.pagingsource.ScratchCardHistoryListSource
import com.navi.coin.repo.repository.ScratchCardHistoryScreenRepo
import com.navi.coin.utils.constant.Constants
import com.navi.coin.utils.facade.ICountDownHelper
import com.navi.rr.utils.facade.ICountDownHelper
import com.navi.common.forge.model.ScreenDefinition
import com.navi.common.forge.model.WidgetModelDefinition
import com.navi.common.network.ApiConstants

View File

@@ -3,8 +3,10 @@ package com.navi.rr.deserializer
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonElement
import com.navi.rr.milestones.models.MilestoneDataV2
import com.navi.rr.uitron.model.data.CountDownTextData
import com.navi.rr.uitron.model.data.LeaderboardHeaderData
import com.navi.rr.uitron.model.data.LeaderboardRewardGridData
import com.navi.rr.uitron.model.ui.CountDownTextProperty
import com.navi.rr.uitron.model.ui.LeaderboardHeaderProperty
import com.navi.rr.uitron.model.ui.LeaderboardRewardGridProperty
import com.navi.rr.uitron.model.ui.RRComposeViewType
@@ -38,6 +40,10 @@ class RRComposePropertyDeserializer : ComposePropertyDeserializer() {
context?.deserialize(jsonObject, LeaderboardRewardGridProperty::class.java)
}
RRComposeViewType.CountDownText.value -> {
context?.deserialize(jsonObject, CountDownTextProperty::class.java)
}
else -> super.deserialize(json, typeOfT, context)
}
}
@@ -72,6 +78,10 @@ class RRComposeDataDeserializer : UiTronDataDeserializer() {
context?.deserialize(jsonObject, LeaderboardRewardGridData::class.java)
}
RRComposeViewType.CountDownText.value -> {
context?.deserialize(jsonObject, CountDownTextData::class.java)
}
else -> super.deserialize(json, typeOfT, context)
}
}

View File

@@ -1,8 +1,8 @@
package com.navi.coin.di.module
package com.navi.rr.di.module
import com.navi.coin.di.qualifiers.CountDownHelperQualifier
import com.navi.coin.utils.facade.CountDownFacade
import com.navi.coin.utils.facade.ICountDownHelper
import com.navi.rr.di.qualifiers.CountDownHelperQualifier
import com.navi.rr.utils.facade.CountDownFacade
import com.navi.rr.utils.facade.ICountDownHelper
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn

View File

@@ -0,0 +1,11 @@
package com.navi.rr.di.qualifiers
import javax.inject.Qualifier
@Qualifier
@Target(
AnnotationTarget.VALUE_PARAMETER,
AnnotationTarget.FIELD,
AnnotationTarget.FUNCTION,
)
annotation class CountDownHelperQualifier

View File

@@ -3,8 +3,10 @@ package com.navi.rr.serializer
import com.google.gson.JsonElement
import com.google.gson.JsonSerializationContext
import com.navi.rr.milestones.models.MilestoneDataV2
import com.navi.rr.uitron.model.data.CountDownTextData
import com.navi.rr.uitron.model.data.LeaderboardHeaderData
import com.navi.rr.uitron.model.data.LeaderboardRewardGridData
import com.navi.rr.uitron.model.ui.CountDownTextProperty
import com.navi.rr.uitron.model.ui.LeaderboardHeaderProperty
import com.navi.rr.uitron.model.ui.LeaderboardRewardGridProperty
import com.navi.rr.uitron.model.ui.RRComposeViewType
@@ -44,6 +46,13 @@ class RRComposePropertySerializer : ComposePropertySerializer() {
)
}
RRComposeViewType.CountDownText.value -> {
context?.serialize(
src as CountDownTextProperty,
CountDownTextProperty::class.java
)
}
else -> super.serialize(src, typeOfSrc, context)
}
}
@@ -75,6 +84,10 @@ class RRComposeDataSerializer : UiTronDataSerializer() {
context?.serialize(src as MilestoneDataV2, MilestoneDataV2::class.java)
}
RRComposeViewType.CountDownText.value -> {
context?.serialize(src as CountDownTextData, CountDownTextData::class.java)
}
else -> super.serialize(src, typeOfSrc, context)
}
}

View File

@@ -0,0 +1,16 @@
package com.navi.rr.uitron.model.data
import com.navi.uitron.model.data.UiTronActionData
import com.navi.uitron.model.data.UiTronData
class CountDownTextData (
val timeLeft: Long? = null,
val prefixText: String? = null,
val suffixText: String? = null,
val onFinish: UiTronActionData? = null,
val onDaysEnd: UiTronActionData? = null,
val onHoursEnd: UiTronActionData? = null,
val onMinutesEnd: UiTronActionData? = null,
val onSecondsEnd: UiTronActionData? = null,
val onTick: UiTronActionData? = null,
) : UiTronData()

View File

@@ -0,0 +1,7 @@
package com.navi.rr.uitron.model.ui
import com.navi.uitron.model.ui.TextProperty
class CountDownTextProperty (
val format: String? = null,
) : TextProperty()

View File

@@ -4,5 +4,6 @@ enum class RRComposeViewType(val value: String) {
TextWithShadow("TextWithShadow"),
LeaderboardHeader("LeaderboardHeader"),
LeaderboardRewardGrid("LeaderboardRewardGrid"),
Milestone("milestone")
Milestone("milestone"),
CountDownText("CountDownText"),
}

View File

@@ -3,10 +3,14 @@ package com.navi.rr.uitron.render
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.navi.common.uitron.render.CommonCustomUiTronRenderer
import com.navi.rr.uitron.model.ui.CountDownTextProperty
import com.navi.rr.uitron.model.ui.LeaderboardHeaderProperty
import com.navi.rr.uitron.model.ui.LeaderboardRewardGridProperty
import com.navi.rr.uitron.model.ui.RRComposeViewType
import com.navi.rr.uitron.model.ui.TextWithShadowProperty
import com.navi.rr.uitron.render.countdowntimer.TimerStrategyFactory
import com.navi.rr.uitron.render.countdowntimer.CountDownTextRenderer
import com.navi.rr.utils.facade.CountDownFacade
import com.navi.uitron.model.data.UiTronData
import com.navi.uitron.model.ui.UiTronView
import com.navi.uitron.viewmodel.UiTronViewModel
@@ -55,6 +59,17 @@ class RRCustomUiTronRenderer : CommonCustomUiTronRenderer() {
}
}
RRComposeViewType.CountDownText.name -> {
(composeView.property as? CountDownTextProperty)?.let { property ->
CountDownTextRenderer(countDownHelper = CountDownFacade(), timerStrategyFactory = TimerStrategyFactory()).Render(
property = property,
uiTronData = dataMap?.getOrElse(property.layoutId.orEmpty()) { null },
uiTronViewModel = uiTronViewModel,
modifier = Modifier
)
}
}
else -> {
super.Render(composeView, modifier, dataMap, uiTronViewModel)
}

View File

@@ -0,0 +1,100 @@
package com.navi.rr.uitron.render.countdowntimer
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import com.navi.rr.uitron.model.data.CountDownTextData
import com.navi.rr.uitron.model.ui.CountDownTextProperty
import com.navi.rr.utils.facade.ICountDownHelper
import com.navi.uitron.model.data.TextData
import com.navi.uitron.model.data.UiTronData
import com.navi.uitron.render.Renderer
import com.navi.uitron.render.TextRenderer
import com.navi.uitron.viewmodel.UiTronViewModel
import javax.inject.Inject
class CountDownTextRenderer @Inject constructor(
val countDownHelper: ICountDownHelper,
val timerStrategyFactory: TimerStrategyFactory
) : Renderer<CountDownTextProperty> {
@Composable
override fun Render(
property: CountDownTextProperty,
uiTronData: UiTronData?,
uiTronViewModel: UiTronViewModel,
modifier: Modifier?
) {
super.Render(property, uiTronData, uiTronViewModel, modifier)
val countDownTimerData = uiTronData as? CountDownTextData
var timeLeft by remember {
mutableStateOf(countDownTimerData?.timeLeft)
}
val textData by remember(timeLeft) {
mutableStateOf(
TextData(
text = buildString {
append(countDownTimerData?.prefixText.orEmpty())
append(formatTimerText(timeLeft ?: 0, property.format))
append(countDownTimerData?.suffixText.orEmpty())
}
)
)
}
fun onTick(timeLeftInLong : Long){
timeLeft = timeLeftInLong
uiTronViewModel.handleActions(countDownTimerData?.onTick)
when (timeLeft) {
ONE_DAY_MILLIS -> {
uiTronViewModel.handleActions(countDownTimerData?.onDaysEnd)
}
ONE_HOUR_MILLIS -> {
uiTronViewModel.handleActions(countDownTimerData?.onHoursEnd)
}
ONE_MINUTE_MILLIS -> {
uiTronViewModel.handleActions(countDownTimerData?.onMinutesEnd)
}
ONE_SECOND_MILLIS -> {
uiTronViewModel.handleActions(countDownTimerData?.onSecondsEnd)
}
}
}
LaunchedEffect(Unit) {
countDownHelper.cancelTimer(property.layoutId.orEmpty())
countDownHelper.startTimer(
timerId = property.layoutId.orEmpty(),
millisInFuture = countDownTimerData?.timeLeft ?: 0,
onTick = ::onTick
) {
uiTronViewModel.handleActions(countDownTimerData?.onFinish)
}
}
TextRenderer().Render(
property = property,
uiTronData = textData,
uiTronViewModel = uiTronViewModel,
modifier = modifier
)
}
private fun formatTimerText(timeLeft: Long, format: String?): String {
return timerStrategyFactory.getTimerStrategy(format).formatTimer(timeLeft)
}
private companion object {
const val ONE_DAY_MILLIS = 86_400_000L
const val ONE_HOUR_MILLIS = 3_600_000L
const val ONE_MINUTE_MILLIS = 60_000L
const val ONE_SECOND_MILLIS = 1_000L
}
}

View File

@@ -0,0 +1,285 @@
package com.navi.rr.uitron.render.countdowntimer
import javax.inject.Inject
interface TimerStrategy {
fun formatTimer(timeLeft: Long): String
}
sealed class BaseTimerStrategy : TimerStrategy {
protected var days = 0L
protected var hours = 0L
protected var minutes = 0L
protected var seconds = 0L
protected var hoursLeft = 0L
protected var minutesLeft = 0L
protected var secondsLeft = 0L
protected fun initParameters(timeLeft: Long) {
seconds = timeLeft / 1000
secondsLeft = seconds % 60
minutes = seconds / 60
minutesLeft = minutes % 60
hours = minutes / 60
hoursLeft = hours % 24
days = hours / 24
}
internal fun Long.checkPlural(): String {
return if (this > 1) {
"s"
} else {
""
}
}
internal fun Long.toTwoDigitTimerFormat(): String {
return if (this < 10) {
"0$this"
} else {
this.toString()
}
}
class AbbreviatedTimerStrategy : BaseTimerStrategy() {
override fun formatTimer(timeLeft: Long): String {
super.initParameters(timeLeft)
return when {
days > 0 -> {
"${days}d"
}
hours > 0 -> {
"${hours}h"
}
minutes > 0 -> {
"${minutes}m"
}
else -> {
"${seconds}s"
}
}
}
}
class DetailedTimerStrategy : BaseTimerStrategy() {
override fun formatTimer(timeLeft: Long): String {
super.initParameters(timeLeft)
return when {
days > 0 -> {
"$days day${days.checkPlural()}"
}
hours > 0 -> {
"$hours hour${hours.checkPlural()}"
}
minutes > 0 -> {
"$minutes minute${minutes.checkPlural()}"
}
else -> {
"$seconds second${seconds.checkPlural()}"
}
}
}
}
class DigitalAbbreviatedTimerStrategy : BaseTimerStrategy() {
override fun formatTimer(timeLeft: Long): String {
super.initParameters(timeLeft)
return when {
days > 0 -> {
"$days day${days.checkPlural()}"
}
hours > 0 -> {
"${hours.toTwoDigitTimerFormat()}:${minutesLeft.toTwoDigitTimerFormat()}h"
}
minutes > 0 -> {
"${minutes.toTwoDigitTimerFormat()}:${secondsLeft.toTwoDigitTimerFormat()}m"
}
else -> {
"${seconds}s"
}
}
}
}
class DigitalTimerStrategy : BaseTimerStrategy() {
override fun formatTimer(timeLeft: Long): String {
super.initParameters(timeLeft)
return when {
days > 0 -> {
"$days day${days.checkPlural()}"
}
hours > 0 -> {
"${hours.toTwoDigitTimerFormat()}:${minutesLeft.toTwoDigitTimerFormat()} hrs"
}
minutes > 0 -> {
"${minutes.toTwoDigitTimerFormat()}:${secondsLeft.toTwoDigitTimerFormat()} mins"
}
else -> {
"$seconds secs"
}
}
}
}
class DigitalDetailedTimerStrategy : BaseTimerStrategy() {
override fun formatTimer(timeLeft: Long): String {
super.initParameters(timeLeft)
return when {
days > 0 -> {
"$days day${days.checkPlural()}"
}
hours > 0 -> {
"${hours.toTwoDigitTimerFormat()}:${minutesLeft.toTwoDigitTimerFormat()} hours"
}
minutes > 0 -> {
"${minutes.toTwoDigitTimerFormat()}:${secondsLeft.toTwoDigitTimerFormat()} minutes"
}
else -> {
"$seconds seconds"
}
}
}
}
class MinSecAbbreviatedTimerStrategy : BaseTimerStrategy() {
override fun formatTimer(timeLeft: Long): String {
super.initParameters(timeLeft)
return when {
days > 0 -> {
"$days day${days.checkPlural()}"
}
hours > 0 -> {
"${hours}h ${minutesLeft}m"
}
minutes > 0 -> {
"${minutesLeft}m ${secondsLeft}s"
}
else -> {
"$seconds s"
}
}
}
}
class MinSecTimerStrategy : BaseTimerStrategy() {
override fun formatTimer(timeLeft: Long): String {
super.initParameters(timeLeft)
return when {
days > 0 -> {
"$days day${days.checkPlural()}"
}
hours > 0 -> {
"$hours hr${hours.checkPlural()} $minutesLeft min${minutesLeft.checkPlural()}"
}
minutes > 0 -> {
"$minutes min${minutes.checkPlural()} $secondsLeft sec${secondsLeft.checkPlural()}"
}
else -> {
"$seconds sec${seconds.checkPlural()}"
}
}
}
}
class MinSecDetailedTimerStrategy : BaseTimerStrategy() {
override fun formatTimer(timeLeft: Long): String {
super.initParameters(timeLeft)
return when {
days > 0 -> {
"$days day${days.checkPlural()}"
}
hours > 0 -> {
"$hours hour${hours.checkPlural()} $minutesLeft minute${minutesLeft.checkPlural()}"
}
minutes > 0 -> {
"$minutes minute${minutes.checkPlural()} $secondsLeft second${secondsLeft.checkPlural()}"
}
else -> {
"$seconds second${seconds.checkPlural()}"
}
}
}
}
class DefaultTimerStrategy : BaseTimerStrategy() {
override fun formatTimer(timeLeft: Long): String {
super.initParameters(timeLeft)
return when {
days > 0 -> {
"$days day${days.checkPlural()}"
}
hours > 0 -> {
"$hours hr${hours.checkPlural()}"
}
minutes > 0 -> {
"$minutes min${minutes.checkPlural()}"
}
else -> {
"$seconds sec${seconds.checkPlural()}"
}
}
}
}
}
class TimerStrategyFactory @Inject constructor() {
fun getTimerStrategy(type: String?): TimerStrategy {
return when (type) {
ABBREVIATED_FORMAT -> BaseTimerStrategy.AbbreviatedTimerStrategy()
DETAILED_FORMAT -> BaseTimerStrategy.DetailedTimerStrategy()
DIGITAL_ABBREVIATED_FORMAT -> BaseTimerStrategy.DigitalAbbreviatedTimerStrategy()
DIGITAL_FORMAT -> BaseTimerStrategy.DigitalTimerStrategy()
DIGITAL_DETAILED_FORMAT -> BaseTimerStrategy.DigitalDetailedTimerStrategy()
MIN_SEC_ABBREVIATED_FORMAT -> BaseTimerStrategy.MinSecAbbreviatedTimerStrategy()
MIN_SEC_FORMAT -> BaseTimerStrategy.MinSecTimerStrategy()
MIN_SEC_DETAILED_FORMAT -> BaseTimerStrategy.MinSecDetailedTimerStrategy()
else -> BaseTimerStrategy.DefaultTimerStrategy()
}
}
private companion object {
const val ABBREVIATED_FORMAT = "ABBREVIATED_FORMAT"
const val DETAILED_FORMAT = "DETAILED_FORMAT"
const val DIGITAL_ABBREVIATED_FORMAT = "DIGITAL_ABBREVIATED_FORMAT"
const val DIGITAL_FORMAT = "DIGITAL_FORMAT"
const val DIGITAL_DETAILED_FORMAT = "DIGITAL_DETAILED_FORMAT"
const val MIN_SEC_ABBREVIATED_FORMAT = "MIN_SEC_ABBREVIATED_FORMAT"
const val MIN_SEC_FORMAT = "MIN_SEC_FORMAT"
const val MIN_SEC_DETAILED_FORMAT = "MIN_SEC_DETAILED_FORMAT"
}
}

View File

@@ -180,4 +180,4 @@ public fun <T> List<T>.secondOrNull(): T? {
public fun <T> List<T>.thirdOrNull(): T? {
return if (size >= 3) get(2) else null
}
}

View File

@@ -5,7 +5,7 @@
*
*/
package com.navi.coin.utils.facade
package com.navi.rr.utils.facade
import android.os.CountDownTimer
import java.io.Closeable