Cross-platform device events and queries.
Mob.Device is the single subscription point for OS-level events that exist
on both iOS and Android. The native side (iOS NotificationCenter,
Android ProcessLifecycleObserver) registers observers once at startup and
emits each event as a tagged tuple to a registered dispatcher pid; this
GenServer fans the events out to subscribers by category.
Subscribe
Mob.Device.subscribe() # default categories
Mob.Device.subscribe(:all) # everything
Mob.Device.subscribe([:app, :power])Subscribers receive {:mob_device, atom} or {:mob_device, atom, payload}
in their mailbox. Default categories are :app, :display, :audio, :memory.
Categories and events
:app—:will_resign_active,:did_become_active,:did_enter_background,:will_enter_foreground,:will_terminate:display—:screen_off,:screen_on,{:orientation_changed, :portrait | :portrait_upside_down | :landscape_left | :landscape_right}:audio—:audio_interrupted,:audio_resumed,:audio_route_changed:appearance—{:color_scheme_changed, :light | :dark}:power—{:battery_state_changed, :unplugged | :charging | :full | :unknown},{:battery_level_changed, integer},{:low_power_mode_changed, boolean}:thermal—{:thermal_state_changed, :nominal | :fair | :serious | :critical}:memory—:memory_warning
Platform-specific events with no cross-platform counterpart go through
Mob.Device.IOS / Mob.Device.Android instead.
Queries
Mob.Device.battery_level() # 0..100 | -1 if unknown
Mob.Device.battery_state() # :unplugged | :charging | :full | :unknown
Mob.Device.thermal_state() # :nominal | :fair | :serious | :critical
Mob.Device.low_power_mode?() # boolean
Mob.Device.foreground?() # boolean
Mob.Device.os_version() # binary
Mob.Device.model() # binary
Mob.Device.orientation() # :portrait | :landscape_left | ...Orientation
orientation/0 reports the current interface orientation; subscribe to
:display to get {:mob_device, :orientation_changed, orientation} when the
device rotates.
Mob.Device.orientation() # :portrait
Mob.Device.lock_orientation(:landscape) # force landscape (either side)
Mob.Device.unlock_orientation() # follow the sensor againLocking forces the orientation regardless of the OS auto-rotate-lock setting
(Android setRequestedOrientation; iOS supported-orientations + a geometry
request). It is global to the app — a screen that wants to be landscape-only
should lock_orientation/1 on enter and unlock_orientation/0 on leave.
Summary
Types
A lock request. :landscape / :portrait allow either side of that axis;
the four concrete values pin a single orientation.
A concrete interface orientation.
Functions
Current battery level (0..100), or -1 if unknown.
Current battery state — :unplugged | :charging | :full | :unknown.
Returns the configured list of valid categories.
Returns a specification to start this module under a supervisor.
True if the app is currently in the foreground.
Lock the app to orientation, overriding the OS auto-rotate setting.
True if Low Power Mode (iOS) / Power Save Mode (Android) is on.
Device model (e.g. "iPhone", "Pixel 8").
Hands a URL to the OS to open in the default browser/handler.
Current interface orientation — :portrait | :portrait_upside_down | :landscape_left | :landscape_right, or :unknown if it can't be determined.
OS version string (e.g. "17.4").
Start the dispatcher. Called from Mob.Application (or the app supervision
tree). After start, the registered NIF dispatcher pid is this GenServer.
Subscribe the calling process to device events.
Current thermal state — :nominal | :fair | :serious | :critical.
Release an orientation lock; the app follows the sensor again.
Unsubscribe the calling process from all categories.
Types
@type category() ::
:app | :display | :audio | :appearance | :power | :thermal | :memory
@type event() :: atom()
@type lock() ::
:portrait
| :portrait_upside_down
| :landscape
| :landscape_left
| :landscape_right
A lock request. :landscape / :portrait allow either side of that axis;
the four concrete values pin a single orientation.
@type orientation() ::
:portrait
| :portrait_upside_down
| :landscape_left
| :landscape_right
| :unknown
A concrete interface orientation.
Functions
@spec battery_level() :: integer()
Current battery level (0..100), or -1 if unknown.
@spec battery_state() :: :unplugged | :charging | :full | :unknown
Current battery state — :unplugged | :charging | :full | :unknown.
@spec categories() :: [category()]
Returns the configured list of valid categories.
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec foreground?() :: boolean()
True if the app is currently in the foreground.
@spec lock_orientation(lock()) :: :ok | {:error, :invalid}
Lock the app to orientation, overriding the OS auto-rotate setting.
Accepts :portrait, :portrait_upside_down, :landscape (either side),
:landscape_left, or :landscape_right. Returns {:error, :invalid} for
anything else. Global to the app; pair with unlock_orientation/0.
@spec low_power_mode?() :: boolean()
True if Low Power Mode (iOS) / Power Save Mode (Android) is on.
@spec model() :: String.t()
Device model (e.g. "iPhone", "Pixel 8").
@spec open_url(String.t()) :: :ok
Hands a URL to the OS to open in the default browser/handler.
Fire-and-forget. Returns :ok immediately; failures (malformed URL, no
registered handler) are logged but not raised.
@spec orientation() :: orientation()
Current interface orientation — :portrait | :portrait_upside_down | :landscape_left | :landscape_right, or :unknown if it can't be determined.
@spec os_version() :: String.t()
OS version string (e.g. "17.4").
@spec start_link(keyword()) :: GenServer.on_start()
Start the dispatcher. Called from Mob.Application (or the app supervision
tree). After start, the registered NIF dispatcher pid is this GenServer.
Subscribe the calling process to device events.
Accepts a single category atom, a list of categories, or :all.
Default is [:app, :display, :audio, :appearance, :memory].
Mob.Device.subscribe()
Mob.Device.subscribe(:thermal)
Mob.Device.subscribe([:app, :power, :thermal])
Mob.Device.subscribe(:all)
@spec thermal_state() :: :nominal | :fair | :serious | :critical
Current thermal state — :nominal | :fair | :serious | :critical.
@spec unlock_orientation() :: :ok
Release an orientation lock; the app follows the sensor again.
@spec unsubscribe() :: :ok
Unsubscribe the calling process from all categories.