0

In the reference application, RegionBootstrap is initialised in a custom application class on it's onCreate method and of course, the application class is called before any activity is called.

Is there a way to initialise RegionBootstrap inside an activity? I already tried making a static variable of RegionBootstrap so i can call it in a different activity, but unfortunately, it doesn't work.

BeaconApplication.regionBootstrap = new RegionBootstrap((BootstrapNotifier) this.getApplication(), downloadedBeacons);

The Regions I needed to be initialised will come from a server, so initialisation of RegionBootstrap must not come from the application class.

* EDIT *

public class LoginActivity extends ActionBarActivity {
    …
    /*** short version ***/
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        /*** after successful login ***/
        BeaconApplication.beacons = downloadBeaconsFromServer();    
    }
}

public class BeaconActivity extends ActionBarActivity {
    …
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        …
        startService(new Intent(this, BeaconService.class));
    }
}

This is where I implemented BeaconConsumer

public class BeaconService extends Service implements BeaconConsumer {
    private BeaconManager beaconManager;
    private BeaconNotifier beaconNotifier;
    private RegionBootstrap regionBootstrap;

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i(TAG, "onCreate");
        beaconManager = BeaconManager.getInstanceForApplication(this);
        beaconManager.getBeaconParsers().add(new BeaconParser().setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24"));
        beaconManager.setBackgroundBetweenScanPeriod(1001);
        beaconManager.setBackgroundScanPeriod(101);
        beaconManager.setForegroundScanPeriod(101);
        beaconManager.setForegroundBetweenScanPeriod(1001);
        beaconNotifier = new BeaconNotifier(this);
        beaconManager.bind(this);
    }

    @Override
    public void onBeaconServiceConnect() {
        beaconManager.setMonitorNotifier(beaconNotifier);
        monitorBeacons();

        regionBootstrap = new RegionBootstrap(beaconNotifier, BeaconApplication.beacons);
    }

    private void monitorBeacons() {
        for (Region beacon : BeaconApplication.beacons) {
            try {
                Log.i(TAG, "Monitoring beacon " + beacon.getUniqueId());
                beaconManager.startMonitoringBeaconsInRegion(beacon);
            } catch (RemoteException e) {
                Log.e(TAG, "Monitoring beacon failed");
                e.printStackTrace();
            }
        }
    }
}

Implementation of BeaconNotifier

public class BeaconNotifier implements BootstrapNotifier {
    private Context context;

    public BeaconNotifier(Context context) {
            this.context = context;
    }

    @Override
    didEnter.. etc

    @Override
    public Context getApplicationContext() {
            return context;
    }
}
Furkan Varol
  • 252
  • 2
  • 8
user158335
  • 111
  • 1
  • 6

1 Answers1

1

You can use:

BeaconManager.setMonitorNotifier(MonitorNotifier);
BeaconManager.startMonitoringBeaconsInRegion(Region);

But do not forget, in order to use BeaconManager methods, you have to wait until BeaconService is connected. Be aware, with this methods, you need to create your own service if you want to monitor beacons even if app is killed.

Btw, I remember, once I have also faced a problem with RegionBootstrap. I used a trick to handle that problem. Can you test following code?

...
BeaconManager.bind(yourConsumer);
...
//wait until BeaconConsumer.onBeaconServiceConnect() is called
//write following code inside of onBeaconServiceConnect
RegionBootstrap dummy = new RegionBootstrap(mBootstrapNotifier, new Region("dummy", null, null, null));
dummy.disable();
//after this point you can create your own RegionBootstrap

There is a key point in here, you need to create your own BootstrapNotifier. If you are doing this in an activity, you can do this:

public class YourActivity extends Activity implements BootstrapNotifier {
    ...
    BootstrapNotifier mBootstrapNotifier = this;
    ...

Or in an Application class:

public class YourApp extends Application implements BootstrapNotifier {
    ...
    BootstrapNotifier mBootstrapNotifier = this;
    ...

In my case, I have created an adapter and that adapter requires Contextin its constructor and I have used that adapter as BootstrapNotifier:

public class AltBeaconAdapter implements BootstrapNotifier {
    private Context mContext;
    ...

    public AltBeaconAdapter(Context context) {
        mContext = context;
        ...
    }

    @Override
    public Context getApplicationContext() {
        return mContext;
    }

