Handle physical input from audio and display glasses

Applicable XR devices
This guidance helps you build experiences for these types of XR devices.
Audio &
Display Glasses

To capture and process user input that is delivered to your app's projected activity, or to implement custom touchpad behaviors, use indirect pointer inputs. To handle other types of events (such as the camera action), see the following sections in this guide.

Handle the camera action

Whenever a user presses a device's camera button, the system delivers an input event to your app's projected activity as a ProjectedInputEvent.ProjectedInputAction.TOGGLE_APP_CAMERA action. Your app can observe this action using a dedicated dispatcher:

@OptIn(ExperimentalProjectedApi::class)
class CameraActionInputSnippetActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Observe hardware camera action events when RESUMED
        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.RESUMED) {
                withContext(Dispatchers.Default) {
                    try {
                        val projectedActivity = ProjectedActivityCompat.create(this@CameraActionInputSnippetActivity)
                        try {
                            projectedActivity.projectedInputEvents.collect { inputEvent ->
                                if (inputEvent.inputAction == TOGGLE_APP_CAMERA) {
                                    withContext(Dispatchers.Main) {
                                        onCameraActionTriggered()
                                    }
                                }
                            }
                        } finally {
                            projectedActivity.close()
                        }
                    } catch (e: CancellationException) {
                        throw e
                    } catch (e: Exception) {
                        Log.w("CameraActionSnippet", "ProjectedActivityCompat unavailable: ${e.message}")
                    }
                }
            }
        }

        setContent {
            GlimmerTheme {
                TouchpadInputSnippet(
                    onTap = { Log.i("CameraActionSnippet", "Touchpad tapped") }
                )
            }
        }
    }

    private fun onCameraActionTriggered() {
        Log.i("CameraActionSnippet", "Camera action button double-pressed!")
    }
}

Inspect generic motion events

Use dispatchGenericMotionEvent to inspect generic motion events delivered to your app's projected activity. Generic motion events are delivered at the activity boundary and don't require a focused Jetpack Compose Glimmer component.

A MotionEvent can include:

Not every value is meaningful for every action. Inspect the action and source before treating a raw event as a completed gesture.

class MotionGestureInputSnippetActivity : ComponentActivity() {

    override fun dispatchGenericMotionEvent(ev: MotionEvent): Boolean {
        val actionName = when (ev.actionMasked) {
            MotionEvent.ACTION_DOWN -> "ACTION_DOWN" // First contact with the glasses touchpad
            MotionEvent.ACTION_MOVE -> "ACTION_MOVE" // Contact moving across the glasses touchpad
            MotionEvent.ACTION_UP -> "ACTION_UP"     // Contact lifted from the glasses touchpad
            else -> "ACTION_${ev.actionMasked}"
        }

        Log.d(
            "MotionGestureSnippet",
            "MotionEvent: action=$actionName x=${ev.x} y=${ev.y}"
        )

        // Delegate to super unless intentionally consuming the event
        return super.dispatchGenericMotionEvent(ev)
    }
}

Return true only when the activity intentionally consumes the event. Otherwise, return super.dispatchGenericMotionEvent(ev) so the event can continue through normal dispatch.

Handle the system Back gesture

When a screen needs custom back behavior, your app should handle the glasses Back gesture using the same Android APIs that you use for other activities, such as OnBackPressedDispatcher and OnBackPressedCallback.

The user gesture depends on the glasses type:

  • On display glasses, swiping down on the touchpad performs the system Back gesture.
  • On audio glasses, swiping back on the touchpad performs the system Back gesture.

Register a back callback when the current screen needs to intercept Back, such as closing an overlay or showing an exit confirmation.

class BackGestureInputSnippetActivity : ComponentActivity() {

    private val backCallback = object : OnBackPressedCallback(true) {
        override fun handleOnBackPressed() {
            onBackGestureReceived()
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Register back gesture callback with Activity's onBackPressedDispatcher
        onBackPressedDispatcher.addCallback(this, backCallback)
    }

    private fun onBackGestureReceived() {
        Log.i("BackGestureSnippet", "System back gesture intercepted")
    }
}

The callback should only be enabled when the active screen should intercept Back. Disable or remove the callback when default system or navigation behavior should handle Back.

Next steps

After implementing input handling for these events, you can learn more about handling other types of input events in the following topics: