Skip to content

Commit

Permalink
Merge pull request #219 from louisg1337/android_bluetooth
Browse files Browse the repository at this point in the history
πŸ€–πŸ›œ Android BLE Integration
  • Loading branch information
shankari authored Apr 14, 2024
2 parents dc1eeb1 + 5afb68b commit dd7d147
Show file tree
Hide file tree
Showing 10 changed files with 389 additions and 4 deletions.
17 changes: 15 additions & 2 deletions plugin.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<plugin xmlns="http://www.phonegap.com/ns/plugins/1.0"
id="cordova-plugin-em-datacollection"
version="1.8.4">
version="1.8.5">

<name>DataCollection</name>
<description>Background data collection FTW! This is the part that I really
Expand Down Expand Up @@ -40,6 +40,7 @@
<hook type="after_plugin_install" src="hooks/android/addResourcesClassImport.js" />

<platform name="android">
<framework src="src/android/e-mission-datacollection.gradle" custom="true" type="gradleReference" />

<config-file target="res/xml/config.xml" parent="/*">
<feature name="DataCollection">
Expand Down Expand Up @@ -73,7 +74,7 @@
<receiver
android:name="edu.berkeley.eecs.emission.cordova.tracker.location.TripDiaryStateMachineReceiver"
android:enabled="true"
android:exported="false">
android:exported="true">
<intent-filter>
<action android:name="local.transition.initialize"></action>
<action android:name="local.transition.exited_geofence"></action>
Expand Down Expand Up @@ -126,6 +127,16 @@
android:enabled="true"
android:exported="false">
</service>
<service
android:name="edu.berkeley.eecs.emission.cordova.tracker.bluetooth.BluetoothService"
android:enabled="true"
android:exported="false">
</service>
<service
android:name="edu.berkeley.eecs.emission.cordova.tracker.bluetooth.BluetoothMonitoringService"
android:enabled="true"
android:exported="false">
</service>
</config-file>

<framework src="com.google.code.gson:gson:2.10.1"/>
Expand Down Expand Up @@ -175,6 +186,8 @@
<source-file src="src/android/wrapper/ConsentConfig.java" target-dir="src/edu/berkeley/eecs/emission/cordova/tracker/wrapper"/>
<source-file src="src/android/wrapper/Timer.java" target-dir="src/edu/berkeley/eecs/emission/cordova/tracker/wrapper"/>
<source-file src="src/android/wrapper/StatsEvent.java" target-dir="src/edu/berkeley/eecs/emission/cordova/tracker/wrapper"/>
<source-file src="src/android/bluetooth/BluetoothService.java" target-dir="src/edu/berkeley/eecs/emission/cordova/tracker/bluetooth"/>
<source-file src="src/android/bluetooth/BluetoothMonitoringService.java" target-dir="src/edu/berkeley/eecs/emission/cordova/tracker/bluetooth"/>
<resource-file src="res/android/statemachine.xml" target="res/values/statemachine.xml" />
<resource-file src="res/android/values/dc_strings.xml" target="res/values/dc_strings.xml"/>
</platform>
Expand Down
11 changes: 11 additions & 0 deletions src/android/DataCollectionPlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import edu.berkeley.eecs.emission.cordova.tracker.wrapper.StatsEvent;
import edu.berkeley.eecs.emission.cordova.tracker.wrapper.BluetoothBLE;
import edu.berkeley.eecs.emission.cordova.tracker.verification.SensorControlForegroundDelegate;
import edu.berkeley.eecs.emission.cordova.tracker.bluetooth.BluetoothService;
import edu.berkeley.eecs.emission.cordova.unifiedlogger.Log;
import edu.berkeley.eecs.emission.cordova.usercache.BuiltinUserCache;
import edu.berkeley.eecs.emission.cordova.usercache.UserCacheFactory;
Expand All @@ -61,6 +62,10 @@ public void pluginInitialize() {
new StatsEvent(myActivity, R.string.app_launched));

TripDiaryStateMachineReceiver.initOnUpgrade(myActivity);

// Ask for bluetooth permissions
// We will change this with future releases, we just ran out of time implementing this into the front end
mControlDelegate.checkAndPromptBluetoothScanPermissions();
}

@Override
Expand Down Expand Up @@ -233,6 +238,12 @@ public void run() {
retVal.put("PRIORITY_NO_POWER", LocationRequest.PRIORITY_NO_POWER);
callbackContext.success(retVal);
return true;
} else if (action.equals("bluetoothScan")) {
Context ctxt = cordova.getActivity();
Log.d(ctxt, TAG, "JS requested scan for bluetooth!");
Intent bluetoothServiceIntent = new Intent(ctxt, BluetoothService.class);
ctxt.startService(bluetoothServiceIntent);
return true;
}
return false;
}
Expand Down
86 changes: 86 additions & 0 deletions src/android/bluetooth/BluetoothMonitoringService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package edu.berkeley.eecs.emission.cordova.tracker.bluetooth;

