/** Notifies the listener about possible region-based events */ private void notifyListener() { IBeacon newNearestBeacon = null; if (_arrOrderedIBeacons.size() > 0) newNearestBeacon = _arrOrderedIBeacons.get(0); // Case 1: enter iBeacon region from nowhere if (_previousNearestIBeacon == null && newNearestBeacon != null) { _listener.enterRegion(newNearestBeacon); _previousNearestIBeacon = newNearestBeacon; } // Case 2: keep in the same iBeacon region, update proximity else if (_previousNearestIBeacon != null && newNearestBeacon != null && _previousNearestIBeacon.equals(newNearestBeacon)) { _previousNearestIBeacon = newNearestBeacon; } // Case 3: enter a different iBeacon region (roaming) else if (_previousNearestIBeacon != null && newNearestBeacon != null && !_previousNearestIBeacon.equals(newNearestBeacon)) { _listener.exitRegion(_previousNearestIBeacon); _listener.enterRegion(newNearestBeacon); _previousNearestIBeacon = newNearestBeacon; } // Case 4: leave iBeacon region else if (_previousNearestIBeacon != null && newNearestBeacon == null) { _listener.exitRegion(_previousNearestIBeacon); _previousNearestIBeacon = null; } }
@Override public int compare(IBeacon b1, IBeacon b2) { String distance_1 = String.valueOf(b1.getProximity()); String distance_2 = String.valueOf(b2.getProximity()); return distance_1.compareTo(distance_2); }
/** * Finds an ibeacon in the list of previously discovered ibeacons * * @param b ibeacon to find * @return <code>null</code> if the ibeacon is not found, the existing copy of the ibeacon * otherwise. */ private IBeacon findIfExists(IBeacon b) { for (int i = 0; i < _arrOrderedIBeacons.size(); i++) { IBeacon existing = (IBeacon) _arrOrderedIBeacons.get(i); if (existing.equals(b)) return existing; } return null; }
@Override public void didRangeBeaconsInRegion(Collection<IBeacon> iBeacons, Region region) { for (IBeacon iBeacon : iBeacons) { iBeacon.requestData(this); String displayString = iBeacon.getProximityUuid() + " " + iBeacon.getMajor() + " " + iBeacon.getMinor() + "\n"; Log.d(TAG, displayString); } myBeacons.clear(); myBeacons.addAll(iBeacons); }
@Override public void iBeaconDataUpdate(IBeacon iBeacon, IBeaconData iBeaconData, DataProviderException e) { if (e != null) { Log.d(TAG, "data fetch error:" + e); } if (iBeaconData != null) { String displayString = iBeacon.getProximityUuid() + " " + iBeacon.getMajor() + " " + iBeacon.getMinor() + "\n" + "Welcome message:" + iBeaconData.get("welcomeMessage"); Log.d(TAG, displayString); } }
/** * Obtains BLE advertisement data and checks if it is an iBeacon * * @param data the advertisement data * @return the IBeacon found or null if not an iBeacon */ private IBeacon parseAdvertisementData(byte[] data) { Log.i(Utils.LOG_TAG, Arrays.toString(data)); // First, check the prefix for our beacons if (data[0] == 0x02 && data[1] == 0x01 && data[4] == (byte) 0xFF && data[7] == 0x02) { // iBeacon candidate byte[] uuid = Arrays.copyOfRange(data, ADV_PREFIX_LENGTH, ADV_PREFIX_LENGTH + ADV_UUID_LENGTH); // Now filter beacons based on UUID if any if (_uuid == null || Arrays.equals(_uuid, uuid)) { int offset = ADV_PREFIX_LENGTH + ADV_UUID_LENGTH; IBeacon ibeacon = new IBeacon(); ibeacon.setUuid(uuid); int major = ((data[offset] << 8) & 0x0000ff00) | (data[offset + 1] & 0x000000ff); ibeacon.setMajor(major); int minor = ((data[offset + 2] << 8) & 0x0000ff00) | (data[offset + 3] & 0x000000ff); ibeacon.setMinor(minor); ibeacon.setPowerValue(data[offset + 4]); return ibeacon; } } return null; }
@Override public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) { Log.i(Utils.LOG_TAG, "BLE packet received"); IBeacon newBeacon = parseAdvertisementData(scanRecord); if (newBeacon == null) return; newBeacon.setMacAddress(device.getAddress()); // If already discovered, then just refresh the RSSI of the existing instance and return if (_arrOrderedIBeacons.contains(newBeacon)) { double newDistance = calculateDistance(newBeacon.getPowerValue(), rssi); IBeacon previousIBeaconInfo = findIfExists(newBeacon); double oldDistance = previousIBeaconInfo.getProximity(); if (newDistance < oldDistance) { Log.i(Utils.LOG_TAG, "Updating distance"); previousIBeaconInfo.setProximity(newDistance); // Sort again Collections.sort(_arrOrderedIBeacons, new IBeaconProximityComparator()); } return; } newBeacon.setEasiBeacon(false); if (device.getName() != null) { Log.e("device.getName()", String.valueOf(device.getName())); if (device.getName().startsWith(EASIBEACON_IDPREFIX)) { newBeacon.setEasiBeacon(true); String version = device.getName().substring(EASIBEACON_IDPREFIX.length()); newBeacon.setVersionModel(version); if (newBeacon.getVersion() == 1) { // Version 1 is always connectable newBeacon.setConnectable(true); } else if (newBeacon.getVersion() == 2) { newBeacon.setConnectable(getConnectable(scanRecord)); if (!newBeacon.isConnectable()) newBeacon.setEasiBeacon(false); // If not connectable we will report it as unknown } } } // Review this Log.i( Utils.LOG_TAG, device.getName() + " " + device.getAddress() + " " + newBeacon.getPowerValue() + " " + rssi + " Connectable: " + newBeacon.isConnectable()); newBeacon.setProximity(calculateDistance(newBeacon.getPowerValue(), rssi)); if (!_arrOrderedIBeacons.contains(newBeacon)) { _arrOrderedIBeacons.add(newBeacon); Collections.sort(_arrOrderedIBeacons, new IBeaconProximityComparator()); _listener.beaconFound(newBeacon); // Every time a new beacon is found, reset the timeout _timeoutHandler.removeCallbacks(timeoutTask); _timeoutHandler.postDelayed(timeoutTask, IBeaconProtocol.SCANNING_PERIOD); } }