Estimote beacons region detection from a service in background
Asked Answered
S

3

10

i recently started testing Estimote Beacons, and I'm trying to launch a notification from a background service when entering a beacon region, but unfortunately my solution doesn't work. It doesn't give errors but the notification is not being launched when a beacon is discovered. I don't know if it is some code error or simply the way of doing it is wrong. I've read this other question but it seems a bit different since what i use is a service instead of an activity, but maybe the answer is similar (app context related)...

here is my service code

public class BeaconsMonitoringService extends Service{

    private BeaconManager beaconManager;

    private String user;

    @Override
      public void onCreate() {
        // Configure BeaconManager.
        beaconManager = new BeaconManager(this);

      }

      @Override
      public int onStartCommand(Intent intent, int flags, int startId) {
          Toast.makeText(this, "Beacons monitoring service starting", Toast.LENGTH_SHORT).show();

          user = intent.getStringExtra("user");

            // Check if device supports Bluetooth Low Energy.
            if (!beaconManager.hasBluetooth()||!beaconManager.isBluetoothEnabled()) {
              Toast.makeText(this, "Device does not have Bluetooth Low Energy or it is not enabled", Toast.LENGTH_LONG).show();
              this.stopSelf();
            }

              connectToService();


          // If we get killed, after returning from here, restart
          return START_STICKY;
      }

      @Override
      public IBinder onBind(Intent intent) {
          // We don't provide binding, so return null
          return null;
      }

      @Override
      public void onDestroy() {
        Toast.makeText(this, "Beacons monitoring service done", Toast.LENGTH_SHORT).show();
      }

      private void connectToService() {


          beaconManager.connect(new BeaconManager.ServiceReadyCallback() {

            @Override
            public void onServiceReady() {
                notifyEnterRegion(0);
//            try {
                  beaconManager.setBackgroundScanPeriod(TimeUnit.SECONDS.toMillis(1), 0);
                  Log.i("BEACOON ", "ANTES DE");
                  beaconManager.setMonitoringListener(new MonitoringListener() {
                    @Override
                    public void onEnteredRegion(Region region, List<Beacon> beacons) {
                      Log.i("BEACOON ", String.valueOf(beacons.get(1).getMinor()));
                    for (Beacon beacon: beacons){
                        Log.i("BEACOON ", String.valueOf(beacon.getMinor()));
                        if (beacon.getMinor() == 64444) {

                            notifyEnterRegion(6444);

                        } else if (beacon.getMinor() == 36328) {

                            notifyEnterRegion(36328);

                        } else if (beacon.getMinor() == 31394) {

                            notifyEnterRegion(31394);

                        }
                    }
                    }

                    @Override
                    public void onExitedRegion(Region region) {

                        notifyExitRegion();

                    }
                  });  

            }
          });
        }

      public void notifyEnterRegion(int code) {

            Toast.makeText(this, "Beacon "+code, Toast.LENGTH_SHORT).show();

            Intent targetIntent = new Intent(this, MainActivity.class);
            PendingIntent contentIntent = PendingIntent.getActivity(this, 0, targetIntent, PendingIntent.FLAG_UPDATE_CURRENT);

            Notification noti = new Notification.Builder(this)
             .setContentTitle("Bienvenido "+user+"!")
             .setContentText("Sólo por estar aquí has ganado....")
             .setSmallIcon(com.smt.beaconssmt.R.drawable.beacon_gray)
             .setContentIntent(contentIntent)
             .getNotification();

            NotificationManager nManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
            nManager.notify(1, noti);
        }

      public void notifyExitRegion(){

            AlertDialog.Builder builder = new AlertDialog.Builder(this);

            builder.setMessage("Hasta pronto!")
                   .setTitle(user+", estás abandonando la zona de beacons");

            builder.setPositiveButton("Ver web", new DialogInterface.OnClickListener() {
                       public void onClick(DialogInterface dialog, int id) {
                           // User clicked OK button
                           Intent i = new Intent(BeaconsMonitoringService.this, WebViewActivity.class);
                           i.putExtra("web", "http://www.google.com/");
                           startActivity(i);
                       }
                   });
            builder.setNegativeButton("Adios!", new DialogInterface.OnClickListener() {
                       public void onClick(DialogInterface dialog, int id) {
                           // User cancelled the dialog
                       }
                   });

            AlertDialog dialog = builder.create();

            dialog.show();
      }



}

i will appreciate very much any kind of help, thanks in advance!

Sterilant answered 9/7, 2014 at 9:14 Comment(0)
E
8

This code is working for me. Make sure you are placing the correct UUID, minor and major numbers.

//Using something like that as global variable

    private static final Region[] BEACONS = new Region[] { 
    new Region("beacon1", "uuid1", 1, 19227), //uuid without "-"
    new Region("beacon2", "uuid2", 1, 61690),
    new Region("beacon3", "uuid3", null, null)
};
//Note: setting minor == null and major == null will detect every beacon with that uuid

  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {
      startMonitoring();
      return START_STICKY;
  }



