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

Commit aa07afed authored by George Lin's avatar George Lin
Browse files

Separate lock and home tab (2/2)

Separate the lock and home tab. This way, when swtiching tabs, we no
longer need to remove and create sections.
The major benefit is that there's no flash effect from removing/adding
sections.

Test: https://drive.google.com/file/d/1ZcOUVD5EXA0ZNIhY8mVsMmgBoakSpXwt/view?usp=sharing&resourcekey=0-qKSuCEB9G2gMRV4ltnncSA
Bug: 275724650
Change-Id: Icc5d85919005157cc27edfd7dda43d7436a274d2
parent fe07d1ce
Loading
Loading
Loading
Loading
+1 −19
Original line number Diff line number Diff line
@@ -30,14 +30,6 @@ import com.android.wallpaper.R
import kotlinx.coroutines.launch

object ClockCarouselViewBinder {
    /**
     * The binding is used by the view where there is an action executed from another view, e.g.
     * toggling show/hide of the view that the binder is holding.
     */
    interface Binding {
        fun show()
        fun hide()
    }

    @JvmStatic
    fun bind(
@@ -46,7 +38,7 @@ object ClockCarouselViewBinder {
        viewModel: ClockCarouselViewModel,
        clockViewFactory: ClockViewFactory,
        lifecycleOwner: LifecycleOwner,
    ): Binding {
    ) {
        val singleClockHostView =
            singleClockView.requireViewById<FrameLayout>(R.id.single_clock_host_view)
        lifecycleOwner.lifecycleScope.launch {
@@ -106,15 +98,5 @@ object ClockCarouselViewBinder {
                }
            }
        )

        return object : Binding {
            override fun show() {
                viewModel.showClockCarousel(true)
            }

            override fun hide() {
                viewModel.showClockCarousel(false)
            }
        }
    }
}
+43 −44
Original line number Diff line number Diff line
@@ -110,7 +110,6 @@ class ClockSettingsFragment : AppbarFragment() {
            lifecycleOwner = this,
            offsetToStart = displayUtils.isSingleDisplayOrUnfoldedHorizontalHinge(activity),
        )
            .show()

        ClockSettingsBinder.bind(
            view,
+2 −22
Original line number Diff line number Diff line
@@ -19,8 +19,6 @@ import com.android.customization.picker.clock.domain.interactor.ClockPickerInter
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
@@ -44,14 +42,7 @@ class ClockCarouselViewModel(

    val seedColor: Flow<Int?> = interactor.seedColor

    private val shouldShowCarousel = MutableStateFlow(false)
    val isCarouselVisible: Flow<Boolean> =
        combine(allClockIds.map { it.size > 1 }.distinctUntilChanged(), shouldShowCarousel) {
                hasMoreThanOneClock,
                shouldShowCarousel ->
                hasMoreThanOneClock && shouldShowCarousel
            }
            .distinctUntilChanged()
    val isCarouselVisible: Flow<Boolean> = allClockIds.map { it.size > 1 }.distinctUntilChanged()

    @OptIn(ExperimentalCoroutinesApi::class)
    val selectedIndex: Flow<Int> =
@@ -69,14 +60,8 @@ class ClockCarouselViewModel(
            .mapNotNull { it }

    // Handle the case when there is only one clock in the carousel
    private val shouldShowSingleClock = MutableStateFlow(false)
    val isSingleClockViewVisible: Flow<Boolean> =
        combine(allClockIds.map { it.size == 1 }.distinctUntilChanged(), shouldShowSingleClock) {
                hasOneClock,
                shouldShowSingleClock ->
                hasOneClock && shouldShowSingleClock
            }
            .distinctUntilChanged()
        allClockIds.map { it.size == 1 }.distinctUntilChanged()

    val clockId: Flow<String> =
        allClockIds
@@ -87,11 +72,6 @@ class ClockCarouselViewModel(
        interactor.setSelectedClock(clockId)
    }

    fun showClockCarousel(shouldShow: Boolean) {
        shouldShowCarousel.value = shouldShow
        shouldShowSingleClock.value = shouldShow
    }

    companion object {
        const val CLOCKS_EVENT_UPDATE_DELAY_MILLIS: Long = 100
    }
+21 −34
Original line number Diff line number Diff line
@@ -22,7 +22,6 @@ import android.content.Context
import android.view.View
import android.view.ViewGroup
import android.view.ViewStub
import androidx.core.view.isVisible
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import com.android.customization.picker.clock.ui.binder.ClockCarouselViewBinder
@@ -46,7 +45,7 @@ import kotlinx.coroutines.launch
class PreviewWithClockCarouselSectionController(
    activity: Activity,
    private val lifecycleOwner: LifecycleOwner,
    private val initialScreen: CustomizationSections.Screen,
    private val screen: CustomizationSections.Screen,
    wallpaperInfoFactory: CurrentWallpaperInfoFactory,
    colorViewModel: WallpaperColorsViewModel,
    displayUtils: DisplayUtils,
@@ -59,7 +58,7 @@ class PreviewWithClockCarouselSectionController(
    ScreenPreviewSectionController(
        activity,
        lifecycleOwner,
        initialScreen,
        screen,
        wallpaperInfoFactory,
        colorViewModel,
        displayUtils,
@@ -67,14 +66,13 @@ class PreviewWithClockCarouselSectionController(
        wallpaperInteractor,
    ) {

    private var clockCarouselBinding: ClockCarouselViewBinder.Binding? = null
    private var clockColorAndSizeButton: View? = null

    override val hideLockScreenClockPreview = true

    override fun createView(context: Context): ScreenPreviewView {
        val view = super.createView(context)

        if (screen == CustomizationSections.Screen.LOCK_SCREEN) {
            val clockColorAndSizeButtonStub: ViewStub =
                view.requireViewById(R.id.clock_color_and_size_button)
            clockColorAndSizeButtonStub.layoutResource = R.layout.clock_color_and_size_button
@@ -87,12 +85,12 @@ class PreviewWithClockCarouselSectionController(
            carouselViewStub.layoutResource = R.layout.clock_carousel_view
            val carouselView = carouselViewStub.inflate() as ClockCarouselView

        // TODO (b/270716937) We should handle the single clock case in the clock carousel itself
            // TODO (b/270716937) We should handle the single clock case in the clock carousel
            // itself
            val singleClockViewStub: ViewStub = view.requireViewById(R.id.single_clock_view_stub)
            singleClockViewStub.layoutResource = R.layout.single_clock_view
            val singleClockView = singleClockViewStub.inflate() as ViewGroup
            lifecycleOwner.lifecycleScope.launch {
            clockCarouselBinding =
                ClockCarouselViewBinder.bind(
                    carouselView = carouselView,
                    singleClockView = singleClockView,
@@ -100,20 +98,9 @@ class PreviewWithClockCarouselSectionController(
                    clockViewFactory = clockViewFactory,
                    lifecycleOwner = lifecycleOwner,
                )
            onScreenSwitched(
                isOnLockScreen = initialScreen == CustomizationSections.Screen.LOCK_SCREEN
            )
            }
        return view
        }

    override fun onScreenSwitched(isOnLockScreen: Boolean) {
        super.onScreenSwitched(isOnLockScreen)
        clockColorAndSizeButton?.isVisible = isOnLockScreen
        if (isOnLockScreen) {
            clockCarouselBinding?.show()
        } else {
            clockCarouselBinding?.hide()
        }
        return view
    }
}
+5 −29
Original line number Diff line number Diff line
@@ -75,46 +75,22 @@ class ClockCarouselViewModelTest {
    }

    @Test
    fun setShouldShowCarousel() = runTest {
    fun multipleClockCase() = runTest {
        underTest = ClockCarouselViewModel(ClockPickerInteractor(repositoryWithMultipleClocks))
        val observedIsCarouselVisible = collectLastValue(underTest.isCarouselVisible)
        val observedIsSingleClockViewVisible = collectLastValue(underTest.isSingleClockViewVisible)
        advanceTimeBy(ClockCarouselViewModel.CLOCKS_EVENT_UPDATE_DELAY_MILLIS)
        underTest.showClockCarousel(false)
        assertThat(observedIsCarouselVisible()).isFalse()
        underTest.showClockCarousel(true)
        assertThat(observedIsCarouselVisible()).isTrue()
        assertThat(observedIsSingleClockViewVisible()).isFalse()
    }

    @Test
    fun shouldNotShowCarouselWhenSingleClock() = runTest {
    fun singleClockCase() = runTest {
        underTest = ClockCarouselViewModel(ClockPickerInteractor(repositoryWithSingleClock))
        val observedIsCarouselVisible = collectLastValue(underTest.isCarouselVisible)
        advanceTimeBy(ClockCarouselViewModel.CLOCKS_EVENT_UPDATE_DELAY_MILLIS)
        underTest.showClockCarousel(false)
        assertThat(observedIsCarouselVisible()).isFalse()
        underTest.showClockCarousel(true)
        assertThat(observedIsCarouselVisible()).isFalse()
    }

    @Test
    fun setShouldShowSingleClock() = runTest {
        underTest = ClockCarouselViewModel(ClockPickerInteractor(repositoryWithSingleClock))
        val observedIsSingleClockViewVisible = collectLastValue(underTest.isSingleClockViewVisible)
        advanceTimeBy(ClockCarouselViewModel.CLOCKS_EVENT_UPDATE_DELAY_MILLIS)
        underTest.showClockCarousel(false)
        assertThat(observedIsSingleClockViewVisible()).isFalse()
        underTest.showClockCarousel(true)
        assertThat(observedIsCarouselVisible()).isFalse()
        assertThat(observedIsSingleClockViewVisible()).isTrue()
    }

    @Test
    fun shouldNotShowSingleClockWhenMultipleClocks() = runTest {
        underTest = ClockCarouselViewModel(ClockPickerInteractor(repositoryWithMultipleClocks))
        val observedIsSingleClockViewVisible = collectLastValue(underTest.isSingleClockViewVisible)
        advanceTimeBy(ClockCarouselViewModel.CLOCKS_EVENT_UPDATE_DELAY_MILLIS)
        underTest.showClockCarousel(false)
        assertThat(observedIsSingleClockViewVisible()).isFalse()
        underTest.showClockCarousel(true)
        assertThat(observedIsSingleClockViewVisible()).isFalse()
    }
}