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

Commit 2351579f authored by George Lin's avatar George Lin
Browse files

[TP] Handle case for single default clock

For the case that the phone only has the single default clock
- Show the single clock on the preview

Test: manually test that the color option is hidden
Bug: 269631245
Change-Id: Iaa62ca98596eba343ec5a7bb30a5d8a5402e8599
parent c8a1c87e
Loading
Loading
Loading
Loading
+25 −0
Original line number Original line Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?><!--
     Copyright (C) 2023 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->
<com.android.wallpaper.picker.DisplayAspectRatioFrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="@dimen/clock_carousel_item_height">
    <FrameLayout
        android:id="@+id/single_clock_host_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center" />
</com.android.wallpaper.picker.DisplayAspectRatioFrameLayout>
 No newline at end of file
+24 −3
Original line number Original line Diff line number Diff line
@@ -16,6 +16,8 @@
package com.android.customization.picker.clock.ui.binder
package com.android.customization.picker.clock.ui.binder


import android.view.View
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.core.view.isVisible
import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleOwner
@@ -23,7 +25,7 @@ import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.customization.picker.clock.ui.view.ClockCarouselView
import com.android.customization.picker.clock.ui.view.ClockCarouselView
import com.android.customization.picker.clock.ui.viewmodel.ClockCarouselViewModel
import com.android.customization.picker.clock.ui.viewmodel.ClockCarouselViewModel
import kotlinx.coroutines.flow.collect
import com.android.wallpaper.R
import kotlinx.coroutines.launch
import kotlinx.coroutines.launch


