1 /*
2 * Copyright (C) 2024 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 com.android.mechanics.debug
18
19 import androidx.compose.runtime.getValue
20 import androidx.compose.runtime.mutableStateOf
21 import androidx.compose.runtime.setValue
22 import com.android.mechanics.MotionValue
23 import com.android.mechanics.impl.DiscontinuityAnimation
24 import com.android.mechanics.spec.InputDirection
25 import com.android.mechanics.spec.SegmentData
26 import com.android.mechanics.spec.SegmentKey
27 import com.android.mechanics.spring.SpringParameters
28 import com.android.mechanics.spring.SpringState
29 import kotlinx.coroutines.DisposableHandle
30
31 /** Utility to gain inspection access to internal [MotionValue] state. */
32 class DebugInspector
33 internal constructor(
34 initialFrameData: FrameData,
35 initialIsActive: Boolean,
36 initialIsAnimating: Boolean,
37 disposableHandle: DisposableHandle,
<lambda>null38 ) : DisposableHandle by disposableHandle {
39
40 /** The last completed frame's data. */
41 var frame: FrameData by mutableStateOf(initialFrameData)
42 internal set
43
44 /** Whether a [MotionValue.keepRunning] coroutine is active currently. */
45 var isActive: Boolean by mutableStateOf(initialIsActive)
46 internal set
47
48 /**
49 * `false` whenever the [MotionValue.keepRunning] coroutine internally is suspended while no
50 * animation is running and the input is not changing.
51 */
52 var isAnimating: Boolean by mutableStateOf(initialIsAnimating)
53 internal set
54 }
55
56 /** The input, output and internal state of a [MotionValue] for the frame. */
57 data class FrameData
58 internal constructor(
59 val input: Float,
60 val gestureDirection: InputDirection,
61 val gestureDragOffset: Float,
62 val frameTimeNanos: Long,
63 val springState: SpringState,
64 private val segment: SegmentData,
65 private val animation: DiscontinuityAnimation,
66 ) {
67 val isStable: Boolean
68 get() = springState == SpringState.AtRest
69
70 val springParameters: SpringParameters
71 get() = animation.springParameters
72
73 val segmentKey: SegmentKey
74 get() = segment.key
75
76 val output: Float
77 get() = currentDirectMapped + (animation.targetValue + springState.displacement)
78
79 val outputTarget: Float
80 get() = currentDirectMapped + animation.targetValue
81
82 private val currentDirectMapped: Float
83 get() = segment.mapping.map(input) - animation.targetValue
84 }
85