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:
- A masked action, such as
ACTION_DOWN,ACTION_MOVE, orACTION_UP. - Pointer coordinates, such as
AXIS_X,AXIS_Y, orAXIS_Z. - Device and source information, such as the device ID and source.
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:
- For more information about UI input, see Focus in Jetpack Compose Glimmer and Handle indirect pointer inputs.
- For projected input events, see
ProjectedActivityCompat,ProjectedInputEvent, andProjectedInputEvent.ProjectedInputAction. - For platform input and navigation, see
MotionEventand Provide custom back navigation.