object ClockCarouselViewBinder {
object ClockCarouselViewBinder {
@@ -39,12 +41,17 @@ object ClockCarouselViewBinder {
    @JvmStatic
    @JvmStatic
    fun bind(
    fun bind(
        view: ClockCarouselView,
        view: ClockCarouselView,
        singleClockView: ViewGroup,
        viewModel: ClockCarouselViewModel,
        viewModel: ClockCarouselViewModel,
        clockViewFactory: (clockId: String) -> View,
        clockViewFactory: (clockId: String) -> View,
        lifecycleOwner: LifecycleOwner,
        lifecycleOwner: LifecycleOwner,
    ): Binding {
    ): Binding {
        val singleClockHostView =
            singleClockView.requireViewById<FrameLayout>(R.id.single_clock_host_view)
        lifecycleOwner.lifecycleScope.launch {
        lifecycleOwner.lifecycleScope.launch {
            lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
            lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
                launch { viewModel.isCarouselVisible.collect { view.isVisible = it } }

                launch {
                launch {
                    viewModel.allClockIds.collect { allClockIds ->
                    viewModel.allClockIds.collect { allClockIds ->
                        view.setUpClockCarouselView(
                        view.setUpClockCarouselView(
@@ -54,20 +61,34 @@ object ClockCarouselViewBinder {
                        )
                        )
                    }
                    }
                }
                }

                launch {
                launch {
                    viewModel.selectedIndex.collect { selectedIndex ->
                    viewModel.selectedIndex.collect { selectedIndex ->
                        view.setSelectedClockIndex(selectedIndex)
                        view.setSelectedClockIndex(selectedIndex)
                    }
                    }
                }
                }

                launch { viewModel.isSingleClockViewVisible.collect { view.isVisible = it } }

                launch {
                    viewModel.clockId.collect { clockId ->
                        singleClockHostView.removeAllViews()
                        val clockView = clockViewFactory(clockId)
                        // The clock view might still be attached to an existing parent. Detach
                        // before adding to another parent.
                        (clockView.parent as? ViewGroup)?.removeView(clockView)
                        singleClockHostView.addView(clockView)
                    }
                }
            }
            }
        }
        }
        return object : Binding {
        return object : Binding {
            override fun show() {
            override fun show() {
                view.isVisible = true
                viewModel.showClockCarousel(true)
            }
            }


            override fun hide() {
            override fun hide() {
                view.isVisible = false
                viewModel.showClockCarousel(false)
            }
            }
        }
        }
    }
    }
+1 −1
Original line number Original line Diff line number Diff line
@@ -83,7 +83,7 @@ object ClockSettingsBinder {
                launch { viewModel.tabs.collect { tabAdapter.setItems(it) } }
                launch { viewModel.tabs.collect { tabAdapter.setItems(it) } }


                launch {
                launch {
                    viewModel.selectedTabPosition.collect { tab ->
                    viewModel.selectedTab.collect { tab ->
                        when (tab) {
                        when (tab) {
                            ClockSettingsViewModel.Tab.COLOR -> {
                            ClockSettingsViewModel.Tab.COLOR -> {
                                colorOptionContainer.isVisible = true
                                colorOptionContainer.isVisible = true
+38 −3
Original line number Original line Diff line number Diff line
@@ -19,22 +19,37 @@ import com.android.customization.picker.clock.domain.interactor.ClockPickerInter
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
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.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.mapNotNull


/**
 * Clock carousel view model that provides data for the carousel of clock previews. When there is
 * only one item, we should show a single clock preview instead of a carousel.
 */
class ClockCarouselViewModel(
class ClockCarouselViewModel(
    private val interactor: ClockPickerInteractor,
    private val interactor: ClockPickerInteractor,
) {
) {

    @OptIn(ExperimentalCoroutinesApi::class)
    @OptIn(ExperimentalCoroutinesApi::class)
    val allClockIds: Flow<List<String>> =
    val allClockIds: Flow<List<String>> =
        interactor.allClocks.mapLatest { clockArray ->
        interactor.allClocks.mapLatest { allClocks ->
            // Delay to avoid the case that the full list of clocks is not initiated.
            // Delay to avoid the case that the full list of clocks is not initiated.
            delay(CLOCKS_EVENT_UPDATE_DELAY_MILLIS)
            delay(CLOCKS_EVENT_UPDATE_DELAY_MILLIS)
            clockArray.map { it.clockId }
            allClocks.map { it.clockId }
        }

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


    @OptIn(ExperimentalCoroutinesApi::class)
    @OptIn(ExperimentalCoroutinesApi::class)
    val selectedIndex: Flow<Int> =
    val selectedIndex: Flow<Int> =
@@ -51,10 +66,30 @@ class ClockCarouselViewModel(
            }
            }
            .mapNotNull { it }
            .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()

    val clockId: Flow<String> =
        allClockIds
            .map { allClockIds -> if (allClockIds.size == 1) allClockIds[0] else null }
            .mapNotNull { it }

    fun setSelectedClock(clockId: String) {
    fun setSelectedClock(clockId: String) {
        interactor.setSelectedClock(clockId)
        interactor.setSelectedClock(clockId)
    }
    }


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

    companion object {
    companion object {
        const val CLOCKS_EVENT_UPDATE_DELAY_MILLIS: Long = 100
        const val CLOCKS_EVENT_UPDATE_DELAY_MILLIS: Long = 100
    }
    }
+2 −2
Original line number Original line Diff line number Diff line
@@ -194,9 +194,9 @@ private constructor(context: Context, private val interactor: ClockPickerInterac
    }
    }


    private val _selectedTabPosition = MutableStateFlow(Tab.COLOR)
    private val _selectedTabPosition = MutableStateFlow(Tab.COLOR)
    val selectedTabPosition: StateFlow<Tab> = _selectedTabPosition.asStateFlow()
    val selectedTab: StateFlow<Tab> = _selectedTabPosition.asStateFlow()
    val tabs: Flow<List<ClockSettingsTabViewModel>> =
    val tabs: Flow<List<ClockSettingsTabViewModel>> =
        selectedTabPosition.map {
        selectedTab.map {
            listOf(
            listOf(
                ClockSettingsTabViewModel(
                ClockSettingsTabViewModel(
                    name = context.resources.getString(R.string.clock_color),
                    name = context.resources.getString(R.string.clock_color),
Loading