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