# Class CompanionDevices

java.lang.Object
com.codename1.nearby.companion.CompanionDevices

public final class CompanionDevices extends Object

Companion-device association: the OS-managed relationship between this app and one particular accessory.

Associating is not pairing. It is the app telling the operating system "this is my device", through a chooser the OS draws and the user picks from, and getting back privileges that an ordinary Bluetooth scan does not carry:

- The OS watches for the device instead of the app. `startObservingPresence(String)` asks the platform to wake the app when the accessory comes into range, which replaces a scan the app would otherwise run -- and pay for in battery -- forever.
- Scanning stops needing location permission. On Android, finding your own associated device is not the same question as finding out where the user is, and the platform treats it accordingly.
- The user sees one honest prompt naming one device, instead of a blanket "this app wants to find nearby devices".

    AssociationRequest req = new AssociationRequest.Builder()
            .addFilter(DeviceFilter.bleService("180D"))
            .build();
    CompanionDevices.associate(req).onResult((device, err) -> {
        if (err == null) {
            Preferences.set("sensor", device.getId());
            CompanionDevices.startObservingPresence(device.getId());
        }
    });

##### Platform support

- Android -- `CompanionDeviceManager`, with presence observation.
- iOS -- AccessorySetupKit, on iOS 18 and later. The picker returns an accessory the app may then talk to over `com.codename1.bluetooth` without holding the blanket Bluetooth authorization. Earlier iOS versions report `isSupported()` false; there the app scans with `com.codename1.bluetooth` as before.
- Simulator, desktop and JavaScript -- a simulated association store reporting `NearbyAvailability.LOCAL_ONLY`.
- Every other port -- unsupported, and every call fails fast.

- ## Method Summary

  Modifier and Type
  Method
  Description
  `static void`
  `addPresenceListener(PresenceListener l)`
  Registers a presence listener.
  `static AsyncResource<CompanionDevice>`
  `associate(AssociationRequest request)`
  Shows the system device chooser and associates whatever the user picks.
  `static AsyncResource<Boolean>`
  `disassociate(String associationId)`
  Drops an association and the privileges that came with it.
  `static List<CompanionDevice>`
  `getAssociations()`
  Every association this app currently holds.
  `static NearbyAvailability`
  `getAvailability()`
  How usable association is right now.
  `static boolean`
  `isSupported()`
  `true` when this port can associate companion devices.
  `static void`
  `removePresenceListener(PresenceListener l)`
  Removes a listener added by `addPresenceListener(PresenceListener)`.
  `static boolean`
  `startObservingPresence(String associationId)`
  Asks the platform to watch for the device and tell this app when it comes and goes, delivering to every registered `PresenceListener`.
  `static void`
  `stopObservingPresence(String associationId)`
  Stops watching an association.

  ### 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 can associate companion devices.

  - ### getAvailability

    public static NearbyAvailability getAvailability()

    How usable association is right now.

    ###### Returns

    the current availability, never null

  - ### associate

    public static AsyncResource<CompanionDevice> associate(AssociationRequest request)

    Shows the system device chooser and associates whatever the user picks.

    This always involves the user -- there is no way to associate silently on either platform, by design.

    ###### Parameters

    - `request`: what to offer the user

    ###### Returns

    resolves with the associated device, or fails with `NearbyError.USER_CANCELED` when the user dismissed the chooser

  - ### getAssociations

    public static List<CompanionDevice> getAssociations()

    Every association this app currently holds.

    Associations survive restarts, so this is what an app calls on startup to find the accessory it was using last time rather than asking the user again.

    ###### Returns

    the associations, never null and possibly empty

  - ### disassociate

    public static AsyncResource<Boolean> disassociate(String associationId)

    Drops an association and the privileges that came with it.

    ###### Parameters

    - `associationId`: the id from `CompanionDevice.getId()`

    ###### Returns

    resolves `true` once the association is gone

  - ### startObservingPresence

    public static boolean startObservingPresence(String associationId)

    Asks the platform to watch for the device and tell this app when it comes and goes, delivering to every registered `PresenceListener`.

    ###### Parameters

    - `associationId`: the id from `CompanionDevice.getId()`

    ###### Returns

    `true` when the platform accepted the request. `false` where presence observation is unsupported -- the association itself is unaffected, so an app can carry on scanning for the device itself.

  - ### stopObservingPresence

    public static void stopObservingPresence(String associationId)

    Stops watching an association. Idempotent.

    ###### Parameters

    - `associationId`: the id from `CompanionDevice.getId()`

  - ### addPresenceListener

    public static void addPresenceListener(PresenceListener l)

    Registers a presence listener. Callbacks arrive on the EDT.

    Register from the app's `init()`: presence is exactly the event that can arrive during a cold start, because the platform may start the process to deliver it. An event that arrived before any listener existed is replayed to the listeners as soon as the first one registers, so a sighting delivered into a process whose `init()` had not run yet is not lost. At most the 64 most recent are kept.

    This is not background execution. The platform starting the process does not make the application run: Android hands the event to a service, and Codename One does not initialize an app there, because an `init()` may build a `Form` and a service has nowhere to put one. The listener hears about the sighting, in order, when the app next initializes.

    ###### Parameters

    - `l`: the listener to add

  - ### removePresenceListener

    public static void removePresenceListener(PresenceListener l)

    Removes a listener added by `addPresenceListener(PresenceListener)`.

    ###### Parameters

    - `l`: the listener to remove