Table of Contents
In 2013 Apple first introduced their iBeacon technology standard, that became a new milestone in evolution of location-aware software.
iBeacon technology allows both iOS and Android apps to listen for signals from beacons (simple Bluetooth transmitters) in the physical world and react on them. Thanks to this technology, an app can deliver highly personalized content to users based on their location.
First appeared in 254 Apple’s U.S. retail stores, iBeacons now are used in many business niches: in museums and restaurants, hospitals, and educational institutions. Virgin Atlantic, Major League Baseball, American Eagle, Nivea, and Walmart are already using beacons for different purposes.
Now it’s time to jump into the bandwagon and learn more about this technology. We’ll show in practice how to use the iBeacon technology in your app. We created a sample app called SilentBeacon, which aims to increase the productivity of a team in an open-space office.
What is SilentBeacon used for
Yalantis, an app development company, was looking for the ways to solve one minor problem in the office. When development teams came to work, they forgot to switch off the sound on their smartphones. They all worked together in an open space, so random ringtones were really irritating! Yalantis decided to solve the problem with the help of iBeacon technology.
The idea of the SilentBeacon Android app is straightforward: this app has a location-awareness functionality just like in the delivery apps like Postmates: https://yalantis.com/blog/how-to-build-an-on-demand-delivery-app-like-postmates. When a phone enters a beacon range, the app immediately asks a user to turn off the sound and put the phone on vibrate mode.
This is a very simple implementation of iBeacon technology, because the app has no need to determine neither a distance to beacons nor a device’s location. It only detects a beacon itself.
From idea to implementation
The app should detect beacons and then differentiate them from other Bluetooth devices. It also should have a power-friendly scanning mechanism, so the app won’t drain user’s battery.
There are a few ready-to-use libraries that helped us deal with these challenges. You can use them for your project if you don’t need to handle any uncommon behaviors. Yalantis has chosen an open-source AltBeacon library for the app.
Background scanning
First, we need to implement endless scanning in the background, let’s create a service for this:
public class SilentBeaconService extends Service implements BeaconConsumer {
private BeaconManager beaconManager;
private AudioManager audioManager;
private SharedPreferences sharedPreferences;
@Override
public void onCreate() {
super.onCreate();
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
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,d:25-25”));
beaconManager.bind(this);
}
@Override
public void onDestroy() {
beaconManager.unbind(this);
super.onDestroy();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onBeaconServiceConnect() {
beaconManager.setMonitorNotifier(new MonitorNotifier() {
@Override
public void didEnterRegion(Region region) {
Log.d(Constants.LOG_TAG, “I just saw an beacon for the first time!”);
Log.d(Constants.LOG_TAG, “Region id – ” + region.getUniqueId());
if (audioManager.getRingerMode() != AudioManager.RINGER_MODE_VIBRATE) {
audioManager.setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
Log.d(Constants.LOG_TAG, “RINGER_MODE_VIBRATE set”);
}
boolean switchSoundOn = sharedPreferences.getBoolean(Constants.SHARED_PREFERENCES_KEY_TURN_SOUND_ON, false);
if (!switchSoundOn) {
stopSelf();
}
}
@Override
public void didExitRegion(Region region) {
boolean switchSoundOn = sharedPreferences.getBoolean(Constants.SHARED_PREFERENCES_KEY_TURN_SOUND_ON, false);
if (switchSoundOn) {
audioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
}
Log.d(Constants.LOG_TAG, “I no longer see a beacon”);
Log.d(Constants.LOG_TAG, “Region id – ” + region.getUniqueId());
}
@Override
public void didDetermineStateForRegion(int state, Region region) {
}
});
try {
Identifier identifier = Identifier.parse(“E2C56DB5-DFFB-48D2-B060-D0F5A71096E0”); //beacon 1
Identifier identifier2 = Identifier.parse(“F7826DA6-4FA2-4E98-8024-BC5B71E0893E”); //beacon 2
beaconManager.startMonitoringBeaconsInRegion(new Region(Constants.REGION1_ID, identifier, null, null));
beaconManager.startMonitoringBeaconsInRegion(new Region(Constants.REGION2_ID, identifier2, null, null));
} catch (RemoteException e) {
Log.e(Constants.LOG_TAG, e.getMessage(), e);
}
}
}
Setting beacon data layout
AltBeacon library has an object called BeaconManager, it allows developers to set up operations with beacons. By default, AltBeacon will only detect beacons that meet the AltBeacon specification.
We changed this by creating our own BeaconParser object and setting a proper beacon data layout to it. Check out how to set a layout in AltBeacon JavaDoc. Or feel free to use the layout from SilentBeacon that matches the majority of modern beacons:
beaconManager.getBeaconParsers().add(new BeaconParser().setBeaconLayout(“m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25”));
Connecting beacons to the app
To let your app handle beacon events, you should bind BeaconConsumer implementation with BeaconManager. Then, you’ll receive a callback from a beacon detection service in onBeaconServiceConnect().
In SilentBeacon, we used a concept of region monitoring. Remember that a region can be set by three beacons maximum. Our app uses only one beacon per region. The MonitorNotifier class will receive callbacks when a user enters or leaves the region. Our app is almost done.
Battery saving
The SilentBeacon is constantly searching for beacons. We needed to ensure that the app won’t drain a phone battery. Even though Bluetooth Low Energy technology includes power saving features, it’s not enough to prevent a battery from draining too fast.
To solve this, we increased a time interval between scanning procedures. It wasn’t difficult since AltBeacon library allows for setting this interval manually.
Otherwise, you can use a background mode for BeaconManager with an existing battery-friendly scanning interval. This will certainly affect the accuracy, since you won’t be able to detect a user who entered a region and quickly left it. But it’s not critical for SilentBeacon, since people in the office spend their whole working day there.
If you create a similar app that works with beacons, you should find a compromise between battery life and beacons’ detection accuracy.
Here is a piece of battery saving code:
@Override
public void onCreate() {
super.onCreate();
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
beaconManager = BeaconManager.getInstanceForApplication(this);
beaconManager.setBackgroundMode(true);
beaconManager.getBeaconParsers().add(new BeaconParser().setBeaconLayout(“m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25”));
beaconManager.bind(this);
}
SilentBacon also has a few minor features such as configuration activity and reacting to sound changes, which a user of the app may apply. You can check out the full project on GitHub.