Using Beacons in SAndroidE

Paolo Bellagente edited this page Feb 28, 2018 · 56 revisions

NOTE: You may find the full source of this application here

Index

Introduction

SAndroidE supports the use of BLE Beacons leveraging on the management layer provided by the famous AltBeacon library. Currently SAndroidE is compatible with Estimote and Gimbal beacons, but it may also detect any beacon transmitting one or more of the following message formats:

  • Eddystone messages: Eddystone-UID, Eddystone-URL and Eddystone-TLM
  • IBeacon
  • Gimbal: even if most of this message is encrypted, SAndroidE is still able to distinguish between different devices
  • Nearable Format: parsing functions has been implemented by debugging the Estimote SDK with Android Studio and accessing its decompiled java classes
  • AltBeacon specification

Depending on its type, a beacon device can be usually configured to transmit one or more of the above messages, using a vendor-provided dedicated app. To use beacons in your application you should essentially perform two steps:

  • to tag each individual beacon, or better to say beacon message, with a name of your choice
  • to instruct your app to perform some action when that tag is found near the smartphone.

Requirements

  1. A smartphone supporting Bluetooth 4.0-4.1 (aka Bluetooth Smart)
  2. A micro USB cable to deploy application to your smartphone
  3. Android Studio
  4. SAndroidE zip package: download and extract the zip file
  5. Basic programming knowledge of Android applications
  6. At least one beacon (see above for the list of supported devices)

Prepare your app for SAndroidE

Some steps are common to all SAndroidE based projects. Please follow our Prepare your app for SAndroidE guide before proceeding with next steps.


Using beacons in your application

To enable beacons into your application we should:

1.Add a new activity inside of the application tag of your AndroidManifest.xml file. This activity named BeaconTagActivity is included within the SAndroidE library and it allows to detect beacons and assign them a tag. Tags may then be used within your application to easily define actions when one of the assigned beacons come in range.

      <activity android:name="it.unibs.sandroide.lib.beacon.ui.BeaconTagActivity"
            android:label="Beacon Tagging">
      </activity>	

2.In your application's User interface, you should create a button, a menu item or whatever you want, to start the BeaconTagActivity from your app:

  • Just as example, we create the following button listener function within the MainActivity.java file
    public void startTagConfiguration(View v) {
        BeaconTags.getInstance().startTaggingActivity(this);
    }
  • and then invoke the above function, from a button defined in our layout file:
         <Button
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:text="Configure Tags"
             android:onClick="startTagConfiguration" />

         <ListView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/logList" />

NOTE : ListView is here only used for demonstration, to show each message arrived from beacon on the User Interface.

3.Then in our MainActivity.java file:

3.1 Extend the activity from SandroideBaseActivity and Implement the BeaconConsumer interface;

public class MainActivity extends SandroideBaseActivity implements BeaconConsumer {
    // your class code here....

3.2 Define a BeaconManager class variable;

3.3 Then we create the variables needed to display beacon messages in a ListView in the Activity, as soon as some beacon message arrives.

public class MainActivity extends SandroideBaseActivity implements BeaconConsumer {
	protected static final String TAG = "MainActivity";
	private BeaconManager beaconManager;
	private ArrayList<String> logLines = new ArrayList<String>();
	private ListView mLogList;
	private ArrayAdapter logLinesAdapter;

3.4 In the OnCreate's method, let's initialise the beacon manager loading the current tags configuration from the application shared preferences;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        beaconManager = SandroideApplication.beaconManager;
        try {
            // load tagged beacons from shared preferences
            BeaconTags.getInstance().load(this);
        } catch (JSONException e) {
            e.printStackTrace();
        }

3.5 Then initialise the parsers for the beacon tags we intend to use in our application.

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        beaconManager = SandroideApplication.beaconManager;
        try {
            // load tagged beacons from shared preferences
            BeaconTags.getInstance().load(this);
        } catch (JSONException e) {
            e.printStackTrace();
        }

        BeaconTags.getInstance().initLayouts(beaconManager,"near");
        BeaconTags.getInstance().initLayouts(beaconManager,"sky");
        BeaconTags.getInstance().initLayouts(beaconManager,"ice");
		
	mLogList=(ListView) findViewById(R.id.logList);
        logLinesAdapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, logLines);
        mLogList.setAdapter(logLinesAdapter);
    }

NOTE: After writing the code, it could happen that some class needs to be imported. To do so, click "Alt+Enter", then click the voice "import class xxx" in the options available.

4.The binding and unbinding of the beacon manager should be done within the OnResume and OnPause methods:

NOTE: We don't call the bind/unbind methods in onCreate and onDestroy methods, otherwise this may conflict with the BeaconTagActivity, or with other custom activities you may define.

    @Override
    protected void onResume() {
        super.onResume();
        beaconManager.bind(this);
    }

    @Override
    protected void onPause() {
        if (beaconManager.isBound(this)) beaconManager.unbind(this);
        super.onPause();
    }

