# Class Ranging

java.lang.Object
com.codename1.nearby.ranging.Ranging

public final class Ranging extends Object

Precision ranging: how far away another device is, and in which direction.

This is ultra-wideband ranging -- Apple's Nearby Interaction on iOS and Jetpack UWB on Android -- which measures distance by timing a radio round trip rather than by guessing from signal strength. Where an RSSI estimate off a Bluetooth advertisement is worth a few meters on a good day, UWB is worth about ten centimeters, and on hardware with multiple antennas it also reports which way the peer is.

##### The shape of a session

Both platforms need the two devices to exchange a token over some channel they already share before any radio ranging can begin, so the API is in two steps and there is no way to collapse them:

    if (!Ranging.isSupported()) {
        return;                       // no UWB radio on this device
    }
    Ranging.prepareSession(RangingRole.CONTROLLER).onResult((session, err) -> {
        if (err != null) {
            return;
        }
        // 1. publish our token however the two apps already talk --
        //    a GATT characteristic from com.codename1.bluetooth is typical
        characteristic.writeValue(session.getLocalToken().toByteArray());

        // 2. when theirs arrives, start ranging
        session.addRangingListener(new RangingAdapter() {
            public void updated(RangingUpdate u) {
                if (u.hasDistance()) {
                    label.setText(Math.round(u.getDistance(RangingUnit.CENTIMETERS)) + " cm");
                }
            }
        });
        session.start(RangingToken.fromByteArray(theirToken));
    });

A session ranges exactly one peer. That is a hard limit of Apple's `NINearbyPeerConfiguration` rather than a simplification, so an app that tracks several peers prepares several sessions -- which is also what the Android port does under the hood.

##### Threading

Every callback here -- `AsyncResource` results and every `RangingListener` method -- is delivered on the EDT.

##### Platform support

- iOS -- Nearby Interaction on devices with a U1 or newer chip (iPhone 11 and later). Peer and accessory ranging, direction where the hardware provides it. Not available on tvOS, watchOS or Mac Catalyst.
- Android -- Jetpack UWB on devices that report the UWB hardware feature. Peer ranging natively; an accessory is ranged by building a token with `RangingToken.forUwbAddress(byte[], int, int, int, byte[])`.
- Simulator, desktop and JavaScript -- a simulated implementation with peers that really move, so ranging UI is developable without hardware. Reports `NearbyAvailability.LOCAL_ONLY`.
- Every other port -- `isSupported()` is `false` and every call fails with `NearbyError.NOT_SUPPORTED`.

- ## Method Summary

  Modifier and Type
  Method
  Description
  `static NearbyAvailability`
  `getAvailability()`
  How usable ranging is at this moment, which is a different question from `isSupported()`: a phone with a U1 chip whose owner denied the permission is supported and unavailable.
  `static RangingCapabilities`
  `getCapabilities()`
  What this device can actually measure.
  `static boolean`
  `isSupported()`
  `true` when this port and this device can range at all.
  `static AsyncResource<RangingSession>`
  `prepareSession(RangingRole role)`
  Allocates a ranging session and, with it, the local token to publish to the peer.
  `static AsyncResource<Boolean>`
  `requestPermissions(NearbyPermission... permissions)`
  Asks for the runtime permissions ranging needs.

  ### Methods inherited from class Object

  `clone, equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`

- ## Method Details

  - ### isSupported

    public static boolean isSupported()

    `true` when this port and this device can range at all.

    This answers for the hardware, not for whether a peer is nearby. It is the query to hide a feature on; use `getAvailability()` to tell a user why a supported feature is not working right now.

  - ### getAvailability

    public static NearbyAvailability getAvailability()

    How usable ranging is at this moment, which is a different question from `isSupported()`: a phone with a U1 chip whose owner denied the permission is supported and unavailable.

    ###### Returns

    the current availability, never null

  - ### getCapabilities

    public static RangingCapabilities getCapabilities()

    What this device can actually measure. Never null: where ranging is absent this is `RangingCapabilities.UNSUPPORTED`, whose every query is `false`.

    ###### Returns

    the capabilities of the local device

  - ### requestPermissions

    public static AsyncResource<Boolean> requestPermissions(NearbyPermission... permissions)

    Asks for the runtime permissions ranging needs.

    Safe to call on every platform: a port with nothing to ask for resolves `true` without showing anything.

    ###### Parameters

    - `permissions`: what the app intends to do

    ###### Returns

    resolves `true` when every requested permission is granted

  - ### prepareSession

    public static AsyncResource<RangingSession> prepareSession(RangingRole role)

    Allocates a ranging session and, with it, the local token to publish to the peer. The session is not ranging yet -- call `RangingSession.start(RangingToken)` once the peer's token arrives.

    ###### Parameters

    - `role`: which end of the session this device is. Ignored on platforms that negotiate roles themselves, but pick one anyway: Android needs exactly one controller.

    ###### Returns

    resolves with the prepared session, or fails with a `NearbyException`