Yalantis: iOS, Android And Web App Development Company

How We Built iBeacon-based SilentBeacon App

It’s been two years since Apple introduced iBeacon technology to its 254 U.S. retail stores proclaiming the next big thing for the retail sector. Even though the indoor positioning system has not yet evolved to a significant extent, we can already predict that iBeacon is here to stay.

It’s enough to skim the headlines of the articles featuring startups that jumped on the iBeacon bandwagon to understand that location context is getting hot, and not only for the retail business.

Read also: 7 things to learn about iBeacon and beacons

In this article, we’ll talk about the app we built which does not target retailers, but serves as a great example of what can be developed using iBeacon technology.

SilentBeacon app for Android

What we created using iBeacon

We have a little problem in our office. When guys come to work, they forget to switch off the sound on their devices. Since we work together in an open space, you can imagine how irritating random ringtones can be!

We ordered several beacons a few days ago so I decided I could use iBeacon technology to solve this problem.

The idea of the SilentBeacon Android app is pretty simple. When a phone enters a beacon range, it should turn off the sound and set “vibrate” for calls and notifications. This is probably the simplest case for a beacon-based app, because we need to determine neither a distance to beacons nor a device’s location. All we have to do is detect a beacon itself.

 

How we developed the SilentBeacon app

As you already know, beacons are simple Bluetooth transmitters, which can be detected using Android SDK.

However, that’s not all. We also needed to differentiate beacons from other Bluetooth devices, and implement a power-friendly scanning mechanism. Of course, there are a few ready-to-use libraries that solve these problems. Feel free to use them if you don’t need to handle any uncommon behaviors.

I chose AltBeacon library for my app. It’s free and open-source.

Background scanning

Since we are going to implement endless scanning in the background, we need to create a service for this. Here is how:

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 provides us with an object called BeaconManager which we will use for operations with beacons. By default, AltBeacon will only detect those beacons which meet the AltBeacon specification. To change this, we need to create our own BeaconParser object and set a proper beacon data layout to it. Check out how to set a layout in AltBeacon JavaDoc or you can use the layout I set since it matches the majority of 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"));


How can an app detect beacons?

To allow your app to handle beacon events you should bind BeaconConsumer implementation (in our case the service itself) with BeaconManager. After this, you’ll receive a callback from a beacon detection service in onBeaconServiceConnect().

I used a concept of region monitoring here. Although a region can be set by three beacons maximum, I used only one beacon per region. MonitorNotifier will receive callbacks when a user enters / leaves the region.

This is almost the end of the story.

Read also:

Battery saving

Battery saving is another important thing we need to address. Our app is constantly searching for beacons while consuming a device's battery. Even though Bluetooth LE technology includes power saving features, it’s not enough to prevent a battery from draining too fast. I tried using the app on my Nexus 4 and after 8 or 10 hours of work the battery went low.

The solution here is to increase a time interval between scanning procedures. AltBeacon library allows us to set 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 in our case, since people in the office tend to 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);
 }

I also added a few minor features to the app, 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.

Insights

3 Types of Mobile Fashion Shopping Marketplaces You Can Build

Tech

7 Things to Learn About iBeacon and Beacons

Tech

How to Develop a Bitcoin Wallet App

See what else we can do

Check out our knowledge and capabilities

Let's talk code