Android App Synchronization with iBeacon - Case Study

September 8th, 2015

Case Studies / Mobile // Natalie

iBeacon Experience Case Study

Recently our developers have been working on a project that required creating an app that would be connected with the iBeacon technology to track the user's car movements (style of driving, speed, etc.) and transmit the data to an insurance company. Another part of the project was social networking element, where this data would be available for sharing (status updates, achievements recorded, contests won.) 

(Side note: this project was done before the appropriate kits and iBeacon libraries were published, so we had to invent a lot as we went.)

What was the main task? 

We had to setup and optimize the synchronization of iBeacon systems with the iPhones and Android phones. For that, we needed to create an app that would connect to iBeacon and automatically start recording the information from the device like speed, position, movements, etc. and be connected to the user’s account. Speed parameter was used as a trigger for the app launch. Besides speed, there was also a possibility to use Google services to manage the app, but we opted for the speed. 

Once we got the main questions about the technology out of the way, we had to come up a way to identify the user and then set up the logic of the workflow: launch the app, gain minimum speed, launch the iBeacon tracker, if the car is idle for over 10 minutes, stop tracking, send the information from the app. This was the main development process. 

What technologies did we use? 

Technologies Core Bluetooth, Core Location, Objective-C, Swift, Java, Ionic, CocoaPods, j2objc, JSON, XML, Git, Jira, Crashlytics/Fabric, iBeacon, GPS
Operating Systems Android OS, iOS OS, Ubuntu OS, Mac OS X
Development Tools and Environments Android SDK, JDK, Android Studio, Xcode, SourceTree, CLI
Databases SQLite

