package org.altbeacon.beacon.startup; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.RemoteException; import org.altbeacon.beacon.BeaconConsumer; import org.altbeacon.beacon.BeaconManager; import org.altbeacon.beacon.MonitorNotifier; import org.altbeacon.beacon.Region; import org.altbeacon.beacon.logging.LogManager; import java.util.ArrayList; import java.util.List; /** * Class allowing a user to set up background launching of an app when a user enters a beacon Region. * Simply constructing and holding a reference to this class will cause background scanning for beacons * to start on Android device startup. If a matching beacon is detected, the BootstrapNotifier * didEnterRegion method will be called, allowing the application to launch an Activity, send a * local notification, or perform any other action desired. * * Using this class as described above will also cause beacon scanning to start back up after power * is connected or disconnected from a device if the user has force terminated the app. * * IMPORTANT NOTE: The RegionBootstrap class registers an internal MonitorNotifier with the * BeaconManager. If you use the RegionBootstrap, your application must not manually register * a second MonitorNotifier, otherwise it will unregister the one configured by the RegionBootstrap, * effectively disabling it. When using the RegionBootstrap, any custom monitoring code must * therefore be placed in the callback methods in the BootstrapNotifier implementation passed to the * RegionBootstrap. */ public class RegionBootstrap { protected static final String TAG = "AppStarter"; private BeaconManager beaconManager; private MonitorNotifier monitorNotifier; private Context context; private List<Region> regions; private boolean disabled = false; private BeaconConsumer beaconConsumer; private boolean serviceConnected = false; /** * Constructor to bootstrap your Application on an entry/exit from a single region. * * @param context * @param monitorNotifier * @param region */ public RegionBootstrap(final Context context, final MonitorNotifier monitorNotifier, Region region) { if (context == null) { throw new NullPointerException("Application Context should not be null"); } this.context = context.getApplicationContext(); this.monitorNotifier = monitorNotifier; regions = new ArrayList<Region>(); regions.add(region); beaconManager = BeaconManager.getInstanceForApplication(context); beaconConsumer = new InternalBeaconConsumer(); if (beaconManager.isBackgroundModeUninitialized()) { beaconManager.setBackgroundMode(true); } beaconManager.bind(beaconConsumer); LogManager.d(TAG, "Waiting for BeaconService connection"); } /** * Constructor to bootstrap your Application on an entry/exit from multiple regions * * @param context * @param monitorNotifier * @param regions */ public RegionBootstrap(final Context context, final MonitorNotifier monitorNotifier, List<Region> regions) { if (context == null) { throw new NullPointerException("Application Context should not be null"); } this.context = context.getApplicationContext(); this.monitorNotifier = monitorNotifier; this.regions = regions; beaconManager = BeaconManager.getInstanceForApplication(context); beaconConsumer = new InternalBeaconConsumer(); if (beaconManager.isBackgroundModeUninitialized()) { beaconManager.setBackgroundMode(true); } beaconManager.bind(beaconConsumer); LogManager.d(TAG, "Waiting for BeaconService connection"); } /** * Constructor to bootstrap your Application on an entry/exit from a single region. * * @param application * @param region */ public RegionBootstrap(BootstrapNotifier application, Region region) { if (application.getApplicationContext() == null) { throw new NullPointerException("The BootstrapNotifier instance is returning null from its getApplicationContext() method. Have you implemented this method?"); } this.context = application.getApplicationContext(); regions = new ArrayList<Region>(); regions.add(region); this.monitorNotifier = application; beaconManager = BeaconManager.getInstanceForApplication(context); beaconConsumer = new InternalBeaconConsumer(); if (beaconManager.isBackgroundModeUninitialized()) { beaconManager.setBackgroundMode(true); } beaconManager.bind(beaconConsumer); LogManager.d(TAG, "Waiting for BeaconService connection"); } /** * Constructor to bootstrap your Application on an entry/exit from multiple regions * * @param application * @param regions */ public RegionBootstrap(BootstrapNotifier application, List<Region> regions) { if (application.getApplicationContext() == null) { throw new NullPointerException("The BootstrapNotifier instance is returning null from its getApplicationContext() method. Have you implemented this method?"); } this.context = application.getApplicationContext(); this.regions = regions; this.monitorNotifier = application; beaconManager = BeaconManager.getInstanceForApplication(context); beaconConsumer = new InternalBeaconConsumer(); if (beaconManager.isBackgroundModeUninitialized()) { beaconManager.setBackgroundMode(true); } beaconManager.bind(beaconConsumer); LogManager.d(TAG, "Waiting for BeaconService connection"); } /** * Used to disable additional bootstrap callbacks after the first is received. Unless this is called, * your application will be get additional calls as the supplied regions are entered or exited. */ public void disable() { if (disabled) { return; } disabled = true; try { for (Region region : regions) { beaconManager.stopMonitoringBeaconsInRegion(region); } } catch (RemoteException e) { LogManager.e(e, TAG, "Can't stop bootstrap regions"); } beaconManager.unbind(beaconConsumer); } /** * Add a new region * * @param region */ public void addRegion(Region region) { if (!regions.contains(region)) { if (serviceConnected) { try { beaconManager.startMonitoringBeaconsInRegion(region); } catch (RemoteException e) { LogManager.e(e, TAG, "Can't add bootstrap region"); } } else { LogManager.w(TAG, "Adding a region: service not yet Connected"); } regions.add(region); } } /** * Remove a given region * * @param region */ public void removeRegion(Region region) { if (regions.contains(region)) { if (serviceConnected) { try { beaconManager.stopMonitoringBeaconsInRegion(region); } catch (RemoteException e) { LogManager.e(e, TAG, "Can't stop bootstrap region"); } } else { LogManager.w(TAG, "Removing a region: service not yet Connected"); } regions.remove(region); } } private class InternalBeaconConsumer implements BeaconConsumer { private Intent serviceIntent; /** * Method reserved for system use */ @Override public void onBeaconServiceConnect() { LogManager.d(TAG, "Activating background region monitoring"); beaconManager.addMonitorNotifier(monitorNotifier); serviceConnected = true; try { for (Region region : regions) { LogManager.d(TAG, "Background region monitoring activated for region %s", region); beaconManager.startMonitoringBeaconsInRegion(region); } } catch (RemoteException e) { LogManager.e(e, TAG, "Can't set up bootstrap regions"); } } /** * Method reserved for system use */ @Override public boolean bindService(Intent intent, ServiceConnection conn, int arg2) { this.serviceIntent = intent; context.startService(intent); return context.bindService(intent, conn, arg2); } /** * Method reserved for system use */ @Override public Context getApplicationContext() { return context; } /** * Method reserved for system use */ @Override public void unbindService(ServiceConnection conn) { context.unbindService(conn); context.stopService(serviceIntent); serviceConnected = false; } } }