1 /* <lambda>null2 * 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.scenecore 18 19 import androidx.annotation.IntDef 20 import androidx.annotation.RestrictTo 21 import androidx.xr.arcore.Anchor 22 import androidx.xr.runtime.PlaneTrackingMode 23 import androidx.xr.runtime.Session 24 import androidx.xr.runtime.internal.AnchorEntity as RtAnchorEntity 25 import androidx.xr.runtime.internal.JxrPlatformAdapter 26 import java.time.Duration 27 import java.util.UUID 28 import java.util.concurrent.Executor 29 import java.util.concurrent.atomic.AtomicReference 30 31 /** 32 * An AnchorEntity is created to track a Pose relative to some position or surface in the "Real 33 * World." Children of this Entity will remain positioned relative to that location in the real 34 * world, for the purposes of creating Augmented Reality experiences. 35 * 36 * Note that Anchors are only relative to the "real world", and not virtual environments. Also, 37 * calling setParent() on an AnchorEntity has no effect, as the parenting of an Anchor is controlled 38 * by the system. 39 */ 40 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) 41 public class AnchorEntity 42 private constructor(rtEntity: RtAnchorEntity, entityManager: EntityManager) : 43 BaseEntity<RtAnchorEntity>(rtEntity, entityManager) { 44 private val state = AtomicReference(rtEntity.state.fromRtState()) 45 46 private var onStateChangedListener: OnStateChangedListener? = null 47 48 /** Specifies the current tracking state of the Anchor. */ 49 @Target(AnnotationTarget.TYPE) 50 @IntDef(State.ANCHORED, State.UNANCHORED, State.TIMEDOUT, State.ERROR) 51 @Retention(AnnotationRetention.SOURCE) 52 internal annotation class StateValue 53 54 public object State { 55 /** 56 * The ANCHORED state means that this Anchor is being actively tracked and updated by the 57 * perception stack. The application should expect children to maintain their relative 58 * positioning to the system's best understanding of a pose in the real world. 59 */ 60 public const val ANCHORED: Int = 0 61 /** 62 * An UNANCHORED state could mean that the perception stack hasn't found an anchor for this 63 * Space, that it has lost tracking. 64 */ 65 public const val UNANCHORED: Int = 1 66 /** 67 * The AnchorEntity timed out while searching for an underlying anchor. This it is not 68 * possible to recover the AnchorEntity. 69 */ 70 public const val TIMEDOUT: Int = 2 71 /** 72 * The ERROR state means that something has gone wrong and this AnchorEntity is invalid 73 * without the possibility of recovery. 74 */ 75 public const val ERROR: Int = 3 76 } 77 78 /** Specifies the current persist state of the Anchor. */ 79 public enum class PersistState { 80 /** The anchor hasn't been requested to persist. */ 81 PERSIST_NOT_REQUESTED, 82 /** The anchor is requested to persist but hasn't been persisted yet. */ 83 PERSIST_PENDING, 84 /** The anchor is persisted successfully. */ 85 PERSISTED, 86 } 87 88 /** Returns the current tracking state for this AnchorEntity. */ 89 public fun getState(): @StateValue Int = state.get() 90 91 /** Registers a listener callback to be issued when an anchor's state changes. */ 92 @Suppress("ExecutorRegistration") 93 public fun setOnStateChangedListener(onStateChangedListener: OnStateChangedListener?) { 94 this.onStateChangedListener = onStateChangedListener 95 onStateChangedListener?.onStateChanged(state.get()) 96 } 97 98 /** Updates the current state. */ 99 private fun setState(newState: @StateValue Int) { 100 state.set(newState) 101 onStateChangedListener?.onStateChanged(newState) 102 } 103 104 /** 105 * Loads the ARCore for XR Anchor using a Jetpack XR Runtime session. 106 * 107 * @param session the Jetpack XR Runtime session to load the Anchor from. 108 * @return the ARCore for XR Anchor corresponding to the native pointer. 109 */ 110 // TODO(b/373711152) : Remove this method once the ARCore for XR API migration is done. 111 public fun getAnchor(session: Session): Anchor { 112 return Anchor.loadFromNativePointer(session, rtEntity.nativePointer) 113 } 114 115 public companion object { 116 private const val TAG = "AnchorEntity" 117 118 /** 119 * Factory method for AnchorEntity. 120 * 121 * @param adapter JxrPlatformAdapter to use. 122 * @param bounds Bounds for this Anchor Entity. 123 * @param planeType Orientation for the plane to which this Anchor should attach. 124 * @param planeSemantic Semantics for the plane to which this Anchor should attach. 125 * @param timeout Maximum time to search for the anchor, if a suitable plane is not found 126 * within the timeout time the AnchorEntity state will be set to TIMED_OUT. 127 */ 128 internal fun create( 129 adapter: JxrPlatformAdapter, 130 entityManager: EntityManager, 131 bounds: Dimensions, 132 planeType: @PlaneTypeValue Int, 133 planeSemantic: @PlaneSemanticValue Int, 134 timeout: Duration = Duration.ZERO, 135 ): AnchorEntity { 136 val rtAnchorEntity = 137 adapter.createAnchorEntity( 138 bounds.toRtDimensions(), 139 planeType.toRtPlaneType(), 140 planeSemantic.toRtPlaneSemantic(), 141 timeout, 142 ) 143 return create(rtAnchorEntity, entityManager) 144 } 145 146 /** 147 * Factory method for AnchorEntity. 148 * 149 * @param anchor Anchor to create an AnchorEntity for. 150 */ 151 internal fun create( 152 adapter: JxrPlatformAdapter, 153 entityManager: EntityManager, 154 anchor: Anchor, 155 ): AnchorEntity = 156 AnchorEntity.create(adapter.createAnchorEntity(anchor.runtimeAnchor), entityManager) 157 158 /** 159 * Factory method for AnchorEntity. 160 * 161 * @param rtAnchorEntity Runtime AnchorEntity instance. 162 */ 163 internal fun create( 164 rtAnchorEntity: RtAnchorEntity, 165 entityManager: EntityManager, 166 ): AnchorEntity { 167 val anchorEntity = AnchorEntity(rtAnchorEntity, entityManager) 168 rtAnchorEntity.setOnStateChangedListener { newRtState -> 169 when (newRtState) { 170 RtAnchorEntity.State.UNANCHORED -> anchorEntity.setState(State.UNANCHORED) 171 RtAnchorEntity.State.ANCHORED -> anchorEntity.setState(State.ANCHORED) 172 RtAnchorEntity.State.TIMED_OUT -> anchorEntity.setState(State.TIMEDOUT) 173 RtAnchorEntity.State.ERROR -> anchorEntity.setState(State.ERROR) 174 } 175 } 176 return anchorEntity 177 } 178 179 /** 180 * Factory method for AnchorEntity. 181 * 182 * @param adapter JxrPlatformAdapter to use. 183 * @param uuid UUID of the persisted Anchor Entity to create. 184 * @param timeout Maximum time to search for the anchor, if a persisted anchor isn't located 185 * within the timeout time the AnchorEntity state will be set to TIMED_OUT. 186 */ 187 internal fun create( 188 adapter: JxrPlatformAdapter, 189 entityManager: EntityManager, 190 uuid: UUID, 191 timeout: Duration = Duration.ZERO, 192 ): AnchorEntity { 193 val rtAnchorEntity = adapter.createPersistedAnchorEntity(uuid, timeout) 194 val anchorEntity = AnchorEntity(rtAnchorEntity, entityManager) 195 rtAnchorEntity.setOnStateChangedListener { newRtState -> 196 when (newRtState) { 197 RtAnchorEntity.State.UNANCHORED -> anchorEntity.setState(State.UNANCHORED) 198 RtAnchorEntity.State.ANCHORED -> anchorEntity.setState(State.ANCHORED) 199 RtAnchorEntity.State.TIMED_OUT -> anchorEntity.setState(State.TIMEDOUT) 200 RtAnchorEntity.State.ERROR -> anchorEntity.setState(State.ERROR) 201 } 202 } 203 return anchorEntity 204 } 205 206 /** 207 * Public factory function for an AnchorEntity which searches for a location to create an 208 * Anchor among the tracked planes available to the perception system. 209 * 210 * @param session Session to create the AnchorEntity in. 211 * @param bounds Bounds for this AnchorEntity. 212 * @param planeType Orientation of plane to which this Anchor should attach. 213 * @param planeSemantic Semantics of the plane to which this Anchor should attach. 214 * @param timeout The amount of time as a [Duration] to search for the a suitable plane to 215 * attach to. If a plane is not found within the timeout, the returned AnchorEntity state 216 * will be set to AnchorEntity.State.TIMEDOUT. It may take longer than the timeout period 217 * before the anchor state is updated. If the timeout duration is zero it will search for 218 * the anchor indefinitely. 219 * @throws [IllegalStateException] if [session.config.planeTracking] is set to 220 * [PlaneTrackingMode.Disabled]. 221 */ 222 @JvmStatic 223 @JvmOverloads 224 public fun create( 225 session: Session, 226 bounds: Dimensions, 227 planeType: @PlaneTypeValue Int, 228 planeSemantic: @PlaneSemanticValue Int, 229 timeout: Duration = Duration.ZERO, 230 ): AnchorEntity { 231 check(session.config.planeTracking != PlaneTrackingMode.Disabled) { 232 "Config.PlaneTrackingMode is set to Disabled." 233 } 234 235 return AnchorEntity.create( 236 session.platformAdapter, 237 session.scene.entityManager, 238 bounds, 239 planeType, 240 planeSemantic, 241 timeout, 242 ) 243 } 244 245 /** 246 * Public factory function for an AnchorEntity which uses an Anchor from ARCore for XR. 247 * 248 * @param session Session to create the AnchorEntity in. 249 * @param anchor The PerceptionAnchor to use for this AnchorEntity. 250 */ 251 @JvmStatic 252 public fun create(session: Session, anchor: Anchor): AnchorEntity { 253 return AnchorEntity.create(session.platformAdapter, session.scene.entityManager, anchor) 254 } 255 } 256 257 /** Extension function that converts [RtAnchorEntity.State] to [AnchorEntity.State]. */ 258 private fun Int.fromRtState() = 259 when (this) { 260 RtAnchorEntity.State.UNANCHORED -> State.UNANCHORED 261 RtAnchorEntity.State.ANCHORED -> State.ANCHORED 262 RtAnchorEntity.State.TIMED_OUT -> State.TIMEDOUT 263 RtAnchorEntity.State.ERROR -> State.ERROR 264 else -> throw IllegalArgumentException("Unknown state: $this") 265 } 266 267 /** 268 * Registers a listener to be called when the Anchor moves relative to its underlying space. 269 * 270 * <p> The callback is triggered by any anchor movements such as those made by the underlying 271 * perception stack to maintain the anchor's position relative to the real world. Any cached 272 * data relative to the activity space or any other "space" should be updated when this callback 273 * is triggered. 274 * 275 * @param listener The listener to register if non-null, else stops listening if null. 276 * @param executor The executor to run the listener on. Defaults to SceneCore executor if null. 277 */ 278 @JvmOverloads 279 @Suppress("ExecutorRegistration") 280 public fun setOnSpaceUpdatedListener( 281 listener: OnSpaceUpdatedListener?, 282 executor: Executor? = null, 283 ) { 284 rtEntity.setOnSpaceUpdatedListener(listener?.let { { it.onSpaceUpdated() } }, executor) 285 } 286 } 287 288 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) 289 public fun interface OnStateChangedListener { onStateChangednull290 public fun onStateChanged(newState: @AnchorEntity.StateValue Int) 291 } 292