public final class CompanionDevices

  1. Object
  2. CompanionDevices

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

  • AndroidCompanionDeviceManager, 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.

Methods

public static boolean isSupported()true when this port can associate companion devices.
public static NearbyAvailability getAvailability()How usable association is right now.
public static AsyncResource<CompanionDevice> associate(AssociationRequest request)Shows the system device chooser and associates whatever the user picks.
public static List<CompanionDevice> getAssociations()Every association this app currently holds.
public static AsyncResource<Boolean> disassociate(String associationId)Drops an association and the privileges that came with it.
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.
public static void stopObservingPresence(String associationId)Stops watching an association.
public static void addPresenceListener(PresenceListener l)Registers a presence listener.
public static void removePresenceListener(PresenceListener l)Removes a listener added by addPresenceListener.

Inherited methods

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 AssociationRequest
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 String
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 String
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 String
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 PresenceListener
the listener to add

removePresenceListener

public static void removePresenceListener(PresenceListener l)
Removes a listener added by addPresenceListener.

Parameters

l PresenceListener
the listener to remove