Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1ebd6dac authored by Andre Le's avatar Andre Le
Browse files

StatusBarDate: Add date to ClockViewModel

- Move longerDateFormat and shorterDateFormat from ShadeHeaderViewModel
  to ClockInteractor so that these flow can be use both in the clock and
  in the shade header.
- Add date text to ClockViewModel, which will be used to render the date
  text in status bar in the next CL.

Bug: 378545144
Flag: com.android.systemui.status_bar_date
Test: ClockViewModelTest
Change-Id: If5d3cc025b0ab4de36521c59d0ded7bc52af41d3
parent b4c11b15
Loading
Loading
Loading
Loading
+35 −0
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ import com.google.common.truth.Truth.assertThat
import java.time.ZoneId
import java.util.Locale
import java.util.TimeZone
import kotlin.time.Duration.Companion.days
import kotlin.time.Duration.Companion.minutes
import kotlinx.coroutines.ExperimentalCoroutinesApi
import org.junit.After
@@ -263,6 +264,40 @@ class ClockViewModelTest : SysuiTestCase() {
            assertThat(underTest.contentDescriptionText).isEqualTo("11:12\u202FPM")
        }

    @Test
    fun shorterDateText_updatesWhenTimeTicks() =
        kosmos.runTest {
            fakeSystemClock.setCurrentTimeMillis(CURRENT_TIME_MILLIS)
            underTest.activateIn(testScope)

            assertThat(underTest.shorterDateText).isEqualTo("May 8")

            fakeSystemClock.advanceTime(2.days.inWholeMilliseconds)
            broadcastDispatcher.sendIntentToMatchingReceiversOnly(
                context,
                Intent(Intent.ACTION_TIME_TICK),
            )

            assertThat(underTest.shorterDateText).isEqualTo("May 10")
        }

    @Test
    fun longerDateText_updatesWhenTimeTicks() =
        kosmos.runTest {
            fakeSystemClock.setCurrentTimeMillis(CURRENT_TIME_MILLIS)
            underTest.activateIn(testScope)

            assertThat(underTest.longerDateText).isEqualTo("Wed, May 8")

            fakeSystemClock.advanceTime(2.days.inWholeMilliseconds)
            broadcastDispatcher.sendIntentToMatchingReceiversOnly(
                context,
                Intent(Intent.ACTION_TIME_TICK),
            )

            assertThat(underTest.longerDateText).isEqualTo("Fri, May 10")
        }

    private fun Kosmos.getTunable(): Tunable {
        val tunableCaptor = argumentCaptor<Tunable>()
        verify(tunerService).addTunable(tunableCaptor.capture(), any())
+25 −0
Original line number Diff line number Diff line
@@ -16,8 +16,11 @@

package com.android.systemui.clock.domain.interactor

import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.icu.text.DateFormat
import android.icu.text.DisplayContext
import android.os.UserHandle
import android.provider.AlarmClock
import androidx.annotation.VisibleForTesting
@@ -26,11 +29,14 @@ import com.android.systemui.clock.data.repository.ClockRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.tuner.TunerService
import com.android.systemui.util.kotlin.emitOnStart
import com.android.systemui.util.time.SystemClock
import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import java.util.Date
import java.util.Locale
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -42,6 +48,7 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.stateIn

@@ -49,6 +56,7 @@ import kotlinx.coroutines.flow.stateIn
class ClockInteractor
@Inject
constructor(
    @ShadeDisplayAware private val context: Context,
    private val repository: ClockRepository,
    private val activityStarter: ActivityStarter,
    private val broadcastDispatcher: BroadcastDispatcher,
@@ -121,6 +129,17 @@ constructor(
                initialValue = Date(systemClock.currentTimeMillis()),
            )

    private val longerPattern = context.getString(R.string.abbrev_wday_month_day_no_year_alarm)
    private val shorterPattern = context.getString(R.string.abbrev_month_day_no_year)

    @OptIn(ExperimentalCoroutinesApi::class)
    val longerDateFormat: Flow<DateFormat> =
        onTimezoneOrLocaleChanged.mapLatest { getFormatFromPattern(longerPattern) }

    @OptIn(ExperimentalCoroutinesApi::class)
    val shorterDateFormat: Flow<DateFormat> =
        onTimezoneOrLocaleChanged.mapLatest { getFormatFromPattern(shorterPattern) }

    /** Launch the clock activity. */
    fun launchClockActivity() {
        val nextAlarmIntent = repository.nextAlarmIntent
@@ -148,6 +167,12 @@ constructor(
        )
    }

    private fun getFormatFromPattern(pattern: String?): DateFormat {
        return DateFormat.getInstanceForSkeleton(pattern, Locale.getDefault()).apply {
            setContext(DisplayContext.CAPITALIZATION_FOR_STANDALONE)
        }
    }

    companion object {
        @VisibleForTesting const val CLOCK_SECONDS_TUNER_KEY = "clock_seconds"
    }
+24 −0
Original line number Diff line number Diff line
@@ -97,6 +97,30 @@ constructor(
            source = _clockText,
        )

    val longerDateText: String by
        hydrator.hydratedStateOf(
            traceName = "longerDateText",
            initialValue = "",
            source =
                combine(clockInteractor.longerDateFormat, clockInteractor.currentTime) {
                    format,
                    time ->
                    format.format(time)
                },
        )

    val shorterDateText: String by
        hydrator.hydratedStateOf(
            traceName = "shorterDateText",
            initialValue = "",
            source =
                combine(clockInteractor.shorterDateFormat, clockInteractor.currentTime) {
                    format,
                    time ->
                    format.format(time)
                },
        )

    override suspend fun onActivated(): Nothing {
        coroutineScope {
            launch { hydrator.activate() }
+6 −21
Original line number Diff line number Diff line
@@ -20,8 +20,6 @@ package com.android.systemui.shade.ui.viewmodel

import android.content.Context
import android.content.Intent
import android.icu.text.DateFormat
import android.icu.text.DisplayContext
import android.provider.Settings
import android.view.ViewGroup
import androidx.compose.runtime.derivedStateOf
@@ -58,14 +56,11 @@ import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsVi
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModelKairos
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import java.util.Locale
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest

/** Models UI state for the shade header. */
@OptIn(ExperimentalCoroutinesApi::class)
@@ -159,20 +154,14 @@ constructor(
        get() =
            dualShadeEducationInteractor.education == DualShadeEducationModel.ForQuickSettingsShade

    private val longerPattern = context.getString(R.string.abbrev_wday_month_day_no_year_alarm)
    private val shorterPattern = context.getString(R.string.abbrev_month_day_no_year)

    private val longerDateFormat: Flow<DateFormat> =
        clockInteractor.onTimezoneOrLocaleChanged.mapLatest { getFormatFromPattern(longerPattern) }
    private val shorterDateFormat: Flow<DateFormat> =
        clockInteractor.onTimezoneOrLocaleChanged.mapLatest { getFormatFromPattern(shorterPattern) }

    val longerDateText: String by
        hydrator.hydratedStateOf(
            traceName = "longerDateText",
            initialValue = "",
            source =
                combine(longerDateFormat, clockInteractor.currentTime) { format, time ->
                combine(clockInteractor.longerDateFormat, clockInteractor.currentTime) {
                    format,
                    time ->
                    format.format(time)
                },
        )
@@ -182,7 +171,9 @@ constructor(
            traceName = "shorterDateText",
            initialValue = "",
            source =
                combine(shorterDateFormat, clockInteractor.currentTime) { format, time ->
                combine(clockInteractor.shorterDateFormat, clockInteractor.currentTime) {
                    format,
                    time ->
                    format.format(time)
                },
        )
@@ -281,12 +272,6 @@ constructor(
        dualShadeEducationInteractor.onDualShadeEducationElementBoundsChange(element, bounds)
    }

    private fun getFormatFromPattern(pattern: String?): DateFormat {
        return DateFormat.getInstanceForSkeleton(pattern, Locale.getDefault()).apply {
            setContext(DisplayContext.CAPITALIZATION_FOR_STANDALONE)
        }
    }

    @AssistedFactory
    interface Factory {
        fun create(): ShadeHeaderViewModel
+2 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.systemui.clock.domain.interactor

import android.content.applicationContext
import com.android.systemui.broadcast.broadcastDispatcher
import com.android.systemui.clock.data.repository.clockRepository
import com.android.systemui.kosmos.Kosmos
@@ -27,6 +28,7 @@ import com.android.systemui.util.time.fakeSystemClock
var Kosmos.clockInteractor: ClockInteractor by
    Kosmos.Fixture {
        ClockInteractor(
            context = applicationContext,
            repository = clockRepository,
            activityStarter = activityStarter,
            broadcastDispatcher = broadcastDispatcher,