1 /*
2  * Copyright 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 androidx.xr.arcore
18 
19 import androidx.annotation.RestrictTo
20 import androidx.xr.runtime.CoreState
21 import androidx.xr.runtime.StateExtender
22 import androidx.xr.runtime.internal.PerceptionManager
23 import androidx.xr.runtime.internal.Runtime
24 import kotlin.time.ComparableTimeMark
25 
26 /** [StateExtender] in charge of extending [CoreState] with [PerceptionState]. */
27 internal class PerceptionStateExtender : StateExtender {
28 
29     internal companion object {
30         internal const val MAX_PERCEPTION_STATE_EXTENSION_SIZE = 100
31 
32         internal val perceptionStateMap = mutableMapOf<ComparableTimeMark, PerceptionState>()
33 
34         private val timeMarkQueue = ArrayDeque<ComparableTimeMark>()
35     }
36 
37     internal lateinit var perceptionManager: PerceptionManager
38 
39     internal val xrResourcesManager = XrResourcesManager()
40 
initializenull41     override fun initialize(runtime: Runtime) {
42         perceptionManager = runtime.perceptionManager
43         xrResourcesManager.lifecycleManager = runtime.lifecycleManager
44         xrResourcesManager.initiateHands(perceptionManager.leftHand, perceptionManager.rightHand)
45     }
46 
extendnull47     override suspend fun extend(coreState: CoreState) {
48         check(this::perceptionManager.isInitialized) {
49             "PerceptionStateExtender is not initialized."
50         }
51 
52         xrResourcesManager.syncTrackables(perceptionManager.trackables)
53         xrResourcesManager.update()
54 
55         xrResourcesManager.leftHand?.update()
56         xrResourcesManager.rightHand?.update()
57 
58         updatePerceptionStateMap(coreState)
59     }
60 
closenull61     internal fun close() {
62         perceptionStateMap.clear()
63         timeMarkQueue.clear()
64         xrResourcesManager.clear()
65     }
66 
updatePerceptionStateMapnull67     private fun updatePerceptionStateMap(coreState: CoreState) {
68         perceptionStateMap.put(
69             coreState.timeMark,
70             PerceptionState(
71                 coreState.timeMark,
72                 xrResourcesManager.trackablesMap.values,
73                 xrResourcesManager.leftHand,
74                 xrResourcesManager.rightHand,
75             ),
76         )
77         timeMarkQueue.add(coreState.timeMark)
78 
79         if (timeMarkQueue.size > MAX_PERCEPTION_STATE_EXTENSION_SIZE) {
80             val timeMark = timeMarkQueue.removeFirst()
81             perceptionStateMap.remove(timeMark)
82         }
83     }
84 }
85 
86 /** The state of the perception system. */
87 @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
88 public val CoreState.perceptionState: PerceptionState?
89     get() = PerceptionStateExtender.perceptionStateMap[this.timeMark]
90