android - How can I observe for date or timezone changes to update the UI using MVVM, UseCase and Hilt? - Stack Overflow

I have a interface called TimeZoneMonitor to monitorize Time Zone or Date changes.I also have a UseCas

I have a interface called TimeZoneMonitor to monitorize Time Zone or Date changes.

I also have a UseCase that queries the database, looking for information based on the current system date.

The code works as expected in terms of querying based on the given date, but I am unable to update the information when the date or time zone changes.

Code:

TimeZoneMonitor

interface TimeZoneMonitor {
    val currentTimeZone: Flow<TimeZone>
    val currentDate:Flow<LocalDateTime>
}

TimeZoneBroadcastMonitor

@Singleton
class TimeZoneBroadcastMonitor @Inject constructor(
    @ApplicationContext private val context: Context,
    @ApplicationScope appScope: CoroutineScope,
    @Dispatcher(IO) private val ioDispatcher: CoroutineDispatcher,
) : TimeZoneMonitor {

    override val currentTimeZone: SharedFlow<TimeZone> =
        callbackFlow {
            // Send the default time zone first.
            trySend(TimeZone.currentSystemDefault())

            // Registers BroadcastReceiver for the TimeZone changes
            val receiver = object : BroadcastReceiver() {
                override fun onReceive(context: Context, intent: Intent) {
                    if (intent.action != Intent.ACTION_TIMEZONE_CHANGED) return

                    val zoneIdFromIntent = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
                        null
                    } else {
                        // Starting Android R we also get the new TimeZone.
                        intent.getStringExtra(Intent.EXTRA_TIMEZONE)?.let { timeZoneId ->
                            // We need to convert it from java.util.Timezone to java.time.ZoneId
                            val zoneId = ZoneId.of(timeZoneId, ZoneId.SHORT_IDS)
                            // Convert to kotlinx.datetime.TimeZone
                            zoneId.toKotlinTimeZone()
                        }
                    }

                    // If there isn't a zoneId in the intent, fallback to the systemDefault, which should also reflect the change
                    trySend(zoneIdFromIntent ?: TimeZone.currentSystemDefault())
                }
            }

            trace("TimeZoneBroadcastReceiver.register") {
                context.registerReceiver(receiver, IntentFilter(Intent.ACTION_TIMEZONE_CHANGED))
            }

            // Send here again, because registering the Broadcast Receiver can take up to several milliseconds.
            // This way, we can reduce the likelihood that a TZ change wouldn't be caught with the Broadcast Receiver.
            trySend(TimeZone.currentSystemDefault())

            awaitClose {
                context.unregisterReceiver(receiver)
            }
        }
            // We use to prevent multiple emissions of the same type, because we use trySend multiple times.
            .distinctUntilChanged()
            .conflate()
            .flowOn(ioDispatcher)
            // Sharing the callback to prevent multiple BroadcastReceivers being registered
            .shareIn(appScope, SharingStarted.WhileSubscribed(5_000), 1)

}

GetHomeUseCase

class GetHomeUseCase @Inject constructor(
    private val universalisRepository: UniversalisRepository,
    private val userDataRepository: UserDataRepository,
) {
    
    operator fun invoke(
        date: Int,
    ): Flow<HomeResource> {
        return combine(
            userDataRepository.userData,
            universalisRepository.countUniversalis(UniversalisResourceQuery(date)),
        ) { userData, count ->
            if (count == 0 && date.isDateValid()) {
                universalisRepository.insertFromRemote(UniversalisResourceQuery(date))
            }
            val newData = universalisRepository.getUniversalisForTest(date).first()
            HomeResource(
                date = date,
                data = newData,
                count = count,
                dynamic = userData
            )
        }
    }
}

HomeViewModel

@HiltViewModel
class HomeViewModel @Inject constructor(
    savedStateHandle: SavedStateHandle,
    private val analyticsHelper: AnalyticsHelper,
    timeZoneMonitor:TimeZoneMonitor,
    val userDataRepository: UserDataRepository,
    getHomeUseCase: GetHomeUseCase,
) : ViewModel() {

    private val selectedTopicIdKey = "selectedTopicIdKey"
    private val route: UniversalisRoute = savedStateHandle.toRoute()

    private val selectedTopicId = savedStateHandle.getStateFlow(
        key = selectedTopicIdKey,
        initialValue = route.initialTopicId.toString(),
    )

    val currentTimeZone = timeZoneMonitor.currentTimeZone
        .stateIn(
            viewModelScope,
            SharingStarted.WhileSubscribed(5_000),
            TimeZone.currentSystemDefault(),
        )
    val zi = ZoneId.of(currentTimeZone.value.id)
    val time = ZonedDateTime.now(zi)
    private val newDate=time.format(DateTimeFormatter.ofPattern("yyyyMMdd")).toInt()
    private var selectedDate = savedStateHandle.getStateFlow(
        key = "date",
        initialValue = newDate,
    )

    val uiState: StateFlow<HomeUiState> = combine(
        selectedTopicId,
        selectedDate,
        getHomeUseCase.invoke(
            date = selectedDate.value,
        ),
        HomeUiState::HomeData,
    ).catch<HomeUiState> {
        val error = HomeUiState.HomeError(
            date = selectedDate.value,
            message = it.message!!
        )
        analyticsHelper.logHomeErrorEvent(error)
        emit(error)
    }//.distinctUntilChanged()
        .stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(5_000),
            initialValue = HomeUiState.Loading,
        )
}

sealed interface HomeUiState {
    data object Loading : HomeUiState

    data class HomeData(
        val selectedTopicId: String?,
        val selectedDate:Int,
        val topics: HomeResource,
    ) : HomeUiState

    data class HomeError(
        val date: Int,
        val message: String
    ) : HomeUiState {

        override fun toString(): String {
            return "Date: $date Msg: $message"
        }
    }

    data object Empty : HomeUiState

}

HomeScreen (Jetpack Compose)

@ExperimentalLayoutApi
@Composable
fun HomeScreen(
    uiState: HomeUiState,
    modifier: Modifier,
    onTopicClick: (String) -> Unit,
    currentTimeZone: State<TimeZone>,
    currentDate: State<LocalDateTime>,
) {

    when (uiState) {
        HomeUiState.Empty -> EmptyState(modifier = modifier)
        is HomeUiState.HomeData -> {
            HomeItems(uiState = uiState, onTopicClick = onTopicClick, currentTimeZone=currentTimeZone,currentDate=currentDate,modifier = modifier, haveDate=true)
        }

        HomeUiState.Loading -> LoadingState(modifier = modifier)
        is HomeUiState.HomeError -> {
            HomeItems(uiState = uiState, onTopicClick = onTopicClick, currentTimeZone=currentTimeZone,currentDate=currentDate,modifier = modifier, haveDate=false)
        }
    }
}

@ExperimentalLayoutApi
@Composable
fun HomeItems(
    uiState: HomeUiState,
    onTopicClick: (String) -> Unit,
    modifier: Modifier,
    haveDate: Boolean = false,
    currentTimeZone: State<TimeZone>,
    currentDate: State<LocalDateTime>

) {

// ...
        val data = uiState as HomeUiState.HomeData

        Text(
              text = data.topics.data.fecha,
              modifier = Modifier.padding(2.dp),
              textAlign = TextAlign.Center,
             )
}

My problem with this code is that if I change the date in the system, the HomeData information is not updated with the new date.

How can I call the UseCase again with the new date and update the UI?

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745075594a4609815.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信