Description
I am using the AltBeacon library in my project to scan for iBeacon devices, but it only detects one device and does not update the `LiveData` object when there are multiple iBeacon devices nearby.
I have tested this behavior with the official Kotlin reference app (`BeaconReferenceApplication`) from the AltBeacon library, and the issue persists there as well. The ranging observer callback is supposed to fire every second, but it does not correctly identify or update the list of iBeacon devices when there are more than one.
Environment:
- Android Device Model: Google Pixel 9 Pro (Android 15), Samsung A50 (Android 11).
- Android Beacon Library Versions: 2.20 and 2.20.6 (same results).
Notes:
- My project uses an adapter to display the data, but this is the only difference from the official reference app.
- I have noticed dependencies in the library that might require specific versions. Where can I find the required versions of dependencies for the library?
Any insights on resolving this issue or ensuring the library detects and updates data for all nearby iBeacon devices would be appreciated!
1. BeaconReferenceApplication.kt
package .altbeacon.beaconreference
import android.app.*
import android.content.Context
import android.content.Intent
import android.util.Log
import androidx.core.app.NotificationCompat
import androidx.lifecycle.Observer
import .altbeacon.beacon.*
import .altbeacon.bluetooth.BluetoothMedic
class BeaconReferenceApplication: Application() {
// the region definition is a wildcard that matches all beacons regardless of identifiers.
// if you only want to detect beacons with a specific UUID, change the id1 paremeter to
// a UUID like Identifier.parse("2F234454-CF6D-4A0F-ADF2-F4911BA9FFA6")
private val bleUUID = "A134D0B2-1DA2-1BA7-C94C-E8E00C9F7A2D" //"2D7A9F0C-E0E8-4CC9-A71B-A21DB2D034A1"
var region = Region("all-beacons", Identifier.parse(bleUUID), null, null)
override fun onCreate() {
super.onCreate()
val beaconManager = BeaconManager.getInstanceForApplication(this)
BeaconManager.setDebug(true)
// By default the AndroidBeaconLibrary will only find AltBeacons. If you wish to make it
// find a different type of beacon, you must specify the byte layout for that beacon's
// advertisement with a line like below. The example shows how to find a beacon with the
// same byte layout as AltBeacon but with a beaconTypeCode of 0xaabb. To find the proper
// layout expression for other beacon types, do a web search for "setBeaconLayout"
// including the quotes.
//
//beaconManager.getBeaconParsers().clear();
//beaconManager.getBeaconParsers().add(new BeaconParser().
// setBeaconLayout("m:0-1=4c00,i:2-24v,p:24-24"));
// By default the AndroidBeaconLibrary will only find AltBeacons. If you wish to make it
// find a different type of beacon like Eddystone or iBeacon, you must specify the byte layout
// for that beacon's advertisement with a line like below.
//
// If you don't care about AltBeacon, you can clear it from the defaults:
//beaconManager.getBeaconParsers().clear()
// Uncomment if you want to block the library from updating its distance model database
//BeaconManager.setDistanceModelUpdateUrl("")
// The example shows how to find iBeacon.
val parser = BeaconParser().
setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24")
parser.setHardwareAssistManufacturerCodes(arrayOf(0x004c).toIntArray())
beaconManager.getBeaconParsers().add(
parser)
// enabling debugging will send lots of verbose debug information from the library to Logcat
// this is useful for troubleshooting problmes
// BeaconManager.setDebug(true)
// The BluetoothMedic code here, if included, will watch for problems with the bluetooth
// stack and optionally:
// - power cycle bluetooth to recover on bluetooth problems
// - periodically do a proactive scan or transmission to verify the bluetooth stack is OK
// BluetoothMedic.getInstance().legacyEnablePowerCycleOnFailures(this) // Android 4-12 only
// BluetoothMedic.getInstance().enablePeriodicTests(this, BluetoothMedic.SCAN_TEST + BluetoothMedic.TRANSMIT_TEST)
//setupBeaconScanning()
}
fun setupBeaconScanning() {
val beaconManager = BeaconManager.getInstanceForApplication(this)
// By default, the library will scan in the background every 5 minutes on Android 4-7,
// which will be limited to scan jobs scheduled every ~15 minutes on Android 8+
// If you want more frequent scanning (requires a foreground service on Android 8+),
// configure that here.
// If you want to continuously range beacons in the background more often than every 15 mintues,
// you can use the library's built-in foreground service to unlock this behavior on Android
// 8+. the method below shows how you set that up.
try {
setupForegroundService()
}
catch (e: SecurityException) {
// On Android TIRAMUSU + this security exception will happen
// if location permission has not been granted when we start
// a foreground service. In this case, wait to set this up
// until after that permission is granted
Log.d(TAG, "Not setting up foreground service scanning until location permission granted by user")
return
}
//beaconManager.setEnableScheduledScanJobs(false);
//beaconManager.setBackgroundBetweenScanPeriod(0);
//beaconManager.setBackgroundScanPeriod(1100);
// Ranging callbacks will drop out if no beacons are detected
// Monitoring callbacks will be delayed by up to 25 minutes on region exit
// beaconManager.setIntentScanningStrategyEnabled(true)
// The code below will start "monitoring" for beacons matching the region definition at the top of this file
beaconManager.startMonitoring(region)
beaconManager.startRangingBeacons(region)
// These two lines set up a Live Data observer so this Activity can get beacon data from the Application class
val regionViewModel = BeaconManager.getInstanceForApplication(this).getRegionViewModel(region)
// observer will be called each time the monitored regionState changes (inside vs. outside region)
regionViewModel.regionState.observeForever( centralMonitoringObserver)
// observer will be called each time a new list of beacons is ranged (typically ~1 second in the foreground)
regionViewModel.rangedBeacons.observeForever( centralRangingObserver)
}
fun setupForegroundService() {
val builder = Notification.Builder(this, "BeaconReferenceApp")
builder.setSmallIcon(R.drawable.ic_launcher_background)
builder.setContentTitle("Scanning for Beacons")
val intent = Intent(this, ListDevices::class.java)
val pendingIntent = PendingIntent.getActivity(
this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT + PendingIntent.FLAG_IMMUTABLE
)
builder.setContentIntent(pendingIntent);
val channel = NotificationChannel("beacon-ref-notification-id",
"My Notification Name", NotificationManager.IMPORTANCE_DEFAULT)
channel.setDescription("My Notification Channel Description")
val notificationManager = getSystemService(
Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel);
builder.setChannelId(channel.getId());
Log.d(TAG, "Calling enableForegroundServiceScanning")
BeaconManager.getInstanceForApplication(this).enableForegroundServiceScanning(builder.build(), 456);
Log.d(TAG, "Back from enableForegroundServiceScanning")
}
val centralMonitoringObserver = Observer<Int> { state ->
if (state == MonitorNotifier.OUTSIDE) {
Log.d(TAG, "outside beacon region: "+region)
}
else {
Log.d(TAG, "inside beacon region: "+region)
sendNotification()
}
}
val centralRangingObserver = Observer<Collection<Beacon>> { beacons ->
val rangeAgeMillis = System.currentTimeMillis() - (beacons.firstOrNull()?.lastCycleDetectionTimestamp ?: 0)
if (rangeAgeMillis < 10000) {
Log.d("BeaconReferenceApplication", "Ranged: ${beacons.count()} beacons")
for (beacon: Beacon in beacons) {
Log.d(TAG, "$beacon about ${beacon.distance} meters away")
}
}
else {
Log.d("BeaconReferenceApplication", "Ignoring stale ranged beacons from $rangeAgeMillis millis ago")
}
}
private fun sendNotification() {
val builder = NotificationCompat.Builder(this, "beacon-ref-notification-id")
.setContentTitle("Beacon Reference Application")
.setContentText("A beacon is nearby.")
.setSmallIcon(R.drawable.ic_launcher_background)
val stackBuilder = TaskStackBuilder.create(this)
stackBuilder.addNextIntent(Intent(this, ListDevices::class.java))
val resultPendingIntent = stackBuilder.getPendingIntent(
0,
PendingIntent.FLAG_UPDATE_CURRENT + PendingIntent.FLAG_IMMUTABLE
)
builder.setContentIntent(resultPendingIntent)
val channel = NotificationChannel("beacon-ref-notification-id",
"My Notification Name", NotificationManager.IMPORTANCE_DEFAULT)
channel.setDescription("My Notification Channel Description")
val notificationManager = getSystemService(
Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel);
builder.setChannelId(channel.getId());
notificationManager.notify(1, builder.build())
}
companion object {
val TAG = "BeaconReference"
}
}
2. ListDevices.kt
package .altbeacon.beaconreference
import android.annotation.SuppressLint
import androidx.appcompat.app.AppCompatActivity
import androidx.activity.OnBackPressedCallback
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.widget.Button
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.Observer
import .altbeacon.beacon.Beacon
import .altbeacon.beacon.BeaconManager
import .altbeacon.beacon.MonitorNotifier
import .altbeacon.adapters.BleDevicesAdapter
import .altbeacon.beacon.permissions.BeaconScanPermissionsActivity
class ListDevices : AppCompatActivity() {
//private lateinit var bluetoothServices: BluetoothServices
private lateinit var beaconReferenceApplication: BeaconReferenceApplication
private lateinit var recyclerView: RecyclerView
private lateinit var bleDevicesAdapter: BleDevicesAdapter
//private lateinit var iBeaconsView: IBeaconsView
@SuppressLint("CheckResult")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_list_devices)
//bluetoothServices = BluetoothServices(this)
recyclerView = findViewById(R.id.resultScannerDevices)
recyclerView.layoutManager = LinearLayoutManager (this)
bleDevicesAdapter = BleDevicesAdapter (this) { device ->
onDeviceClick(device)
}
recyclerView.adapter = bleDevicesAdapter
//iBeaconsView = ViewModelProvider(this)[IBeaconsView::class.java]
//iBeaconsView.beacons.observe(this) {deviceList ->
// bleDevicesAdapter.updateDevices(deviceList)
// }
beaconReferenceApplication = application as BeaconReferenceApplication
//I set up a Live Data observer for the signaling data
val regionViewModel = BeaconManager.getInstanceForApplication(this).getRegionViewModel(beaconReferenceApplication.region)
regionViewModel.regionState.observe(this, monitoringObserver)
regionViewModel.rangedBeacons.observe(this, rangingObserver)
//check if all permissions are accepted
if (!BeaconScanPermissionsActivity.allPermissionsGranted(this, true)){
// permissions are not supported and prompt the user
val intent = Intent(this, BeaconScanPermissionsActivity::class.java)
intent.putExtra("backgroundAccessRequested", true)
startActivity(intent)
} else {
//permissions are accepted and start foreground service and scan
if (BeaconManager.getInstanceForApplication(this).monitoredRegions.size == 0){
(application as BeaconReferenceApplication).setupBeaconScanning()
val beaconManager = BeaconManager.getInstanceForApplication(this)
beaconManager.startMonitoring(beaconReferenceApplication.region)
beaconManager.startRangingBeacons(beaconReferenceApplication.region)
}
if (BeaconManager.getInstanceForApplication(this).rangedRegions.size == 0){
val beaconManager = BeaconManager.getInstanceForApplication(this)
beaconManager.startRangingBeacons(beaconReferenceApplication.region)
beaconManager.startMonitoring(beaconReferenceApplication.region)
}
}
val stopScaning: Button = findViewById(R.id.stopScaning)
stopScaning.setOnClickListener {
val beaconManager = BeaconManager.getInstanceForApplication(this)
beaconManager.stopRangingBeacons(beaconReferenceApplication.region)
beaconManager.stopMonitoring(beaconReferenceApplication.region)
startActivity(Intent(this, MainActivity::class.java))
finishAffinity()
}
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
val beaconManager = BeaconManager.getInstanceForApplication(this@ListDevices)
beaconManager.stopRangingBeacons(beaconReferenceApplication.region)
beaconManager.stopMonitoring(beaconReferenceApplication.region)
finish()
}
})
}
private val monitoringObserver = Observer<Int> {state ->
if (state == MonitorNotifier.OUTSIDE){
Log.d("RESULT_SCAN", "nu este nimic în jur")
} else {
Log.d("RESULT_SCAN", "ceva este înapropriere")
}
}
private val rangingObserver = Observer<Collection<Beacon>> {beacons ->
if (BeaconManager.getInstanceForApplication(this).rangedRegions.size > 0){
//beacons.sortedBy { it.distance }
beacons .forEach {
Log.d("RESULT_SCAN", "Nume ${it.bluetoothName} mac adresa ${it.bluetoothAddress}")
//val iBeaconDevice
val iBeacon = IBeacon(it.bluetoothAddress)
iBeacon.uuid = it.id1.toString()
iBeacon.major = it.id2.toInt()
iBeacon.minor = it.id3.toInt()
iBeacon.rssi = it.rssi
bleDevicesAdapter.updateDevices(listOf(iBeacon))
}
}
}
private fun onDeviceClick(device: IBeacon) {
val beaconManager = BeaconManager.getInstanceForApplication(this)
beaconManager.stopRangingBeacons(beaconReferenceApplication.region)
beaconManager.stopMonitoring(beaconReferenceApplication.region)
val intentConnecting = Intent(applicationContext, CommunicationWithTheDevice::class.java)
val selectedDevice = device.macAddress
intentConnecting.putExtra("connectingTo", selectedDevice)
startActivity(intentConnecting)
finishAffinity()
}
}
3. BleDevicesAdapter
package .altbeacon.adapters
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import android.widget.Button
import .altbeacon.beaconreference.IBeacon
import .altbeacon.beaconreference.DataServices
import .altbeacon.beaconreference.R
class BleDevicesAdapter (private val context: Context, private val devices: MutableList<IBeacon> = mutableListOf(), private val onItemClick: (IBeacon) ->Unit) : RecyclerView.Adapter<BleDevicesAdapter.ViewHolder>() {
private val dataServices = DataServices()
inner class ViewHolder (itemView: View) : RecyclerView.ViewHolder (itemView) {
private val resultScannerDevices: Button = itemView.findViewById(R.id.resultScannerDevicesButton)
//private val resultScannerDevices: View = itemView.findViewById(R.id.resultScannerDevices)
fun bind(resultBleDevice: IBeacon) {
val categoryTextWidgets = dataServices.getNameByMajor(context, resultBleDevice.major.toString())
val numberTextWidgets = dataServices.getNumberByMinor(context, resultBleDevice.major.toString(), resultBleDevice.minor.toString())
val informationWidgets = dataServices.getInformationByNumber(context, resultBleDevice.major.toString(), resultBleDevice.minor.toString())
resultScannerDevices.text = context.getString(R.string.DeviceWidgetName, categoryTextWidgets, numberTextWidgets, informationWidgets)
resultScannerDevices.setOnClickListener{
onItemClick(resultBleDevice)
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.item_device, parent, false)
return ViewHolder(itemView)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val device = devices[position]
holder.bind(device)
}
override fun getItemCount() = devices.size
fun updateDevices (newDevices: List<IBeacon>){
val previousSize = devices.size
devices.clear()
devices.addAll(newDevices)
if (previousSize <devices.size) {
notifyItemRangeInserted(previousSize, devices.size - previousSize)
} else if (previousSize > devices.size) {
notifyItemRangeRemoved(devices.size, previousSize - devices.size)
} else {
// notifyDataSetChanged()
}
}
}
NOTE: I am visually impaired and working on this alone, which makes it quite challenging for me. This is why I am reaching out for help.
Steps to reproduce:
1. Integrate the AltBeacon library into a project.
2. Start scanning for iBeacon devices using a foreground service.
3. Observe the `LiveData` updates or use the example app provided in the library with multiple iBeacon devices nearby.
Expected Behavior:
- The library should detect all nearby iBeacon devices and update the `LiveData` object with each detected device.
Actual Behavior:
- The library detects only one iBeacon device and does not update `LiveData` when additional devices are present.
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1742402749a4437240.html
评论列表(0条)