AltBeacon / android-beacon-library

Allows Android apps to interact with BLE beacons
Apache License 2.0
2.83k stars 834 forks source link

Improved BeaconParser API #1052

Closed davidgyoung closed 5 months ago

davidgyoung commented 2 years ago

The Problem

Failing to configure the proper BeaconParser is a common source of frustration with library users. Library users often don't realize the library only detects AltBeacon out of the box, and then are disappointed when their code fails to detect their beacon that advertises the Eddystone or iBeacon format. The library needs to make this step easy and obvious.

Constraints

This proposal for improvement in the library API is designed to stay within the following constraints:

  1. Make it easier for library users to configure use of their chosen beacon format (AltBeacon, iBeacon, Eddystone, etc.)
  2. Keep ranging and monitoring APIs similar to iOS for interoperability.
  3. Keep intellectual property (e.g. details of the iBeacon format) out of this open source codebase.
  4. Only make active the minimal number of BeaconParsers required for a project, as every additional beacon parser requires extra processing power and battery usage.

Proposal

Modify the Region class so that the BeaconParser is specified for each region. Instead of registering new BeaconParsers with the BeaconManager, the user will have no choice but to specify the BeaconParser at the time a Region is constructed to start ranging or monitoring. This will make it obvious to users what kind of beacon they are telling the library to look for.

Under the hood, the library will register a new parser as needed if the parser specified in the region has not yet been added. When ranging or monitoring are stopped, if no remaining region uses a parser, that parser will be removed.

Example

instead of:

beaconManager.getBeaconParsers().add(new BeaconParser().setBeaconLayout(BeaconParser.EDDYSTONE_UID_LAYOUT));
val region = Region("my-eddystone-beacons", "0x12345678", null, null)
beaconManager.startMonitoring(region)

The new API would be this:

val region = Region("my-eddystone-beacons", BeaconTypes.EDDYSTONE_UID, "0x12345678", null, null)
beaconManager.startMonitoring(region)

or perhaps

val region = Region.forEddystoneUid("my-eddystone-beacons", "0x12345678", null)
beaconManager.startMonitoring(region)

Deprecation Plan

The old API where a Region is constructed without specifying a parser will be deprecated with a warning added in the log when used. The beaconManager.getBeaconParsers() will be deprecated as well.

davidgyoung commented 2 years ago

Thinking more, the system probably should still support wildcard regions. It is common to do this with the current api:

val region = Region("all beacons", null, null, null)

The above will match all beacons of all formats for registered beacon parsers. This is highly useful for many cases and less awkward than registering multiple wildcard regions, one for each beacon parser. This should still be supported somehow, perhaps like this:

val region = Region("all beacons") // a wildcard region matching all beacons of all known parser types

Similarly, we could allow setting a default beacon parser for the common case where users only use one at a time, for example:

BeaconTypes.default = BeaconTypes.EDDYSTONE_UID

Then once the above is set, any calls to the old, non-explicit syntax will work without warnings:

val region = Region("my-eddystone-beacons", "0x12345678", null, null)

The above would effectively be the same as this:

val region = Region("my-eddystone-beacons", BeaconTypes.EDDYSTONE_UID, "0x12345678", null, null)

davidgyoung commented 5 months ago

Closed in favor of #1061