Loading src/com/android/customization/picker/clock/ui/binder/ClockCarouselViewBinder.kt +5 −5 Original line number Diff line number Diff line Loading @@ -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 { Loading @@ -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 { Loading @@ -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 = Loading @@ -61,7 +62,6 @@ object ClockCarouselViewBinder { .largeClock .config .hasCustomWeatherDataDisplay hideSmartspace(hasCustomWeatherDataDisplay) }, ) Loading src/com/android/customization/picker/clock/ui/view/ClockCarouselView.kt +223 −64 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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) Loading @@ -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( Loading @@ -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( Loading @@ -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 { Loading @@ -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, ) } } } Loading Loading @@ -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() } } } src/com/android/customization/picker/clock/ui/view/ClockViewFactory.kt +11 −15 Original line number Diff line number Diff line Loading @@ -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) } } Loading Loading @@ -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 { Loading src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselViewModel.kt +3 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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() Loading Loading
src/com/android/customization/picker/clock/ui/binder/ClockCarouselViewBinder.kt +5 −5 Original line number Diff line number Diff line Loading @@ -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 { Loading @@ -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 { Loading @@ -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 = Loading @@ -61,7 +62,6 @@ object ClockCarouselViewBinder { .largeClock .config .hasCustomWeatherDataDisplay hideSmartspace(hasCustomWeatherDataDisplay) }, ) Loading
src/com/android/customization/picker/clock/ui/view/ClockCarouselView.kt +223 −64 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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) Loading @@ -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( Loading @@ -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( Loading @@ -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 { Loading @@ -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, ) } } } Loading Loading @@ -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() } } }
src/com/android/customization/picker/clock/ui/view/ClockViewFactory.kt +11 −15 Original line number Diff line number Diff line Loading @@ -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) } } Loading Loading @@ -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 { Loading
src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselViewModel.kt +3 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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() Loading