1 /* <lambda>null2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.constraintlayout.compose 18 19 import androidx.compose.runtime.MutableFloatState 20 import androidx.compose.runtime.withFrameNanos 21 import androidx.compose.ui.geometry.Offset 22 import androidx.compose.ui.unit.Velocity 23 24 /** 25 * Helper class that handles the interactions between Compose and 26 * [androidx.constraintlayout.core.state.Transition]. 27 */ 28 @ExperimentalMotionApi 29 internal class TransitionHandler( 30 private val motionMeasurer: MotionMeasurer, 31 private val motionProgress: MutableFloatState 32 ) { 33 private val transition: androidx.constraintlayout.core.state.Transition 34 get() = motionMeasurer.transition 35 36 /** 37 * Whether we consume the rest of the drag for OnSwipe. 38 * 39 * @see androidx.constraintlayout.core.state.Transition.isFirstDownAccepted 40 */ 41 fun onAcceptFirstDownForOnSwipe(offset: Offset) = 42 transition.isFirstDownAccepted(offset.x, offset.y) 43 44 /** The [motionProgress] is updated based on the [Offset] from a single drag event. */ 45 fun updateProgressOnDrag(dragAmount: Offset) { 46 val progressDelta = 47 transition.dragToProgress( 48 motionProgress.floatValue, 49 motionMeasurer.layoutCurrentWidth, 50 motionMeasurer.layoutCurrentHeight, 51 dragAmount.x, 52 dragAmount.y 53 ) 54 var newProgress = motionProgress.floatValue + progressDelta 55 newProgress = newProgress.coerceIn(0f, 1f) 56 motionProgress.floatValue = newProgress 57 } 58 59 /** 60 * Called when a swipe event ends, sets up the underlying Transition with the [velocity] of the 61 * swipe at the next frame. 62 */ 63 suspend fun onTouchUp(velocity: Velocity) { 64 withFrameNanos { timeNanos -> 65 transition.setTouchUp(motionProgress.floatValue, timeNanos, velocity.x, velocity.y) 66 } 67 } 68 69 /** 70 * Call to update the [motionProgress] after a swipe has ended and as long as there are no other 71 * touch gestures. 72 */ 73 suspend fun updateProgressWhileTouchUp() { 74 val newProgress = withFrameNanos { timeNanos -> transition.getTouchUpProgress(timeNanos) } 75 motionProgress.floatValue = newProgress 76 } 77 78 /** 79 * Returns true if the progress is still expected to be updated by [updateProgressWhileTouchUp]. 80 */ 81 fun pendingProgressWhileTouchUp(): Boolean { 82 return transition.isTouchNotDone(motionProgress.floatValue) 83 } 84 } 85