// Android imports
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;

// Altbeacon imports
import org.altbeacon.beacon.BeaconManager;
import org.altbeacon.beacon.Region;
import org.altbeacon.beacon.MonitorNotifier;
import org.altbeacon.beacon.Beacon;

// Other plugin imports
import edu.berkeley.eecs.emission.R;
import edu.berkeley.eecs.emission.cordova.unifiedlogger.Log;
import edu.berkeley.eecs.emission.cordova.tracker.ExplicitIntent;
import edu.berkeley.eecs.emission.cordova.tracker.verification.SensorControlChecks;


public class BluetoothMonitoringService extends Service {
private static String TAG = "BluetoothMonitoringService";
private BeaconManager beaconManager;
private String uuid = "bf3df3b1-6e46-35fa-86e5-927c95dd096c";

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(this, TAG, "onStartCommand called!!!!");

// Instantiate variable
beaconManager = BeaconManager.getInstanceForApplication(this);
// This line will ensure that we always get a first callback if beacon is in region when we start monitoring
// https://github.com/AltBeacon/android-beacon-library/issues/708#issuecomment-399513853
beaconManager.setRegionStatePersistenceEnabled(false);

// Start monitoring for BLE beacons
startMonitoring();

// Start sticky
return 1;
}

@Override
public IBinder onBind(Intent intent) {
return null;
}

private void startMonitoring() {
Log.d(this, TAG, "Start monitoring has been called!");

// Code to start monitoring for BLE beacons using AltBeacon library
beaconManager.addMonitorNotifier(new MonitorNotifier() {
@Override
public void didEnterRegion(Region region) {
Log.d(BluetoothMonitoringService.this, TAG, "I just saw a beacon for the first time!");
}

@Override
public void didExitRegion(Region region) {
Log.d(BluetoothMonitoringService.this, TAG, "I no longer see an beacon");
}

@Override
public void didDetermineStateForRegion(int state, Region region) {
Log.d(BluetoothMonitoringService.this, TAG, "I have just switched from seeing/not seeing beacons: "+state);
}
});

// Define our region and start monitoring
Region region = new Region(uuid, null, null, null);
beaconManager.startMonitoring(region);
Log.d(this, TAG, "Starting to monitor for our region: " + region.toString());
}

@Override
public void onDestroy() {
stopMonitoring();
super.onDestroy();
}

private void stopMonitoring() {
Log.d(this, TAG, "Stopping monitoring for the beacon.");
beaconManager.stopMonitoring(new Region(uuid, null, null, null));
beaconManager.removeAllMonitorNotifiers();
}
}
137 changes: 137 additions & 0 deletions src/android/bluetooth/BluetoothService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package edu.berkeley.eecs.emission.cordova.tracker.bluetooth;

// Android imports
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import java.util.Collection;
import java.util.Set;
import java.util.HashSet;

// Altbeacon imports
import org.altbeacon.beacon.BeaconManager;
import org.altbeacon.beacon.Region;
import org.altbeacon.beacon.RangeNotifier;
import org.altbeacon.beacon.Beacon;

// Other plugin imports
import edu.berkeley.eecs.emission.R;
import edu.berkeley.eecs.emission.cordova.unifiedlogger.Log;
import edu.berkeley.eecs.emission.cordova.tracker.ExplicitIntent;
import edu.berkeley.eecs.emission.cordova.tracker.verification.SensorControlChecks;

// Saving data
import edu.berkeley.eecs.emission.cordova.usercache.UserCacheFactory;
import edu.berkeley.eecs.emission.cordova.tracker.wrapper.BluetoothBLE;

public class BluetoothService extends Service {
private static String TAG = "BluetoothService";
private BeaconManager beaconManager;
private Set<Beacon> scanned;
private String uuid = "bf3df3b1-6e46-35fa-86e5-927c95dd096c";

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(this, TAG, "onStartCommand called!!!!");

// Instantiate variables
beaconManager = BeaconManager.getInstanceForApplication(this);
scanned = new HashSet<>();

// Check to see if we even have permission to scan at all
boolean bluetoothPermissions = SensorControlChecks.checkBluetoothScanningPermissions(this);

if (!bluetoothPermissions) {
return 1;
}

// Start scanning for BLE beacons
startBeaconScan();

// Start sticky
return 1;
}

