Reputation: 11
I'm try to scan Eddystone TLM beacon using Android Beacon library foreground service or using JobScheduler-based scans. Android version 29. My code is android-beacon-library-reference. I'm add code for Eddystone TLM from documentation and beacon fouded and transmit data good. But when I uncomment code in BeaconReferenceApplication class for start foreground scan (or for JobScheduler-based scans) my beacon newer founded. Can anybody send me sample work example for scanning beacon in foreground? Оr explain me, what i need to do for make code working. Below application code:
public class BeaconMonitoringActivity extends Activity implements BeaconConsumer, RangeNotifier{
protected static final String TAG = "BeaconMonitoringActivity";
private BeaconManager mBeaconManager = BeaconManager.getInstanceForApplication(this);
BackgroundPowerSaver backgroundPowerSaver;
@RequiresApi(api = Build.VERSION_CODES.M)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ranging);
backgroundPowerSaver = new BackgroundPowerSaver(this);
mBeaconManager.setBackgroundScanPeriod(5000L);
mBeaconManager.setBackgroundBetweenScanPeriod(6000L);
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case 1: {
if(grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// {beacon code}
}
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
}
@Override
public void onResume() {
super.onResume();
mBeaconManager = BeaconManager.getInstanceForApplication(this.getApplicationContext());
// Detect the main Eddystone-UID frame:
mBeaconManager.getBeaconParsers().add(new BeaconParser().
setBeaconLayout(BeaconParser.EDDYSTONE_UID_LAYOUT));
// Detect the telemetry Eddystone-TLM frame:
mBeaconManager.getBeaconParsers().add(new BeaconParser().
setBeaconLayout(BeaconParser.EDDYSTONE_TLM_LAYOUT));
mBeaconManager.bind(this);
}
public void onBeaconServiceConnect() {
Region region = new Region("all-beacons-region", null, null, null);
try {
mBeaconManager.startRangingBeaconsInRegion(region);
} catch (RemoteException e) {
e.printStackTrace();
}
mBeaconManager.addRangeNotifier(this);
}
@Override
public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
for (Beacon beacon: beacons) {
if (beacon.getServiceUuid() == 0xfeaa && beacon.getBeaconTypeCode() == 0x00) {
// This is a Eddystone-UID frame
Identifier namespaceId = beacon.getId1();
Identifier instanceId = beacon.getId2();
// Log.d(TAG, "I see a beacon transmitting namespace id: "+namespaceId+
// " and instance id: "+instanceId+
// " approximately "+beacon.getDistance()+" meters away.");
// Do we have telemetry data?
int iDataSize = beacon.getExtraDataFields().size();
if (beacon.getExtraDataFields().size() > 0) {
long telemetryVersion = beacon.getExtraDataFields().get(0);
long batteryMilliVolts = beacon.getExtraDataFields().get(1);
long telemetryTemperature = beacon.getExtraDataFields().get(2);
double tempValue= convertTemperature(telemetryTemperature);
long pduCount = beacon.getExtraDataFields().get(3);
long uptime = beacon.getExtraDataFields().get(4);
// Log.d(TAG, "The above beacon is sending telemetry version "+telemetryVersion+
// ", has been up for : "+uptime+" seconds"+
// ", has a battery level of "+batteryMilliVolts+" mV"+
// ", and has transmitted "+pduCount+" advertisements.");
}
}
}
}
double convertTemperature(long source) {
double temp;
double temperature;
long unsignedTemp = (source >> 8);
if(unsignedTemp > 128) {
temp = unsignedTemp - 256;
}else{
temp = unsignedTemp;
}
temperature = unsignedTemp + (source & 0xff) / 256.0;
return temperature;
}
@Override
public void onPause() {
super.onPause();
mBeaconManager.unbind(this);
}
}
public class MonitoringActivity extends Activity {
protected static final String TAG = "MonitoringActivity";
private static final int PERMISSION_REQUEST_FINE_LOCATION = 1;
private static final int PERMISSION_REQUEST_BACKGROUND_LOCATION = 2;
private static final int PERMISSION_REQUEST_COARSE_LOCATION = 3;
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_monitoring);
verifyBluetooth();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (this.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if (this.checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
if (!this.shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_BACKGROUND_LOCATION)) {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("This app needs background location access");
builder.setMessage("Please grant location access so this app can detect beacons in the background.");
builder.setPositiveButton(android.R.string.ok, null);
builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
@TargetApi(23)
@Override
public void onDismiss(DialogInterface dialog) {
requestPermissions(new String[]{Manifest.permission.ACCESS_BACKGROUND_LOCATION},
PERMISSION_REQUEST_BACKGROUND_LOCATION);
}
});
builder.show();
}
else {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Functionality limited");
builder.setMessage("Since background location access has not been granted, this app will not be able to discover beacons in the background. Please go to Settings -> Applications -> Permissions and grant background location access to this app.");
builder.setPositiveButton(android.R.string.ok, null);
builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
}
});
builder.show();
}
}
}
} else {
if (!this.shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)) {
requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_BACKGROUND_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION},
PERMISSION_REQUEST_FINE_LOCATION);
requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},
PERMISSION_REQUEST_COARSE_LOCATION);
}
else {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Functionality limited");
builder.setMessage("Since location access has not been granted, this app will not be able to discover beacons. Please go to Settings -> Applications -> Permissions and grant location access to this app.");
builder.setPositiveButton(android.R.string.ok, null);
builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
}
});
builder.show();
}
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case PERMISSION_REQUEST_FINE_LOCATION: {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "fine location permission granted");
} else {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Functionality limited");
builder.setMessage("Since location access has not been granted, this app will not be able to discover beacons.");
builder.setPositiveButton(android.R.string.ok, null);
builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
}
});
builder.show();
}
return;
}
case PERMISSION_REQUEST_BACKGROUND_LOCATION: {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "background location permission granted");
} else {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Functionality limited");
builder.setMessage("Since background location access has not been granted, this app will not be able to discover beacons when in the background.");
builder.setPositiveButton(android.R.string.ok, null);
builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
}
});
builder.show();
}
return;
}
}
}
public void onRangingClicked(View view) {
// Intent myIntent = new Intent(this, RangingActivity.class);
Intent myIntent = new Intent(this, BeaconMonitoringActivity.class);
this.startActivity(myIntent);
}
public void onEnableClicked(View view) {
BeaconReferenceApplication application = ((BeaconReferenceApplication) this.getApplicationContext());
if (BeaconManager.getInstanceForApplication(this).getMonitoredRegions().size() > 0) {
application.disableMonitoring();
((Button)findViewById(R.id.enableButton)).setText("Re-Enable Monitoring");
}
else {
((Button)findViewById(R.id.enableButton)).setText("Disable Monitoring");
application.enableMonitoring();
}
}
@Override
public void onResume() {
super.onResume();
BeaconReferenceApplication application = ((BeaconReferenceApplication) this.getApplicationContext());
// application.setMonitoringActivity(this);
updateLog(application.getLog());
}
@Override
public void onPause() {
super.onPause();
((BeaconReferenceApplication) this.getApplicationContext()).setMonitoringActivity(null);
}
private void verifyBluetooth() {
try {
if (!BeaconManager.getInstanceForApplication(this).checkAvailability()) {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Bluetooth not enabled");
builder.setMessage("Please enable bluetooth in settings and restart this application.");
builder.setPositiveButton(android.R.string.ok, null);
builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
//finish();
//System.exit(0);
}
});
builder.show();
}
}
catch (RuntimeException e) {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Bluetooth LE not available");
builder.setMessage("Sorry, this device does not support Bluetooth LE.");
builder.setPositiveButton(android.R.string.ok, null);
builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
//finish();
//System.exit(0);
}
});
builder.show();
}
}
public void updateLog(final String log) {
runOnUiThread(new Runnable() {
public void run() {
EditText editText = (EditText)MonitoringActivity.this
.findViewById(R.id.monitoringText);
editText.setText(log);
}
});
}
}
public class BeaconReferenceApplication extends Application implements BootstrapNotifier {
private static final String TAG = "BeaconReferenceApp";
private RegionBootstrap regionBootstrap;
private BackgroundPowerSaver backgroundPowerSaver;
private BeaconMonitoringActivity monitoringActivity = null;
private String cumulativeLog = "";
public void onCreate() {
super.onCreate();
BeaconManager beaconManager = org.altbeacon.beacon.BeaconManager.getInstanceForApplication(this);
// 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:2-3=beac,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25"));
beaconManager.setDebug(true);
// Uncomment the code below to use a foreground service to scan for beacons. This unlocks
// the ability to continually scan for long periods of time in the background on Andorid 8+
// in exchange for showing an icon at the top of the screen and a always-on notification to
// communicate to users that your app is using resources in the background.
//
Notification.Builder builder = new Notification.Builder(this);
builder.setSmallIcon(R.drawable.ic_launcher);
builder.setContentTitle("Scanning for Beacons");
Intent intent = new Intent(this, BeaconMonitoringActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(
this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT
);
builder.setContentIntent(pendingIntent);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel("My Notification Channel ID",
"My Notification Name", NotificationManager.IMPORTANCE_DEFAULT);
channel.setDescription("My Notification Channel Description");
NotificationManager notificationManager = (NotificationManager) getSystemService(
Context.NOTIFICATION_SERVICE);
notificationManager.createNotificationChannel(channel);
builder.setChannelId(channel.getId());
}
beaconManager.enableForegroundServiceScanning(builder.build(), 456);
beaconManager.setForegroundBetweenScanPeriod(0);
beaconManager.setForegroundScanPeriod(60000);
// For the above foreground scanning service to be useful, you need to disable
// JobScheduler-based scans (used on Android 8+) and set a fast background scan
// cycle that would otherwise be disallowed by the operating system.
//
beaconManager.setEnableScheduledScanJobs(false);
beaconManager.setBackgroundBetweenScanPeriod(0);
beaconManager.setBackgroundScanPeriod(11000);
Log.d(TAG, "setting up background monitoring for beacons and power saving");
// wake up the app when a beacon is seen
Region region = new Region("backgroundRegion",
null, null, null);
regionBootstrap = new RegionBootstrap(this, region);
// simply constructing this class and holding a reference to it in your custom Application
// class will automatically cause the BeaconLibrary to save battery whenever the application
// is not visible. This reduces bluetooth power usage by about 60%
backgroundPowerSaver = new BackgroundPowerSaver(this);
// If you wish to test beacon detection in the Android Emulator, you can use code like this:
// BeaconManager.setBeaconSimulator(new TimedBeaconSimulator() );
// ((TimedBeaconSimulator) BeaconManager.getBeaconSimulator()).createTimedSimulatedBeacons();
}
public void disableMonitoring() {
if (regionBootstrap != null) {
regionBootstrap.disable();
regionBootstrap = null;
}
}
public void enableMonitoring() {
Region region = new Region("backgroundRegion",
null, null, null);
regionBootstrap = new RegionBootstrap(this, region);
}
@Override
public void didEnterRegion(Region arg0) {
Log.d(TAG, "did enter region.");
// Send a notification to the user whenever a Beacon
// matching a Region (defined above) are first seen.
Log.d(TAG, "Sending notification.");
sendNotification();
if (monitoringActivity != null) {
// If the Monitoring Activity is visible, we log info about the beacons we have
// seen on its display
logToDisplay("I see a beacon again" );
}
}
@Override
public void didExitRegion(Region region) {
logToDisplay("I no longer see a beacon.");
}
@Override
public void didDetermineStateForRegion(int state, Region region) {
logToDisplay("Current region state is: " + (state == 1 ? "INSIDE" : "OUTSIDE ("+state+")"));
}
private void sendNotification() {
NotificationManager notificationManager =
(NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
Notification.Builder builder;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel("Beacon Reference Notifications",
"Beacon Reference Notifications", NotificationManager.IMPORTANCE_HIGH);
channel.enableLights(true);
channel.enableVibration(true);
channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
notificationManager.createNotificationChannel(channel);
builder = new Notification.Builder(this, channel.getId());
}
else {
builder = new Notification.Builder(this);
builder.setPriority(Notification.PRIORITY_HIGH);
}
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
stackBuilder.addNextIntent(new Intent(this, BeaconMonitoringActivity.class));
PendingIntent resultPendingIntent =
stackBuilder.getPendingIntent(
0,
PendingIntent.FLAG_UPDATE_CURRENT
);
builder.setSmallIcon(R.drawable.ic_launcher);
builder.setContentTitle("I detect a beacon");
builder.setContentText("Tap here to see details in the reference app");
builder.setContentIntent(resultPendingIntent);
notificationManager.notify(1, builder.build());
}
public void setMonitoringActivity(BeaconMonitoringActivity activity) {
this.monitoringActivity = activity;
}
private void logToDisplay(String line) {
cumulativeLog += (line + "\n");
if (this.monitoringActivity != null) {
//this.monitoringActivity.updateLog(cumulativeLog);
}
}
public String getLog() {
return cumulativeLog;
}
}
Thanks
Upvotes: 1
Views: 210
Reputation: 65025
A few issues:
BackgroundPower saver should be set up once. Right now it is set up in the BeaconMonitoringActivity
and BeaconScannerApplication
. I suggest you remove it from BeaconMonitoringActivity
.
Similarly, the Eddystone parser should be set up once. I suggest you move it from the onResume method of BeaconMonitoringActivity
to BeaconScannerApplication
right where the comment says // 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...
The scan period configured (beaconManager.setForegroundScanPeriod(60000);
) will case the library to scan for beacons for a full 60 seconds before reporting them to your application. What you perceive as a failure to detect may just be that it is taking 60 seconds to get each callback. Try setting this to a lower number like 1100ms.
Upvotes: 0