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

Commit 73cfb75e authored by George Lin's avatar George Lin Committed by Automerger Merge Worker
Browse files

Merge "Introduce small clock carousel" into udc-dev am: 73e46698

parents ff91a0d5 73e46698
Loading
Loading
Loading
Loading
+5 −5
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import com.android.customization.picker.clock.ui.view.ClockCarouselView
import com.android.customization.picker.clock.ui.view.ClockViewFactory
import com.android.customization.picker.clock.ui.viewmodel.ClockCarouselViewModel
import com.android.wallpaper.R
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch

object ClockCarouselViewBinder {
@@ -40,6 +41,7 @@ object ClockCarouselViewBinder {
        lifecycleOwner: LifecycleOwner,
        hideSmartspace: (Boolean) -> Unit,
    ) {
        carouselView.setClockViewFactory(clockViewFactory)
        val singleClockHostView =
            singleClockView.requireViewById<FrameLayout>(R.id.single_clock_host_view)
        lifecycleOwner.lifecycleScope.launch {
@@ -47,12 +49,11 @@ object ClockCarouselViewBinder {
                launch { viewModel.isCarouselVisible.collect { carouselView.isVisible = it } }

                launch {
                    viewModel.allClockIds.collect { allClockIds ->
                    combine(viewModel.selectedClockSize, viewModel.allClockIds, ::Pair).collect {
                        (size, allClockIds) ->
                        carouselView.setUpClockCarouselView(
                            clockSize = size,
                            clockIds = allClockIds,
                            onGetClockController = { clockId ->
                                clockViewFactory.getController(clockId)
                            },
                            onClockSelected = { clockId ->
                                viewModel.setSelectedClock(clockId)
                                val hasCustomWeatherDataDisplay =
@@ -61,7 +62,6 @@ object ClockCarouselViewBinder {
                                        .largeClock
                                        .config
                                        .hasCustomWeatherDataDisplay

                                hideSmartspace(hasCustomWeatherDataDisplay)
                            },
                        )
+223 −64
Original line number Diff line number Diff line
@@ -23,6 +23,10 @@ import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.constraintlayout.helper.widget.Carousel
import androidx.constraintlayout.motion.widget.MotionLayout
import androidx.core.view.doOnPreDraw
import androidx.core.view.get
import androidx.core.view.isNotEmpty
import com.android.customization.picker.clock.shared.ClockSize
import com.android.systemui.plugins.ClockController
import com.android.wallpaper.R
import java.lang.Float.max
@@ -39,12 +43,15 @@ class ClockCarouselView(
    val carousel: Carousel
    private val motionLayout: MotionLayout
    private lateinit var adapter: ClockCarouselAdapter
    private var scalingUpClockController: ClockController? = null
    private var scalingDownClockController: ClockController? = null
    private var scalingUpClockView: View? = null
    private var scalingDownClockView: View? = null
    private var showingCardView: View? = null
    private var hidingCardView: View? = null
    private lateinit var clockViewFactory: ClockViewFactory
    private var toCenterClockController: ClockController? = null
    private var offCenterClockController: ClockController? = null
    private var toCenterClockView: View? = null
    private var offCenterClockView: View? = null
    private var toCenterClockHostView: ClockHostView? = null
    private var offCenterClockHostView: ClockHostView? = null
    private var toCenterCardView: View? = null
    private var offCenterCardView: View? = null

    init {
        val clockCarousel = LayoutInflater.from(context).inflate(R.layout.clock_carousel, this)
@@ -52,12 +59,19 @@ class ClockCarouselView(
        motionLayout = clockCarousel.requireViewById(R.id.motion_container)
    }

    /**
     * Make sure to set [clockViewFactory] before calling any functions from [ClockCarouselView].
     */
    fun setClockViewFactory(factory: ClockViewFactory) {
        clockViewFactory = factory
    }

    fun setUpClockCarouselView(
        clockSize: ClockSize,
        clockIds: List<String>,
        onGetClockController: (clockId: String) -> ClockController,
        onClockSelected: (clockId: String) -> Unit,
    ) {
        adapter = ClockCarouselAdapter(clockIds, onGetClockController, onClockSelected)
        adapter = ClockCarouselAdapter(clockSize, clockIds, clockViewFactory, onClockSelected)
        carousel.setAdapter(adapter)
        carousel.refresh()
        motionLayout.setTransitionListener(
@@ -68,70 +82,146 @@ class ClockCarouselView(
                    startId: Int,
                    endId: Int
                ) {
                    if (motionLayout == null) {
                        return
                    }
                    when (clockSize) {
                        ClockSize.DYNAMIC -> prepareDynamicClockView(motionLayout, endId)
                        ClockSize.SMALL -> prepareSmallClockView(motionLayout, endId)
                    }
                    prepareCardView(motionLayout, endId)
                    setCarouselItemAnimationState(true)
                }

                override fun onTransitionChange(
                    motionLayout: MotionLayout?,
                    startId: Int,
                    endId: Int,
                    progress: Float,
                ) {
                    when (clockSize) {
                        ClockSize.DYNAMIC -> onDynamicClockViewTransition(progress)
                        ClockSize.SMALL -> onSmallClockViewTransition(progress)
                    }
                    onCardViewTransition(progress)
                }

                override fun onTransitionCompleted(motionLayout: MotionLayout?, currentId: Int) {
                    setCarouselItemAnimationState(currentId == R.id.start)
                }

                private fun prepareDynamicClockView(motionLayout: MotionLayout, endId: Int) {
                    val scalingDownClockId = adapter.clockIds[carousel.currentIndex]
                    val scalingUpIdx =
                        if (endId == R.id.next) (carousel.currentIndex + 1) % adapter.count()
                        else (carousel.currentIndex - 1 + adapter.count()) % adapter.count()
                    val scalingUpClockId = adapter.clockIds[scalingUpIdx]
                    scalingDownClockController = adapter.onGetClockController(scalingDownClockId)
                    scalingUpClockController = adapter.onGetClockController(scalingUpClockId)
                    scalingDownClockView = motionLayout?.findViewById(R.id.clock_scale_view_2)
                    scalingUpClockView =
                        motionLayout?.findViewById(
                    offCenterClockController = clockViewFactory.getController(scalingDownClockId)
                    toCenterClockController = clockViewFactory.getController(scalingUpClockId)
                    offCenterClockView = motionLayout.findViewById(R.id.clock_scale_view_2)
                    toCenterClockView =
                        motionLayout.findViewById(
                            if (endId == R.id.next) R.id.clock_scale_view_3
                            else R.id.clock_scale_view_1
                        )
                    showingCardView = motionLayout?.findViewById(R.id.item_card_2)
                    hidingCardView =
                        motionLayout?.findViewById(
                }

                private fun prepareSmallClockView(motionLayout: MotionLayout, endId: Int) {
                    offCenterClockHostView = motionLayout.findViewById(R.id.clock_host_view_2)
                    toCenterClockHostView =
                        motionLayout.findViewById(
                            if (endId == R.id.next) R.id.clock_host_view_3
                            else R.id.clock_host_view_1
                        )
                }

                private fun prepareCardView(motionLayout: MotionLayout, endId: Int) {
                    offCenterCardView = motionLayout.findViewById(R.id.item_card_2)
                    toCenterCardView =
                        motionLayout.findViewById(
                            if (endId == R.id.next) R.id.item_card_3 else R.id.item_card_1
                        )
                    setCardAnimationState(true)
                }

                override fun onTransitionChange(
                    motionLayout: MotionLayout?,
                    startId: Int,
                    endId: Int,
                    progress: Float
                ) {
                    scalingDownClockController
                private fun onCardViewTransition(progress: Float) {
                    offCenterCardView?.alpha = getShowingAlpha(progress)
                    toCenterCardView?.alpha = getHidingAlpha(progress)
                }

                private fun onDynamicClockViewTransition(progress: Float) {
                    offCenterClockController
                        ?.largeClock
                        ?.animations
                        ?.onPickerCarouselSwiping(1 - progress)
                    scalingUpClockController
                    toCenterClockController
                        ?.largeClock
                        ?.animations
                        ?.onPickerCarouselSwiping(progress)
                    val scalingUpScale = getScalingUpScale(progress)
                    val scalingDownScale = getScalingDownScale(progress)
                    scalingUpClockView?.scaleX = scalingUpScale
                    scalingUpClockView?.scaleY = scalingUpScale
                    scalingDownClockView?.scaleX = scalingDownScale
                    scalingDownClockView?.scaleY = scalingDownScale
                    showingCardView?.alpha = getShowingAlpha(progress)
                    hidingCardView?.alpha = getHidingAlpha(progress)
                    val scalingUpScale = getScalingUpScale(progress)
                    offCenterClockView?.scaleX = scalingDownScale
                    offCenterClockView?.scaleY = scalingDownScale
                    toCenterClockView?.scaleX = scalingUpScale
                    toCenterClockView?.scaleY = scalingUpScale
                }

                override fun onTransitionCompleted(motionLayout: MotionLayout?, currentId: Int) {
                    setCardAnimationState(currentId == R.id.start)
                private fun onSmallClockViewTransition(progress: Float) {
                    val offCenterClockHostView = offCenterClockHostView ?: return
                    val toCenterClockHostView = toCenterClockHostView ?: return
                    val offCenterClockFrame =
                        if (offCenterClockHostView.isNotEmpty()) {
                            offCenterClockHostView[0]
                        } else {
                            null
                        }
                            ?: return
                    val toCenterClockFrame =
                        if (toCenterClockHostView.isNotEmpty()) {
                            toCenterClockHostView[0]
                        } else {
                            null
                        }
                            ?: return
                    offCenterClockHostView.doOnPreDraw {
                        it.pivotX = progress * it.width / 2
                        it.pivotY = progress * it.height / 2
                    }
                    toCenterClockHostView.doOnPreDraw {
                        it.pivotX = (1 - progress) * it.width / 2
                        it.pivotY = (1 - progress) * it.height / 2
                    }
                    offCenterClockFrame.translationX =
                        getTranslationDistance(
                            offCenterClockHostView.width,
                            offCenterClockFrame.width,
                            offCenterClockFrame.left,
                        ) * progress
                    offCenterClockFrame.translationY =
                        getTranslationDistance(
                            offCenterClockHostView.height,
                            offCenterClockFrame.height,
                            offCenterClockFrame.top,
                        ) * progress
                    toCenterClockFrame.translationX =
                        getTranslationDistance(
                            toCenterClockHostView.width,
                            toCenterClockFrame.width,
                            toCenterClockFrame.left,
                        ) * (1 - progress)
                    toCenterClockFrame.translationY =
                        getTranslationDistance(
                            toCenterClockHostView.height,
                            toCenterClockFrame.height,
                            toCenterClockFrame.top,
                        ) * (1 - progress)
                }

                private fun setCardAnimationState(isStart: Boolean) {
                    scalingDownClockView?.scaleX = if (isStart) 1f else CLOCK_CAROUSEL_VIEW_SCALE
                    scalingDownClockView?.scaleY = if (isStart) 1f else CLOCK_CAROUSEL_VIEW_SCALE
                    scalingUpClockView?.scaleX = if (isStart) CLOCK_CAROUSEL_VIEW_SCALE else 1f
                    scalingUpClockView?.scaleY = if (isStart) CLOCK_CAROUSEL_VIEW_SCALE else 1f
                    scalingDownClockController
                        ?.largeClock
                        ?.animations
                        ?.onPickerCarouselSwiping(if (isStart) 1f else 0f)
                    scalingUpClockController
                        ?.largeClock
                        ?.animations
                        ?.onPickerCarouselSwiping(if (isStart) 0f else 1f)
                    showingCardView?.alpha = if (isStart) 0f else 1f
                    hidingCardView?.alpha = if (isStart) 1f else 0f
                private fun setCarouselItemAnimationState(isStart: Boolean) {
                    when (clockSize) {
                        ClockSize.DYNAMIC -> onDynamicClockViewTransition(if (isStart) 0f else 1f)
                        ClockSize.SMALL -> onSmallClockViewTransition(if (isStart) 0f else 1f)
                    }
                    onCardViewTransition(if (isStart) 0f else 1f)
                }

                override fun onTransitionTrigger(
@@ -154,9 +244,10 @@ class ClockCarouselView(
        }
    }

    class ClockCarouselAdapter(
    private class ClockCarouselAdapter(
        val clockSize: ClockSize,
        val clockIds: List<String>,
        val onGetClockController: (clockId: String) -> ClockController,
        private val clockViewFactory: ClockViewFactory,
        private val onClockSelected: (clockId: String) -> Unit
    ) : Carousel.Adapter {

@@ -175,29 +266,89 @@ class ClockCarouselView(
            val clockHostView =
                getClockHostViewId(viewRoot.id)?.let { viewRoot.findViewById(it) as? ClockHostView }
                    ?: return
            val clockId = clockIds[index]

            // Add the clock view to the cloc host view
            clockHostView.removeAllViews()
            val clockView = onGetClockController(clockIds[index]).largeClock.view
            val clockView =
                when (clockSize) {
                    ClockSize.DYNAMIC -> clockViewFactory.getLargeView(clockId)
                    ClockSize.SMALL -> clockViewFactory.getSmallView(clockId)
                }
            // The clock view might still be attached to an existing parent. Detach before adding to
            // another parent.
            (clockView.parent as? ViewGroup)?.removeView(clockView)
            clockHostView.addView(clockView)
            // initialize scaling state for all clocks
            if (!isMiddleView(viewRoot.id)) {
                cardView.alpha = 1f

            val isMiddleView = isMiddleView(viewRoot.id)
            when (clockSize) {
                ClockSize.DYNAMIC ->
                    initializeDynamicClockView(
                        isMiddleView,
                        clockScaleView,
                        clockId,
                    )
                ClockSize.SMALL ->
                    initializeSmallClockView(
                        isMiddleView,
                        clockHostView,
                        clockView,
                    )
            }
            cardView.alpha = if (isMiddleView) 0f else 1f
        }

        private fun initializeDynamicClockView(
            isMiddleView: Boolean,
            clockScaleView: View,
            clockId: String,
        ) {
            if (isMiddleView) {
                clockScaleView.scaleX = 1f
                clockScaleView.scaleY = 1f
                clockViewFactory
                    .getController(clockId)
                    .largeClock
                    .animations
                    .onPickerCarouselSwiping(1F)
            } else {
                clockScaleView.scaleX = CLOCK_CAROUSEL_VIEW_SCALE
                clockScaleView.scaleY = CLOCK_CAROUSEL_VIEW_SCALE
                onGetClockController(clockIds[index])
                clockViewFactory
                    .getController(clockId)
                    .largeClock
                    .animations
                    .onPickerCarouselSwiping(0F)
            }
        }

        private fun initializeSmallClockView(
            isMiddleView: Boolean,
            clockHostView: ClockHostView,
            clockView: View,
        ) {
            clockHostView.doOnPreDraw {
                if (isMiddleView) {
                    it.pivotX = 0F
                    it.pivotY = 0F
                    clockView.translationX = 0F
                    clockView.translationY = 0F
                } else {
                cardView.alpha = 0f
                clockScaleView.scaleX = 1f
                clockScaleView.scaleY = 1f
                onGetClockController(clockIds[index])
                    .largeClock
                    .animations
                    .onPickerCarouselSwiping(1F)
                    it.pivotX = it.width / 2F
                    it.pivotY = it.height / 2F
                    clockView.translationX =
                        getTranslationDistance(
                            clockHostView.width,
                            clockView.width,
                            clockView.left,
                        )
                    clockView.translationY =
                        getTranslationDistance(
                            clockHostView.height,
                            clockView.height,
                            clockView.top,
                        )
                }
            }
        }

@@ -258,5 +409,13 @@ class ClockCarouselView(
        fun isMiddleView(rootViewId: Int): Boolean {
            return rootViewId == R.id.item_view_2
        }

        private fun getTranslationDistance(
            hostLength: Int,
            frameLength: Int,
            edgeDimen: Int,
        ): Float {
            return ((hostLength - frameLength) / 2 - edgeDimen).toFloat()
        }
    }
}
+11 −15
Original line number Diff line number Diff line
@@ -70,21 +70,20 @@ class ClockViewFactory(
                FrameLayout.LayoutParams.WRAP_CONTENT,
                resources.getDimensionPixelSize(R.dimen.small_clock_height)
            )
        layoutParams.topMargin =
            getStatusBarHeight(resources) +
                resources.getDimensionPixelSize(R.dimen.small_clock_padding_top)
        layoutParams.topMargin = getSmallClockTopMargin()
        layoutParams.marginStart = getSmallClockStartPadding()
        smallClockFrame.layoutParams = layoutParams

        smallClockFrame.setPaddingRelative(
            resources.getDimensionPixelSize(R.dimen.clock_padding_start),
            0,
            0,
            0
        )
        smallClockFrame.clipChildren = false
        return smallClockFrame
    }

    private fun getSmallClockTopMargin() =
        getStatusBarHeight(appContext.resources) +
            appContext.resources.getDimensionPixelSize(R.dimen.small_clock_padding_top)

    private fun getSmallClockStartPadding() =
        appContext.resources.getDimensionPixelSize(R.dimen.clock_padding_start)

    fun updateColorForAllClocks(@ColorInt seedColor: Int?) {
        clockControllers.values.forEach { it.events.onSeedColorChanged(seedColor = seedColor) }
    }
@@ -185,12 +184,9 @@ class ClockViewFactory(
     * and position the clock view
     */
    private fun getSmallClockRegion(): Rect {
        val topMargin =
            getStatusBarHeight(resources) +
                resources.getDimensionPixelSize(R.dimen.small_clock_padding_top)
        val start = resources.getDimensionPixelSize(R.dimen.clock_padding_start)
        val topMargin = getSmallClockTopMargin()
        val targetHeight = resources.getDimensionPixelSize(R.dimen.small_clock_height)
        return Rect(start, topMargin, screenSize.x, topMargin + targetHeight)
        return Rect(getSmallClockStartPadding(), topMargin, screenSize.x, topMargin + targetHeight)
    }

    companion object {
+3 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.android.customization.picker.clock.domain.interactor.ClockPickerInteractor
import com.android.customization.picker.clock.shared.ClockSize
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
@@ -53,6 +54,8 @@ constructor(
            }
            .stateIn(viewModelScope, SharingStarted.Eagerly, emptyList())

    val selectedClockSize: Flow<ClockSize> = interactor.selectedClockSize

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

    val isCarouselVisible: Flow<Boolean> = allClockIds.map { it.size > 1 }.distinctUntilChanged()