// @Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}

private void isInRange() {
Log.d(this, TAG, "Is in range has been called!");

stopBeaconScan();

Log.d(this, TAG, "Done waiting, results are... " + scanned.size());

if (scanned.size() > 0) {
Log.d(this, TAG, "Found something!");
Log.d(this, TAG, scanned.toString());
this.sendBroadcast(new ExplicitIntent(this, R.string.transition_ble_beacon_found));
}
onDestroy();
}

private void startBeaconScan() {
// Code to start scanning for BLE beacons using AltBeacon library
Log.d(this, TAG, "startBeaconScan called!!!!");

// Keep track of how many times we have scanned
beaconManager.addRangeNotifier(new RangeNotifier() {
int numScans = 0;

@Override
public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
Log.d(BluetoothService.this, TAG, "Range notifier called...");

if (beacons.size() > 0) {
Log.d(BluetoothService.this, TAG, "Found beacons " + beacons.toString());

for (Beacon beacon : beacons) {
// Even though we are scanning for beacons in a certain region, beacons with different UUID's still come up.
// Until we figure out why that is the case, put this check in place so we only save the ones with the right UUID.
if (beacon.getId1().toString().equals(uuid)) {
scanned.add(beacon);
BluetoothBLE currWrapper = BluetoothBLE.initRangeUpdate(
beacon.getId1().toString(),
System.currentTimeMillis() / 1000, // timestamp in always in secs for us
beacon.getId2().toInt(),
beacon.getId3().toInt(),
// TODO: Figure out what to do with the distance calculations
"ProximityNear",
// accuracy = rough distance estimate limited to two decimal places (in metres)
// NO NOT ASSUME THIS IS ACCURATE - it is effected by radio interference and obstacles
// from https://github.com/petermetz/cordova-plugin-ibeacon
Math.round((beacon.getDistance() * 100)/100),
beacon.getRssi());
UserCacheFactory.getUserCache(BluetoothService.this)
.putSensorData(R.string.key_usercache_bluetooth_ble, currWrapper);
}
}
}

numScans++;

if (numScans >= 4) {
// Once we have hit certain number of scans, stop and determine if any beacons are in range
isInRange();
}
}
});

beaconManager.startRangingBeacons(new Region(uuid, null, null, null));
}

@Override
public void onDestroy() {
// stopBeaconScan();
super.onDestroy();
}

private void stopBeaconScan() {
Log.d(this, TAG, "Stopping monitoring for the beacon.");
beaconManager.stopRangingBeacons(new Region(uuid, null, null, null));
beaconManager.removeAllRangeNotifiers();
}
}
7 changes: 7 additions & 0 deletions src/android/e-mission-datacollection.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
repositories {
mavenCentral()
}

dependencies {
implementation 'org.altbeacon:android-beacon-library:2.19'
}
15 changes: 15 additions & 0 deletions src/android/location/TripDiaryStateMachineForegroundService.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
import edu.berkeley.eecs.emission.cordova.tracker.verification.SensorControlBackgroundChecker;
import edu.berkeley.eecs.emission.cordova.tracker.ExplicitIntent;
import edu.berkeley.eecs.emission.cordova.tracker.wrapper.StatsEvent;
import edu.berkeley.eecs.emission.cordova.tracker.bluetooth.BluetoothService;
import edu.berkeley.eecs.emission.cordova.tracker.bluetooth.BluetoothMonitoringService;
import edu.berkeley.eecs.emission.cordova.usercache.BuiltinUserCache;

import edu.berkeley.eecs.emission.cordova.unifiedlogger.Log;
Expand Down Expand Up @@ -79,6 +81,19 @@ public static String humanizeState(Context ctxt, String state) {
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(this, TAG, "onStartCommand called with intent = "+intent+
" flags = " + flags + " and startId = " + startId);

if (intent != null && intent.getAction() != null) {
if (intent.getAction().equals("foreground_start_bluetooth")) {
Intent bluetoothService = new Intent(this, BluetoothService.class);
this.startService(bluetoothService);
return START_STICKY;
} else if (intent.getAction().equals("foreground_start_bluetooth_monitoring")) {
Intent bluetoothService = new Intent(this, BluetoothMonitoringService.class);
this.startService(bluetoothService);
return START_STICKY;
}
}

String message = humanizeState(this, TripDiaryStateMachineService.getState(this));
if (intent == null) {
SensorControlBackgroundChecker.checkAppState(this);
Expand Down
Loading

0 comments on commit dd7d147

Please sign in to comment.