5.We implement the OnBeaconServiceConnect by defining how to react when tags are detected. You can detect and interact with beacons in two different ways:

  • Monitoring: actions triggered on entering/exiting region's range; to define a region use beacon tags. see Tagging beacons
  • Ranging: actions triggered based on proximity to a beacon;
    @Override
    public void onBeaconServiceConnect() {

        BeaconTags.getInstance().clearNotifiers(beaconManager);

        BeaconTags.getInstance().addRangeNotifierForTag(beaconManager, "near", new TagRangeNotifier() {
            @Override
            public void onTaggedBeaconReceived(BeaconMsgBase b) {
                BeaconMsgNearable beac = (BeaconMsgNearable) (new BeaconMsgNearable(b).parse());
                if (beac!=null) {
                    addLogLine(String.format("Found nearable! Temp:%s, AX: %s, AY: %s, AZ: %s",Double.valueOf(Math.round(beac.getTemp())),beac.getAccellX(),beac.getAccellY(),beac.getAccellZ()));
                    Log.i("MainActivityBeacon",String.format("Found my nearable: %s",beac.toString()));
                } else {
                    addLogLine(String.format("This is not a nearable message: %s",b.getKeyIdentifier()));
                    Log.e("MainActivityBeacon",String.format("This is not a nearable message: %s",b.getKeyIdentifier()));
                }
            }
        });

        BeaconTags.getInstance().addRangeNotifierForTag(beaconManager, "ice", new TagRangeNotifier() {
            @Override
            public void onTaggedBeaconReceived(BeaconMsgBase b) {
                addLogLine(String.format("ICE Beacon in range for tag:%s, key:%s, ids:%s","ice", b.getParserSimpleClassname(), b.getIdentifiers().toString()));
            }
        });

        BeaconTags.getInstance().addMonitorNotifier(beaconManager, "sky", new TagMonitorNotifier(){
            @Override
            public void didEnterTag(String tag) {
                addLogLine(String.format("ENTER tag %s",tag));
            }

            @Override
            public void didExitTag(String tag) {
                addLogLine(String.format("EXIT tag %s",tag));
            }

            @Override
            public void didDetermineStateForTag(int i, String tag) {
                addLogLine(String.format("didDetermineStateForTag tag %s, %d",tag,i));
            }
        });
    }

As you can see from the code above:

  • The program ranges for the near tag: each time a beacon message tagged with near is received by the smartphone, the defined callback is executed. The new BeaconMsgNearable(b).parse(); line converts a message into the most appropriate Java class.
  • The program ranges for the ice tag: each time a beacon message tagged with ice is received by the smartphone, the defined callback is executed.
  • The program monitors the sky tag: each time a beacon with tag sky enters or leaves the bluetooth range of the smartphone, the appropriates callbacks are executed.

6.Finally we define the utility method, which simply adds a log message to the application user interface, each time some beacons get in range:

    private void addLogLine(final String s) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                logLines.add(0,String.format("%s %s", Calendar.getInstance().get(Calendar.SECOND),s));
                if (logLines.size()>10) logLines.remove(logLines.size()-1);
                logLinesAdapter.notifyDataSetChanged();
            }
        });
    }

Starting your application

When you run your application for the first time, no tags have been yet assigned to beacons.

App starts with no tags assigned


Tagging beacons

To tag our beacons it is sufficient to run our app, and click on the appropriate button, dedicated to start the BeaconTagActivity.

The BeaconTagActivity User Interface offers two tabs: Beacons Nearby and Tags .

The Beacons Nearby tab gives you a list of the real time messages received from beacons near the smarthpone. Each message identifies a beacon, but a beacon can be configured with vendor-provided dedicated apps, to transmit more than one message at a time, also with different time intervals.

Beacons Nearby tab

NOTE: If no beacons are displayed, double check that you gave required permissions to the app and try turning GPS on.

For each message these attributes are displayed:

  • a message identifier (which does not change for subsequent messages)
  • a message type (which is also the name of the related java class used for its parsing)
  • a distance from smartphone indication (note this is only an indicator in Beta 0.4 release, so should not be considered reliable for production uses)
  • the time since last message arrival: if a message is not received for more than 30 seconds, than it is removed from the list
  • a button to add, edit or remove a tag from this message, with a label displaying the number of tags currently assigned to this message.

By clicking on the button related to one of the beacons nearby, it is possible to define a list of comma-separated tags. The textbox provides you with an Autocomplete feature, so if you don't exactly rembember a previously assigned tag name, then the UI will help you, by displaying all the tags already assigned, beginning with your input. From the same textbox you can also remove one or all the existing tags assigned to the beacon.

Add, Edit or Remove a tag for a beacon message

From the Tags tab instead, you can review the list of your currently defined existing tags and drop them if you don't need them anymore, by clicking the button next to them.

Tags tab

From the activity's menu you can choose to save the current tag configuration to your app's sharedpreferences. In this way your app will rembember the tag configuration after a restart.

Save current tags configuration


Back to your app

When you finished tagging your beacon, your app will now be able to detect beacons as expected.

App after tags assignments

You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.