We decided to go with iBeacon. It is a little Bluetooth hardware device that has a short description within itself as for its purpose and transmits the information about it. It offers Location services, so it can be used for navigation or, for example, be a tour guide around a museum, checking the position of the user. Besides iBeacon, we had to work with T2 technology that is used to transmit the information from the car’s computer to the device (and from there, to the user's phone). 

Who was on the project team?

Team Leader: 1
Project Manager: 1
Developers: 2
QA Engineers: 1

What difficulties did we encounter during the development of the iBeacon for Android? 

The main difficulty was that, at the moment of the development, there was no Google iBeacon SDK for discovery and work with iBeacon devices. Android and iBeacons were at the very start of their successful journey. 

Like all good developers, our team went on a search for the ready solutions to save some development time. However, after the search was performed, they found only a couple of libraries that had minimum functionality:

  • Discovery
  • Distance calculator

Thankfully, these were the requirements we needed for our task - discovering the device and then, based on location, perform a few other tasks. We decided to use the org.altbeacon library and here's a short tutorial about how we were figuring out the question: "can iBeacon work on Android?" and how to make it happen.

onBeaconServiceConnect - launches from Application project class to launch the scanning service

setup - our manager's method, that would set the parameters for search region and other necessary tools 

class RunCheckingBeaconThread extends Thread

This thread starts the methods for scanner launch. It runs constantly by default until the user turns off this feature in the iBeacon launch settings.

The code looks like this:

    private class RunCheckingBeaconThread extends Thread {
 
        private int trySearchIBeaconCount = 0;
 
        @Override
        public void run() {
            super.run();
            while (true) {
                /**
                 * Sleep for correct work system
                 */
                try {
                    TimeUnit.MILLISECONDS.sleep(App.AutoStartByIBeacon.DELAY_SCAN_MS);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (identityManager != null
                        && identityManager.getAuthenticatedUser() == null) {
                    identityManager.setup();
                }
                /**
                 * If beacon manager not work, we execute start
                 * manager for scan for search iBeacon devices
                 */
                if (canStartScanning()) {
                    if (bluetoothAdapter != null
                            && !bluetoothAdapter.isEnabled()
                            && preferencesManager.isAutoStartByIBeaconEnabled()) {
                        bluetoothAdapter.enable();
                    }
                    setProcessScanningIBeaconWork(true);
                    beaconManager.setRangeNotifier(new RangeNotifier() {
                        @Override
                        public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
                            if (beacons.size() > 0) {
                                for (Beacon beacon : beacons) {
                                    checkBeaconWithMyDevices(beacon);
                                }
                            } else {
                                addBeaconToFoundedList(new MyBeacon());
                                sendSetDeviceMac(toStringArrayDevicesFounded());
                                if (trySearchIBeaconCount > 10) {
                                    if (TelemetryServiceManager.getStaticBeaconStatus() == BeaconStatus.CONNECTED) {
                                        sendSetBeaconStatus(BeaconStatus.LOST_OF_RANGE);
                                    } else if(TelemetryServiceManager.getStaticBeaconStatus() != BeaconStatus.DISCONNECTED){
                                        sendSetBeaconStatus(BeaconStatus.NONE);
                                    }
                                    trySearchIBeaconCount = 0;
                                } else {
                                    trySearchIBeaconCount++;
                                }
                            }
                        }
                    });
                    try {
                        beaconManager.startRangingBeaconsInRegion(region);
                    } catch (RemoteException e) {
                    }
                }
            }
        }
    }
 
    private boolean canStartScanning() {
        return preferencesManager.isAutoStartByIBeaconEnabled()
                && !isProcessScanningIBeaconWork()
                && isSetupFinished()
                && beaconManager != null
                && preferencesManager.isRecordingServiceEnable()
                && identityManager != null
                && identityManager.isAuthenticated()
                && !isConnectedBeacon;
    }

In the meantime, the app gets a list of found iBeacon devices nearby.

checkBeaconWithMyDevices(Beacon beacon)

This method receives one of the found devices, adds it to the list of found ones, and checks whether this device belongs to the logged in user via MAC address. 

After this, the status is updated and the list of the devices that belong to the user also updates.  

    private void checkBeaconWithMyDevices(Beacon beacon) {
        addBeaconToFoundedList(beacon);
        sendSetDeviceMac(toStringArrayDevicesFounded());
        updateMyDevices();
        boolean isFounded = false;
        for (BLDevice device : myDevices) {
            if (beacon.getBluetoothAddress().contentEquals(device.addressMAC)
                    && (lastDistance = beacon.getDistance()) <= mProfile.getMovementIBeaconDistance()) {
                isFounded = true;
 
                context.sendBroadcast(new Intent(ACTION_ENABLE_GPS_MANAGER));
 
                /**
                 * update timer, if connection lose > 10 sec - set isConnectedBeacon false
                 */
                updateTimerCheckingConnections();
                try {
                    currentRSSI = String.valueOf(beacon.getRssi());
                } catch (Exception e) {
                    e.printStackTrace();
                }
 
                /**
                 * Save info about connected iBeacon device
                 * for send to server
                 */
                if (device.name == null) {
                    device.name = beacon.getBluetoothName();
                    device.uuid = String.valueOf(beacon.getServiceUuid());
                }
                preferencesManager.setCurrentBLDevice(device);
 
                /**
                 * If the user has scored the required speed
                 * and iBeacon is status connected
                 * start record trip
                 * else if speed = 0 and iBeacon is disconnected
                 * record trip stop, and send to server
                 */
                if (isConnectedBeacon
                        && executeCalcSpeed() > mProfile.getMovementStartSpeed()) {
                    if (listenerTrip != null
                            && !listenerTrip.isRecording()) {
                        listenerTrip.record();
                    }
                    updateCheckSpeed();
                } else {
                    if (listenerTrip != null
                            && listenerTrip.isRecording()
                            && !isConnectedBeacon
                            && executeCalcSpeed() < mProfile.getMovementStartSpeed()) {
                        listenerTrip.stop();
                    }
                }
            } else {
 
            }
        }
        if (!isFounded) {
            saveOrUpdateDataIBeacon(beacon);
        }
    }

boolean updateTimerCheckingConnections()

We sent the information about the GPS tracking launch to track the speed and to continue the workflow once the object is moving at a set speed (in our case, set at 30 km/h) or faster.

See the code that checks the app's connection with the iBeacon device below:

    private boolean updateTimerCheckingConnections() {
        sendSetBeaconStatus(BeaconStatus.CONNECTED);
        Log.w(TAG, "### CONNECTED");
        handlerCheckingConnectionToIBeacon.removeCallbacksAndMessages(null);
        if (!isShowStatusConnectedToast) {
            handlerCheckingConnectionToIBeacon.post(setStatusConnectedWorker);
            isShowStatusConnectedToast = true;
        }
        handlerCheckingConnectionToIBeacon.postDelayed(
                setStatusDisconnectedWorker,
                App.AutoStartByIBeacon.DELAY_CONNECTION_UPDATE);
        return isConnectedBeacon = true;
    }
 
    private Runnable setStatusConnectedWorker = new Runnable() {
        @Override
        public void run() {
            Tools.showToastCenter(MyApplication.getInstance(), context.getString(R.string.found_ibeacon_device));
            toggleVibrate(true);
            sendSetBeaconStatus(BeaconStatus.CONNECTED);
        }
    };
 
    private Runnable setStatusDisconnectedWorker = new Runnable() {
        @Override
        public void run() {
            sendSetBeaconStatus(BeaconStatus.LOST_OF_RANGE);
            if (isProcessScanningIBeaconWork()) {
                sendSetBeaconStatus(BeaconStatus.DISCONNECTED);
                Log.w(TAG, "### DISCONNECTED");
                isConnectedBeacon = false;
                Tools.showToastCenter(context, context.getString(R.string.lose_ibeacon_device));
                toggleVibrate(false);
                isShowStatusConnectedToast = false;
                dropGPSManagerListener();
                /**
                 * If device lose connection with iBeacon
                 * and speed < THRESHOLD_SPEED
                 * device stop recording trip
                 */
                if (listenerTrip != null
                        && listenerTrip.isRecording()
                        && executeCalcSpeed() < mProfile.getMovementStartSpeed()) {
                    listenerTrip.stop();
                }
            }
        }
    };

While the workflow execution, the code also monitors the connection with the iBeacon device and the messages are broadcasted to the user via UI. The entire process is interacting with the service that is responsible for interaction with the user. 

Want mobile app development for your business?

Author: Natalie

Natalie is a Project Manager who is a great team leader for her mobile development team. She is an expert when it comes to iOS and Android development and to building apps that win markets.

Tags iBeacon

See all blog

x

Grossum Startup Guide:
Five Things to Consider Before Web & Mobile Development

Get the Guide