diff --git a/WearScript/build.gradle b/WearScript/build.gradle
index 02598b77..9477c21a 100644
--- a/WearScript/build.gradle
+++ b/WearScript/build.gradle
@@ -20,7 +20,7 @@ android {
buildToolsVersion "19.1.0"
defaultConfig {
- minSdkVersion 15
+ minSdkVersion 19
targetSdkVersion 19
}
}
diff --git a/WearScript/src/main/AndroidManifest.xml b/WearScript/src/main/AndroidManifest.xml
index 1358cecd..45bdfa41 100644
--- a/WearScript/src/main/AndroidManifest.xml
+++ b/WearScript/src/main/AndroidManifest.xml
@@ -17,6 +17,9 @@
+
diff --git a/WearScript/src/main/assets/init.js b/WearScript/src/main/assets/init.js
index abd08c86..1cc90ca1 100644
--- a/WearScript/src/main/assets/init.js
+++ b/WearScript/src/main/assets/init.js
@@ -1016,9 +1016,19 @@ function WearScript() {
this.group = function () {
return WSRAW.group();
}
- this.bluetoothList = function (callback) {
+ this.beacon = function (rangeCb, enterCb, exitCb) {
+ rangeCb = this._funcfix(rangeCb);
+ enterCb = this._funcfix(enterCb);
+ exitCb = this._funcfix(exitCb);
+ WSRAW.beacon(this._funcwrap(rangeCb), this._funcwrap(enterCb), this._funcwrap(exitCb));
+ }
+ this.bluetoothList = function (callback, btle) {
callback = this._funcfix(callback);
- WSRAW.bluetoothList(this._funcwrap(function (x) {callback(JSON.parse(x))}));
+ if(btle) {
+ WSRAW.bluetoothList(this._funcwrap(callback), true);
+ } else {
+ WSRAW.bluetoothList(this._funcwrap(function (x) {callback(JSON.parse(x))}), false);
+ }
}
this.bluetoothRead = function (address, callback) {
callback = this._funcfix(callback);
diff --git a/WearScript/src/main/assets/init.js.min b/WearScript/src/main/assets/init.js.min
index 17a3d22a..1fc83db5 100644
--- a/WearScript/src/main/assets/init.js.min
+++ b/WearScript/src/main/assets/init.js.min
@@ -25,6 +25,7 @@ WSRAW.warpGlassToPreviewH(this._funcwrap(function(c){a(JSON.parse(c))}))};this.w
h?(h=this._funcfix(h),WSRAW.cameraOn(a,c,b,!1,this._funcwrap(h))):WSRAW.cameraOn(a,c,b,!1)};this.cameraOnBackgroundUnsafe=function(a,c,b,h){c||(c=0);b||(maxwidth=0);h?(h=this._funcfix(h),WSRAW.cameraOn(a,c,b,!0,this._funcwrap(h))):WSRAW.cameraOn(a,c,b,!0)};this.cameraPhoto=function(a){a?(a=this._funcfix(a),WSRAW.cameraPhoto(this._funcwrap(a))):WSRAW.cameraPhoto()};this.cameraPhotoData=function(a){a?(a=this._funcfix(a),WSRAW.cameraPhotoData(this._funcwrap(a))):WSRAW.cameraPhoto()};this.cameraVideo=
function(a){a?(a=this._funcfix(a),WSRAW.cameraVideo(this._funcwrap(a))):WSRAW.cameraVideo()};this.cameraOff=function(){WSRAW.cameraOff()};this.activityCreate=function(){WSRAW.activityCreate()};this.activityDestroy=function(){WSRAW.activityDestroy()};this.wifiOn=function(a){a=this._funcfix(a);WSRAW.wifiOn(this._funcwrap(a))};this.wifiOff=function(){WSRAW.wifiOff()};this.wifiScan=function(){WSRAW.wifiScan()};this.serverConnect=function(a,c){c=this._funcfix(c);WSRAW.serverConnect(a,this._funcwrap(c))};
this.wake=function(){WSRAW.wake()};this.sound=function(a){WSRAW.sound(a)};this.gestureCallback=function(a,c){c=this._funcfix(c);WSRAW.gestureCallback(a,this._funcwrap(c))};this.speechRecognize=function(a,c){c=this._funcfix(c);WSRAW.speechRecognize(a,this._funcwrap(function(a){c(atob(a))}))};this.liveCardCreate=function(a,c){var b=[],h=Array.prototype.slice.call(arguments).slice(2);0==h.length&&(h.push("Open"),h.push(function(){WS.activityCreate()}),h.push("Shutdown"),h.push(function(){WS.shutdown()}));
-for(var k=0;k gatts;
+
+ public BluetoothLEManager(BackgroundService bs) {
+ super(bs);
+ gatts = new ConcurrentHashMap();
+ BluetoothManager bluetoothManager = (BluetoothManager) service.getSystemService(Context.BLUETOOTH_SERVICE);
+ mBluetoothAdapter = bluetoothManager.getAdapter();
+ }
+
+ @Override
+ public void reset() {
+ super.reset();
+ scanOff();
+ }
+
+ @Override
+ public void shutdown() {
+ super.shutdown();
+ scanOff();
+ for(GattConnection connection : gatts.values()){
+ connection.close();
+ }
+ gatts.clear();
+ }
+
+
+ @Override
+ protected void registerCallback(String type, String jsFunction) {
+ super.registerCallback(type, jsFunction);
+ if (type.startsWith(READ)) {
+ scanOff();
+ String address = type.substring(READ.length());
+ BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
+ if(!gatts.containsKey(address)){
+ gatts.put(address, new GattConnection(jsFunction));
+ }
+ device.connectGatt(service, true, gatts.get(address));
+ }else if(type.equals(LIST)){
+ scanOn();
+ }
+
+ }
+
+ private void scanOff() {
+ if(mBluetoothAdapter != null)
+ mBluetoothAdapter.stopLeScan(mLeScanCallback);
+ }
+
+ public void scanOn() {
+ mBluetoothAdapter.startLeScan(mLeScanCallback);
+ }
+
+ private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
+ @Override
+ public void onLeScan(BluetoothDevice bluetoothDevice, int i, byte[] bytes) {
+ makeCall(LIST, bluetoothDevice.getAddress(), bluetoothDevice.getName());
+ }
+ };
+
+ private class GattConnection extends BluetoothGattCallback {
+ private static final int STATE_DISCONNECTED = 0;
+ private static final int STATE_CONNECTING = 1;
+ private static final int STATE_CONNECTED = 2;
+
+ private final String mCallback;
+ private int mConnectionState = STATE_DISCONNECTED;
+ private BluetoothGatt mGatt;
+
+ GattConnection(String callback) {
+ super();
+ mCallback = callback;
+ }
+
+ @Override
+ public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
+ super.onConnectionStateChange(gatt, status, newState);
+ mGatt = gatt;
+ if (newState == BluetoothProfile.STATE_CONNECTED) {
+ mConnectionState = STATE_CONNECTED;
+ gatt.discoverServices();
+
+ } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
+ mConnectionState = STATE_DISCONNECTED;
+ }
+ }
+
+ @Override
+ public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
+ super.onCharacteristicRead(gatt, characteristic, status);
+ mGatt = gatt;
+ final byte[] data = characteristic.getValue();
+ if (data != null && data.length > 0) {
+ final StringBuilder stringBuilder = new StringBuilder(data.length);
+ for(byte byteChar : data)
+ stringBuilder.append(String.format("%02X ", byteChar));
+ makeCall(mCallback, stringBuilder.toString());
+ }
+ }
+
+ public void close() {
+ if(mGatt != null)
+ mGatt.close();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/WearScript/src/main/java/com/dappervision/wearscript/managers/DataManager.java b/WearScript/src/main/java/com/dappervision/wearscript/managers/DataManager.java
index da0ce0eb..b98fff3f 100644
--- a/WearScript/src/main/java/com/dappervision/wearscript/managers/DataManager.java
+++ b/WearScript/src/main/java/com/dappervision/wearscript/managers/DataManager.java
@@ -9,7 +9,6 @@
import com.dappervision.wearscript.dataproviders.DataPoint;
import com.dappervision.wearscript.dataproviders.DataProvider;
import com.dappervision.wearscript.dataproviders.GPSDataProvider;
-import com.dappervision.wearscript.dataproviders.IBeaconDataProvider;
import com.dappervision.wearscript.dataproviders.NativeDataProvider;
import com.dappervision.wearscript.dataproviders.PebbleDataProvider;
import com.dappervision.wearscript.dataproviders.RemoteDataProvider;
@@ -54,8 +53,6 @@ else if (type == WearScript.SENSOR.BATTERY.id())
dp = new BatteryDataProvider(this, samplePeriod);
else if (type == WearScript.SENSOR.PEBBLE_ACCELEROMETER.id())
dp = new PebbleDataProvider(this, samplePeriod, type);
- else if (type == WearScript.SENSOR.IBEACON.id())
- dp = new IBeaconDataProvider(this, samplePeriod);
else
throw new RuntimeException("Invalid type: " + type);
registerProvider(type, dp);
diff --git a/WearScript/src/main/java/com/dappervision/wearscript/managers/IBeaconManager.java b/WearScript/src/main/java/com/dappervision/wearscript/managers/IBeaconManager.java
index 3346075a..d6ae880c 100644
--- a/WearScript/src/main/java/com/dappervision/wearscript/managers/IBeaconManager.java
+++ b/WearScript/src/main/java/com/dappervision/wearscript/managers/IBeaconManager.java
@@ -7,10 +7,6 @@
import com.dappervision.wearscript.BackgroundService;
import com.dappervision.wearscript.Log;
-import com.dappervision.wearscript.Utils;
-import com.dappervision.wearscript.WearScript;
-import com.dappervision.wearscript.events.SendEvent;
-import com.dappervision.wearscript.events.SensorJSEvent;
import com.radiusnetworks.ibeacon.IBeacon;
import com.radiusnetworks.ibeacon.IBeaconConsumer;
import com.radiusnetworks.ibeacon.RangeNotifier;
@@ -18,16 +14,17 @@
import java.util.Collection;
-import com.radiusnetworks.ibeacon.IBeaconConsumer;
import com.radiusnetworks.ibeacon.MonitorNotifier;
-import com.radiusnetworks.ibeacon.Region;
public class IBeaconManager extends Manager implements IBeaconConsumer{
- com.radiusnetworks.ibeacon.IBeaconManager iBeaconManager;
+ public static final String RANGE_NOTIFICATION = "RANGE_NOTIFICATION";
+ public static final String ENTER_REGION = "ENTER_REGION";
+ public static final String EXIT_REGION = "EXIT_REGION";
+ private com.radiusnetworks.ibeacon.IBeaconManager iBeaconManager;
- Region MONITOR_REGION = new Region("myMonitoringUniqueId", null, null, null);
- Region RANGING_REGION = new Region("myRangingUniqueId", null, null, null);
+ Region MONITOR_REGION = new Region("everythingRegion", null, null, null);
+ Region RANGING_REGION = new Region("everythingRegion", null, null, null);
public IBeaconManager(BackgroundService bs) {
super(bs);
@@ -36,22 +33,23 @@ public IBeaconManager(BackgroundService bs) {
@Override
public void reset() {
super.reset();
- ibeaconSensorOff();
- ibeaconSensorOn();
+ ibeaconOff();
}
@Override
public void shutdown() {
super.shutdown();
- ibeaconSensorOff();
+ ibeaconOff();
}
- public void ibeaconSensorOn() {
- iBeaconManager = com.radiusnetworks.ibeacon.IBeaconManager.getInstanceForApplication(service);
- iBeaconManager.bind(this);
+ public void ibeaconOn() {
+ if(iBeaconManager == null)
+ iBeaconManager = com.radiusnetworks.ibeacon.IBeaconManager.getInstanceForApplication(service);
+ if(!iBeaconManager.isBound(this))
+ iBeaconManager.bind(this);
}
- public void ibeaconSensorOff() {
+ public void ibeaconOff() {
if(iBeaconManager == null)
return;
try {
@@ -64,15 +62,17 @@ public void ibeaconSensorOff() {
iBeaconManager = null;
}
- public void onEvent(SensorJSEvent event) {
- int type = event.getType();
- if(event.getStatus()) {
- if (type == WearScript.SENSOR.IBEACON.id())
- ibeaconSensorOn();
- }
- else {
- ibeaconSensorOff();
- }
+ @Override
+ protected void registerCallback(String type, String jsFunction) {
+ super.registerCallback(type, jsFunction);
+ ibeaconOn();
+ }
+
+ @Override
+ protected void unregisterCallback(String type) {
+ super.unregisterCallback(type);
+ if(jsCallbacks.isEmpty())
+ ibeaconOff();
}
@Override
@@ -82,7 +82,7 @@ public void onIBeaconServiceConnect() {
public void didRangeBeaconsInRegion(Collection iBeacons, Region region) {
for(IBeacon myBeacon : iBeacons){
if(myBeacon.getAccuracy() >= 0)
- Utils.eventBusPost(new SendEvent("ibeacon:"+myBeacon.getProximityUuid(), myBeacon.getProximityUuid(), myBeacon.getRssi(), myBeacon.getMajor(), myBeacon.getMinor()));
+ makeCall(RANGE_NOTIFICATION, myBeacon.getProximityUuid(), String.valueOf(myBeacon.getRssi()), String.valueOf(myBeacon.getMajor()), String.valueOf(myBeacon.getMinor()), String.valueOf(myBeacon.getProximity()));
}
}
});
@@ -94,14 +94,12 @@ public void didRangeBeaconsInRegion(Collection iBeacons, Region region)
iBeaconManager.setMonitorNotifier(new MonitorNotifier() {
@Override
public void didEnterRegion(Region region) {
- Log.i(TAG, "I just saw an iBeacon for the firt time!");
- Utils.eventBusPost(new SendEvent("ibeacon:enter", region.getProximityUuid(), region.getMajor(), region.getMinor()));
+ makeCall(ENTER_REGION, region.getProximityUuid(), String.valueOf(region.getMajor()), String.valueOf(region.getMinor()));
}
@Override
public void didExitRegion(Region region) {
- Log.i(TAG, "I no longer see an iBeacon");
- Utils.eventBusPost(new SendEvent("ibeacon:exit", region.getProximityUuid(), region.getMajor(), region.getMinor()));
+ makeCall(EXIT_REGION, region.getProximityUuid(), String.valueOf(region.getMajor()), String.valueOf(region.getMinor()));
}
@Override
diff --git a/WearScript/src/main/java/com/dappervision/wearscript/managers/Manager.java b/WearScript/src/main/java/com/dappervision/wearscript/managers/Manager.java
index c95d7447..1190873a 100644
--- a/WearScript/src/main/java/com/dappervision/wearscript/managers/Manager.java
+++ b/WearScript/src/main/java/com/dappervision/wearscript/managers/Manager.java
@@ -1,11 +1,17 @@
package com.dappervision.wearscript.managers;
+import android.text.TextUtils;
+
import com.dappervision.wearscript.BackgroundService;
import com.dappervision.wearscript.Log;
import com.dappervision.wearscript.Utils;
import com.dappervision.wearscript.events.CallbackRegistration;
import com.dappervision.wearscript.events.JsCall;
+import org.apache.commons.codec.binary.StringUtils;
+
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.concurrent.ConcurrentHashMap;
public abstract class Manager {
@@ -50,7 +56,7 @@ public void shutdown() {
Utils.getEventBus().unregister(this);
}
- protected void makeCall(String key, String data) {
+ protected void makeCall(String key, String ... data) {
Log.d(TAG, jsCallbacks.toString());
if (!jsCallbacks.containsKey(key)) {
Log.d(TAG, "Callback not found: " + key);
@@ -60,16 +66,22 @@ protected void makeCall(String key, String data) {
Utils.eventBusPost(new JsCall(url));
}
- protected void makeCallDirect(String callback, String data) {
+ protected void makeCallDirect(String callback, String ... data) {
if (callback == null || data == null)
return;
- String url = String.format("javascript:%s(%s);", callback, data);
+ String url = String.format("javascript:%s(%s);", callback, formatParams(data));
Utils.eventBusPost(new JsCall(url));
}
- protected String buildCallbackString(String key, String data) {
+ protected String buildCallbackString(String key, String... data) {
if (!jsCallbacks.containsKey(key))
throw new RuntimeException("No such callback registered");
- return String.format("javascript:%s(%s);", jsCallbacks.get(key), data);
+ return String.format("javascript:%s(%s);", jsCallbacks.get(key), formatParams(data));
+ }
+
+ private String formatParams(String[] data){
+ if(data.length == 1)
+ return data[0];
+ return "\"" + TextUtils.join("\",\"", data) + "\"";
}
}
diff --git a/WearScript/src/main/java/com/dappervision/wearscript/managers/ManagerManager.java b/WearScript/src/main/java/com/dappervision/wearscript/managers/ManagerManager.java
index 2c17f05f..a36c3ba0 100644
--- a/WearScript/src/main/java/com/dappervision/wearscript/managers/ManagerManager.java
+++ b/WearScript/src/main/java/com/dappervision/wearscript/managers/ManagerManager.java
@@ -1,5 +1,7 @@
package com.dappervision.wearscript.managers;
+import android.content.pm.PackageManager;
+
import com.dappervision.wearscript.BackgroundService;
import com.dappervision.wearscript.HardwareDetector;
@@ -43,6 +45,9 @@ public void newManagers(BackgroundService bs) {
add(new CardTreeManager(bs));
add(new EyeManager(bs));
}
+ if (bs.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)){
+ add(new BluetoothLEManager(bs));
+ }
}
public void add(Manager manager) {