private void startMonitoring() {
    if (beaconManager == null) {
        beaconManager = new BeaconManager(this);

        // Configure verbose debug logging.
        L.enableDebugLogging(true);

        /**
         * Scanning
         */
        beaconManager.setBackgroundScanPeriod(TimeUnit.SECONDS.toMillis(1), 1);

        beaconManager.setRangingListener(new RangingListener() {

            @Override
            public void onBeaconsDiscovered(Region paramRegion, List<Beacon> paramList) {
                if (paramList != null && !paramList.isEmpty()) {
                    Beacon beacon = paramList.get(0);
                    Proximity proximity = Utils.computeProximity(beacon);
                    if (proximity == Proximity.IMMEDIATE) {
                        Log.d(TAG, "entered in region " + paramRegion.getProximityUUID());
                        postNotification(paramRegion);
                    } else if (proximity == Proximity.FAR) {
                        Log.d(TAG, "exiting in region " + paramRegion.getProximityUUID());
                         removeNotification(paramRegion);
                    }
                }
            }

        });

        beaconManager.connect(new BeaconManager.ServiceReadyCallback() {
            @Override
            public void onServiceReady() {
                try {
                    Log.d(TAG, "connected");
                    for (Region region : BEACONS) {
                        beaconManager.startRanging(region);
                    }
                } catch (RemoteException e) {
                    Log.d("TAG", "Error while starting monitoring");
                }
            }
        });
    }
}

***Editing: code to compute accuracy

 public static double computeAccuracy(Beacon beacon)
 {
    if (beacon.getRssi() == 0) 
    {
       return -1.0D;
    }

    double ratio = beacon.getRssi() / beacon.getMeasuredPower();
    double rssiCorrection = 0.96D + Math.pow(Math.abs(beacon.getRssi()), 3.0D) % 10.0D / 150.0D;

    if (ratio <= 1.0D) 
    {
       return Math.pow(ratio, 9.98D) * rssiCorrection;
    }
    return (0.103D + 0.89978D * Math.pow(ratio, 7.71D)) * rssiCorrection;
 }

 public static Proximity proximityFromAccuracy(double accuracy)
 {
    if (accuracy < 0.0D) 
    {
        return Proximity.UNKNOWN;
    }
    if (accuracy < 0.5D) 
    {
        return Proximity.IMMEDIATE;
    }
    if (accuracy <= 3.0D) {
        return Proximity.NEAR;
    }
    return Proximity.FAR;
}



 public static Proximity computeProximity(Beacon beacon) {
     return proximityFromAccuracy(computeAccuracy(beacon));
 }
Eidetic answered 9/7, 2014 at 9:31 Comment(5)
Ok, this solution is working, thanks! but i have one doubt about it, how do you "compute proximity"? is something in the sdk or official code or is your own solution? So from this i understand that BeaconManager.setMonitoringListener() is not usable in this case and we have to use setRangingListener() instead??Sterilant
The code to compute accuracy should be in the SDK. But i'm using a custom sdk implementation so i edited the answer to show you the code. About setMonitoringListener I couldn't have it to work so I finish using this solution.Eidetic
I am integrating for my Xamarin application, here EnterRegion and ExitRegion calls multiple time sequentially, don't know why? I read about it, it should trigger only when device comes under range of region. But When it comes in range it triggered sequentially until device go out the range of beacon. Kindly help to sort thisSoggy
You can't use Ranging in background continuously. It is not a good practise. It will drain battery and resources very quickly. For background purpose use Monitoring only or if needed Ranging for sometime, not continuously or mixture of both.If you still do, let me tell your app may got rejected while publishing in play store.Oppidan
@AjaySharma since you are using Ranging, your trigger will be triggered for every scan period untill you are in range of beacon. It is not a good practise. Use Monitoring Only.Oppidan
T
0

Check this one I hope this will help you. You can find proximity by this function Utils.computeProximity(nearestBeacon).

beaconManager.setRangingListener(new BeaconManager.RangingListener() {

        @Override
        public void onBeaconsDiscovered(Region region, final List<Beacon> beaconList) {
            if (!beaconList.isEmpty()) {
                Beacon nearestBeacon = beaconList.get(0);
                Log.e("Current Proximity - ", String.valueOf(Utils.computeProximity(nearestBeacon)));
            }
        }
});
Terramycin answered 28/12, 2016 at 10:12 Comment(0)
H
0

From your service, you could also use this library to scan for iBeacons. It may save your users some battery. I would also prefer this, as BeaconManager actually spawns it's own service, and so you have 2 services running just to scan iBeacons. It does however lack distance computation.

Also mind that, to scan for iBeacons you need:

  • Have a Bluetooth LE chip: any.
  • Have Bluetooth on: any.
  • Have location on: Android 6+.
  • Have location runtime permission: Android 6+.
  • Maximum start 5 scans in 30 seconds: Android 7+.

And to keep your service running you will have to add the following broadcast listeners:

  • location state changed
  • bluetooth state changed
  • boot receiver

From these receivers you should start your service again.

Hildehildebrand answered 29/12, 2016 at 7:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.