    ...
}

Also, you have to implement MonitorNotifier methods since BootstrapNotifier is a sub class of MonitorNotifier.

Yes, this trick is weird and it shows there is an error in the library with initializing RegionBootstrap but I have service so I switched to first method that I proposed to you. If this trick works for you too, let me know so that I can create an issue on the library's GitHub page.

Furkan Varol
  • 252
  • 2
  • 8
  • Will those methods open the app from the background and monitor the beacons even after android kills the app same as stated here? http://altbeacon.github.io/android-beacon-library/resume-after-terminate.html – user158335 Feb 23 '15 at 01:52
  • I have updated my answer, Please check it and let me know if it works for you. – Furkan Varol Feb 23 '15 at 07:01
  • RegionBootstrap needs two arguments, not just the Region as stated in my question. I tried this `RegionBootstrap dummy = new RegionBootstrap((BootstrapNotifier) getApplicationContext(), new Region("dummy", null, null, null));` but I got this error _java.lang.ClassCastException: com.tektosworld.ibeaconmobileapp.BeaconApplication cannot be cast to org.altbeacon.beacon.startup.BootstrapNotifier_. This is the same error I'm having when I don't put the BootstrapNotifier inside the Application class. – user158335 Feb 26 '15 at 05:27
  • Sorry, that was my bad. Again I have updated my answer. I hope this time it will help you :). Btw, `RegionBootstrap` requires a `BootstrapNotifier` and you are supplying only an `Application` object which causes `ClassCastException`. You should implement `BootstrapNotifier` in your `com.tektosworld.ibeaconmobileapp.BeaconApplication` class. Let me know if I can help you more. – Furkan Varol Feb 26 '15 at 06:49
  • Does implementing BootstrapNotifier means that I should not implement BeaconConsumer anymore? Also, if I implement BootstrapNotifier in a service will this change anything? – user158335 Feb 26 '15 at 08:10
  • Sorry I'm really confused. You said first that I should wait for `BeaconConsumer.onBeaconServiceConnect()` before creating a dummy RegionBootStrap. This means that I should implement **BeaconConsumer** right? Then I have to implement a **BootstrapNotifier**. But this would create two MonitorNotifier right? – user158335 Feb 26 '15 at 08:17
  • Actually, If you are going to only monitor `Region`s, `RegionBootstrap` and `BootstrapNotifier` should be sufficient but I tried it before and it was problematic for me for late initialization. In my case I was also ranging beacons so I had to use `BeaconManager` and `BeaconConsumer`. If you do only wish to monitor regions, you can try without `BeaconManager` and create a dummy and disable it before creating the real one. Btw, If you can post your code here or related piece of it I can help you more. And also sorry for confusion. – Furkan Varol Feb 26 '15 at 08:35
  • I also need to range. In your AltBeaconAdapter, getApplicationContext() will return a Context not a BootstrapNotifier. So `RegionBootstrap dummy = new RegionBootstrap((new AltBeaconAdapter(this)).getApplicationContext(), new Region("dummy", null, null, null));` will still cause `ClassCastException`. I now implement BootstrapNotifier in a normal Class (not extending anything), so I think my code is somehow similar to your AltBeaconAdapter. Where do you call your AltBeaconAdapter constructor? – user158335 Feb 26 '15 at 09:01
  • You need to do `RegionBootstrap dummy = new RegionBootstrap((new AltBeaconAdapter(this)), new Region("dummy", null, null, null));`. But, If you are going to use an adapter, keep its reference do not give it like that. You can use this Adapter anywhere at anytime. I am using it in my service and initializing it at its constructor but there is no place limit for initialization. – Furkan Varol Feb 26 '15 at 09:18
  • No errors but the app didn't detected the beacons after a restart. Where should I put a reference of the RegionBootstrap dummy? Should I save it in the Application class? In my code, it is saved in a service. – user158335 Feb 26 '15 at 09:44
  • You don't need to save the dummy reference. I meant reference of `(new AltBeaconAdapter(this))` object since you will need it again. I think you forget to reinitialize your real `RegionBootstrap` after a restart. It is too hard to help you without seeing any of your code. – Furkan Varol Feb 26 '15 at 09:57
  • Sorry for the late response. I edited my question to post also some parts of my code. Hope this is clearer now. Thanks Furkan – user158335 Mar 04 '15 at 05:31
  • Can you also write content of the `monitorBeacons();` method in `BeaconService`s `onBeaconServiceConnect()`? – Furkan Varol Mar 04 '15 at 08:30
  • It can detect beacons, but after a restart or a force kill, it stops working. – user158335 Mar 04 '15 at 08:59
  • Yes, your code looks like valid but there is a problem with `RegionBootstrap` initialization. Since you are doing in a **service**, in order to re-initialize after app is killed, you need to restart your service as well. To do that, you need to persist your `Region`s as soon as you get them and retrieve them next time you start your service. Also you need to start your service in your `Application` class so that service can restart beacon scan after a kill. Btw, 101 milliseconds is to short for beacon scan, look at [this](http://altbeacon.github.io/android-beacon-library/battery_manager.html) – Furkan Varol Mar 04 '15 at 10:04
  • I'll try doing this and update you later. Thanks. Also I posted this problem in github. https://github.com/AltBeacon/android-beacon-library/issues/137#issuecomment-77150090 – user158335 Mar 05 '15 at 01:01
  • It's still not working. I already have a function that saves the beacons in sqlite database, So what I only did is load the beacon list to BeaconApplication.beacons from db, then move the startService from Activity to the my Application class – user158335 Mar 05 '15 at 01:44
  • Did you check that your service is started at `BeaconApplication` properly? Or even your `BeaconApplication` is created after a kill command? – Furkan Varol Mar 05 '15 at 06:38
  • More basic approach is to add some logs at `BeaconApplication`s `onCreate` and `BeaconService`s `onCreate`. Even maybe more where you want to see what is going on. – Furkan Varol Mar 05 '15 at 07:07
  • That's what I did, but I can't see any output from my app. – user158335 Mar 05 '15 at 07:08
  • This is what I did to start my service in `Application` class: `Intent intent = new Intent(this, BeaconService.class); startService(intent); bindService(intent, this, Activity.BIND_AUTO_CREATE);`. For me, it was enough to restart my service. – Furkan Varol Mar 05 '15 at 07:31
  • bindService's second paramater needs to be an android.content.ServiceConnection. – user158335 Mar 05 '15 at 07:37
  • Yes, my `Applcation` class implements it. – Furkan Varol Mar 05 '15 at 07:40
  • I see. What are the things needed to be added inside the onServiceConnected/disconnected? – user158335 Mar 05 '15 at 07:42
  • Dude! It's working! Only after a restart though, when the app is force killed, it still doesn't detect any beacon. Is that normal with the current module? – user158335 Mar 05 '15 at 08:03
  • In order to restart your service after its killed, you need to add or replace following code to your `BeaconService`: `@Override public int onStartCommand(Intent intent, int flags, int startId) { return START_STICKY; }` – Furkan Varol Mar 05 '15 at 11:22
  • Yes, I already have that in my code from the start. It doesn't work though. – user158335 Mar 06 '15 at 01:43
  • It seems like problem with the service. For me, it works the way I told you but I only tried to kill app via app switcher (in my case it is enough). – Furkan Varol Mar 06 '15 at 06:55
  • Should I initialise `beaconManager` in `onStartCommand` rather than `onCreate`? Do you think the problem relates to that? – user158335 Mar 09 '15 at 09:50
  • Nope. It seems like your service is not sticky. Since you are not using `RegionBootstrap` in your `Application` class, you need to deal with the service operations like restarting it after a kill command. – Furkan Varol Mar 09 '15 at 10:54
  • How would I do that? I was looking for how services are restarted after it has been killed, but I read that it's impossible because of security reasons. – user158335 Mar 11 '15 at 02:12
  • As far as I heard, there are workarounds by `BroadcastReceiver` with some certain events like `BOOT_COMPLETED`, `ACTION_POWER_CONNECTED`, `ACTION_POWER_DISCONNECTED` and etc but I am not sure and also as you said because of security issues and respect to owner of the device, you probably should let it go :). You can check `BroadcastReceiver`s though. Let me know if you find anything usefull about this topic. – Furkan Varol Mar 11 '15 at 19:36
  • Isn't that the task of the library? It will be redundant if I implement it again. The library works with phone restart and app switcher. But not when I forced kill it. The reference app works with all three situations. – user158335 Mar 13 '15 at 09:44
  • Result of the reference app is actually weird, it shouldn't work when you force kill it. Even in libraries documentation it says in this way. – Furkan Varol Mar 18 '15 at 06:52