Stream the user's raw screen touches (x/y) to a Mob screen.

Per-widget on_tap only fires for a tapped button. mob_touch observes every touch on the app surface and reports its coordinates — for drawing, custom gestures, joysticks, heatmaps, and the like. It observes without consuming, so buttons and scrolling keep working while you stream. No runtime permission.

Usage

# in a screen
def handle_info({:tap, :start}, socket), do: {:noreply, MobTouch.start(socket)}

def handle_info({:touch, %{phase: phase, x: x, y: y, pointer: p}}, socket) do
  # phase: :down | :move | :up | :cancel
  # x, y:  dp (logical px), origin top-left — the layout's coordinate space
  # p:     stable id per finger (multi-touch); ignore for single-finger use
  {:noreply, draw(socket, x, y)}
end

# later
MobTouch.stop(socket)

start/2 takes throttle_ms: (default 16 ≈ 60 Hz) — the minimum gap between :move deliveries; :down/:up/:cancel are never throttled.

Install

# mix.exs
{:mob_touch, "~> 0.1"}

# mob.exs
config :mob, :plugins, [:mob_touch]
config :mob, :trusted_plugins, %{mob_touch: "ed25519:<fingerprint>"}

mix mob.plugin.trust mob_touch records the fingerprint, then mix mob.deploy --native.

How it works

  • Android: wraps the Activity window's Window.Callback with a reflective proxy that observes dispatchTouchEvent and forwards it on. Coordinates are px ÷ density → dp.
  • iOS: a passive UIGestureRecognizer on the key window with cancelsTouchesInView = NO that never recognizes, so it sees every touch while the app still receives them.

Touches are observed app-wide (the window, not a single widget) — that's the only thing the native layer can give cheaply, and it's what arbitrary-coordinate features need. For injecting synthetic touches (test automation), use mob core's Mob.Test harness over Erlang distribution; that's a separate concern.

License

MIT.