# `Mob.Device`
[🔗](https://github.com/genericjam/mob/blob/master/lib/mob/device.ex#L1)

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`
- `: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

# `category`

```elixir
@type category() ::
  :app | :display | :audio | :appearance | :power | :thermal | :memory
```

# `event`

```elixir
@type event() :: atom()
```

# `battery_level`

```elixir
@spec battery_level() :: integer()
```

Current battery level (0..100), or -1 if unknown.

# `battery_state`

```elixir
@spec battery_state() :: :unplugged | :charging | :full | :unknown
```

Current battery state — `:unplugged | :charging | :full | :unknown`.

# `categories`

```elixir
@spec categories() :: [category()]
```

Returns the configured list of valid categories.

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `foreground?`

```elixir
@spec foreground?() :: boolean()
```

True if the app is currently in the foreground.

# `low_power_mode?`

```elixir
@spec low_power_mode?() :: boolean()
```

True if Low Power Mode (iOS) / Power Save Mode (Android) is on.

# `model`

```elixir
@spec model() :: String.t()
```

Device model (e.g. "iPhone", "Pixel 8").

# `open_url`

```elixir
@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.

# `os_version`

```elixir
@spec os_version() :: String.t()
```

OS version string (e.g. "17.4").

# `start_link`

```elixir
@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`

```elixir
@spec subscribe(category() | [category()] | :all) :: :ok
```

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)

# `thermal_state`

```elixir
@spec thermal_state() :: :nominal | :fair | :serious | :critical
```

Current thermal state — `:nominal | :fair | :serious | :critical`.

# `unsubscribe`

```elixir
@spec unsubscribe() :: :ok
```

Unsubscribe the calling process from all categories.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
