Packages
mob
0.6.1
0.7.20
0.7.19
0.7.18
0.7.17
0.7.16
0.7.15
0.7.14
0.7.13
0.7.12
0.7.11
0.7.10
0.7.9
0.7.8
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.26
0.6.25
0.6.24
0.6.23
0.6.22
0.6.21
0.6.20
0.6.19
0.6.18
0.6.17
0.6.16
0.6.15
0.6.14
0.6.13
0.6.12
0.6.11
0.6.10
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.2
0.6.1
0.6.0
0.5.18
0.5.17
0.5.16
0.5.15
0.5.14
0.5.11
0.5.10
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.0
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.0
0.1.0
BEAM-on-device mobile framework for Elixir
Current section
Files
Jump to
Current section
Files
lib/mob/device/android.ex
defmodule Mob.Device.Android do
@moduledoc """
Android-specific device events. Symmetrical with `Mob.Device.IOS`.
Messages are tagged `:mob_device_android`:
{:mob_device_android, event}
{:mob_device_android, event, payload}
## Subscribe
Mob.Device.Android.subscribe()
## Status
Android event surfacing is **pending implementation**. The Elixir API is
stable; subscribing succeeds but no events fire until
`ProcessLifecycleObserver` and `ComponentCallbacks2` are wired up in the
generated app's Java/Kotlin side. Tracked in `PLAN.md` under "Native event
surface — Batch 1".
## Planned events
- `:doze_mode_changed` — Doze mode entered/exited (no iOS counterpart)
- `:idle_mode_changed` — light idle mode (`ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED`)
- `:trim_memory` with level — `ComponentCallbacks2.onTrimMemory(level)`
- `:airplane_mode_changed` — `ACTION_AIRPLANE_MODE_CHANGED`
- `:user_present` — `ACTION_USER_PRESENT` (device unlocked)
- all cross-platform `Mob.Device` events re-emitted under this tag too
"""
use GenServer
@spec start_link(keyword()) :: GenServer.on_start()
def start_link(opts \\ []) do
GenServer.start_link(__MODULE__, opts, name: __MODULE__)
end
@doc "Subscribe the calling process to Android-specific device events."
@spec subscribe() :: :ok
def subscribe do
GenServer.call(__MODULE__, {:subscribe, self()})
end
@doc "Unsubscribe the calling process."
@spec unsubscribe() :: :ok
def unsubscribe do
GenServer.call(__MODULE__, {:unsubscribe, self()})
end
# ── GenServer ─────────────────────────────────────────────────────────────
@impl true
def init(_opts), do: {:ok, %{subscribers: %{}, monitors: %{}}}
@impl true
def handle_call({:subscribe, pid}, _from, state) do
state = put_subscriber(state, pid)
{:reply, :ok, state}
end
@impl true
def handle_call({:unsubscribe, pid}, _from, state) do
state = drop_subscriber(state, pid)
{:reply, :ok, state}
end
@impl true
def handle_call(:__test_subscribers__, _from, state) do
{:reply, state.subscribers, state}
end
@impl true
def handle_info({:mob_device_android, _} = msg, state) do
fan_out(state, msg)
{:noreply, state}
end
def handle_info({:mob_device_android, _, _} = msg, state) do
fan_out(state, msg)
{:noreply, state}
end
def handle_info({:DOWN, _ref, :process, pid, _reason}, state) do
{:noreply, drop_subscriber(state, pid)}
end
def handle_info(_other, state), do: {:noreply, state}
defp put_subscriber(state, pid) do
monitors =
case Map.get(state.monitors, pid) do
nil -> Map.put(state.monitors, pid, Process.monitor(pid))
_ref -> state.monitors
end
%{state | subscribers: Map.put(state.subscribers, pid, true), monitors: monitors}
end
defp drop_subscriber(state, pid) do
case Map.pop(state.monitors, pid) do
{nil, monitors} ->
%{state | monitors: monitors}
{ref, monitors} ->
Process.demonitor(ref, [:flush])
%{state | subscribers: Map.delete(state.subscribers, pid), monitors: monitors}
end
end
defp fan_out(state, msg) do
Enum.each(state.subscribers, fn {pid, _} -> send(pid, msg) end)
end
end