Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: start/stop advertising #27

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
dependencies {
// Add the module's library dependencies here.
// See: https://developer.android.com/studio/build/dependencies
implementation 'org.altbeacon:android-beacon-library:2+'
// implementation 'org.altbeacon:android-beacon-library:2.19.6'
implementation 'org.altbeacon:android-beacon-library:2.19.5'
}
Binary file removed dist/com.liferay.beacons-android-2.0.0.zip
Binary file not shown.
Binary file removed dist/com.liferay.beacons-android-4.0.1.zip
Binary file not shown.
Binary file added dist/com.liferay.beacons-android-4.1.0.zip
Binary file not shown.
63 changes: 44 additions & 19 deletions documentation/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ A Titanium module to interact with iBeacons in Titanium projects that support An

Place the ZIP file into your project's root directory, and declare the module and required android permissions in your `tiapp.xml` file (or in your custom `platform/android/AndroidManifest.xml` file if you are using that):

```
```xml
<ti:app>
...
<android xmlns:android="http://schemas.android.com/apk/res/android">
<manifest package="[YOUR_APP_PACKAGE_NAME]">
<uses-permission
android:name="android.permission.BLUETOOTH"/>
<uses-permission
android:name="android.permission.BLUETOOTH_SCAN"/>
<uses-permission
android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission
Expand All @@ -28,7 +30,7 @@ Place the ZIP file into your project's root directory, and declare the module an
android:label="iBeacon"
android:name="com.radiusnetworks.ibeacon.service.IBeaconService">
</service>
<service android:enabled="true"
<service android:enabled="true" android:exported="true"
android:name="com.radiusnetworks.ibeacon.IBeaconIntentProcessor">
<meta-data android:name="background" android:value="true" />
<intent-filter
Expand All @@ -52,7 +54,7 @@ Don't forget to replace the `[YOUR_APP_PACKAGE_NAME]` with your app's package na

Next, to access this module from JavaScript, you would do the following:

```
```js
var TiBeacons = null;
if (Ti.Platform.name === "android") {
TiBeacons = require("com.liferay.beacons");
Expand All @@ -63,7 +65,7 @@ if (Ti.Platform.name === "android") {

As of Android 6.0, your app will need to request permission after launch in the form of a popup. This will need to be accepted by the user or else the service will fail. You can request permisison using an approach simialr to the below.

```
```js
var permissions = ['android.permission.ACCESS_FINE_LOCATION'];
Ti.Android.requestPermissions(permissions, function(e) {
if (e.success) {
Expand Down Expand Up @@ -93,7 +95,7 @@ A typical workflow for beacons, and the corresponding JavaScript APIs for this m

1. Get a reference to the module via

```
```js
var TiBeacons = require('com.liferay.beacons');
```

Expand All @@ -105,7 +107,7 @@ android.os.RemoteException: The IBeaconManager is not bound to the service. Call

Instead of guessing when the service is ready, we can check using the following method adn force the bind:

```
```js
var handle;
TiBeacons.bindBeaconService(); // This will force the bind to prevent TiBeacons.isReady() from always remaining false
handle = setInterval(function(){
Expand All @@ -126,7 +128,7 @@ handle = setInterval(function(){

4. Attach listeners for region and range events

```
```js
TiBeacons.addEventListener("enteredRegion", handleRegionEnter);
TiBeacons.addEventListener("exitedRegion", handleRegionExit);
TiBeacons.addEventListener("determinedRegionState", handleRegionDeterminedState);
Expand All @@ -138,13 +140,13 @@ TiBeacons.addEventListener("beaconRanges", handleRanges);

You can also remove event listeners using the `TiBeacons.removeEventListener()`, for example:

```
```js
TiBeacons.removeEventListener("enteredRegion", handleRegionEnter);
```

5. Begin monitoring one or more regions

```
```js
TiBeacons.startMonitoringForRegion({
identifier: 'Region by UUID only',
uuid: '11111111-2222-3333-4444-555555555555'
Expand All @@ -171,7 +173,7 @@ If autoranging is enabled, then the moment a region is entered, ranging (which i

If autoranging is NOT enabled, you must manually begin ranging (if you are interested in proximity/range events) like this:

```
```js
TiBeacons.startRangingForBeacons({
identifier: 'Region by UUID only',
uuid: '11111111-2222-3333-4444-555555555555'
Expand All @@ -182,7 +184,7 @@ TiBeacons.startRangingForBeacons({

To turn everything off:

```
```js
TiBeacons.stopRangingForAllBeacons();
TiBeacons.stopMonitoringAllRegions();
TiBeacons.unbindBeaconService(); // to force unbind
Expand All @@ -194,15 +196,15 @@ When one of your registered listeners' callbacks is called, they will receive di

#### enteredRegion

```
```js
function enteredRegionCallback(e) {
console.log("identifer: " + e.identifier);
}
```

#### exitedRegion

```
```js
function exitedRegionCallback(e) {
console.log("identifer: " + e.identifier);
}
Expand All @@ -212,7 +214,7 @@ function exitedRegionCallback(e) {

State can be either `inside` or `outside`. If the state is determined to be *unknown* then the callback will not be called.

```
```js
function determinedRegionStateCallback(e) {
console.log("identifer: " + e.identifier);

Expand All @@ -223,7 +225,7 @@ function determinedRegionStateCallback(e) {

#### beaconProximity

```
```js
function beaconProximityCallback(e) {
console.log("identifer: " + e.identifier);
console.log("uuid: " + e.uuid);
Expand Down Expand Up @@ -251,7 +253,7 @@ You normally only need to listen for `beaconProximity` *or* `beaconRanges`. You

Also note that the order of the beacons in the array of the `beaconRanges` event is not guaranteed to be in any particular order across callbacks.

```
```js
function beaconRangingCallback(e) {

console.log("I am in the " + e.identifier + " region");
Expand All @@ -272,6 +274,29 @@ function beaconRangingCallback(e) {
}
```

### Emulate a beacon - start/stop advertising

You can emulate a beacon with `startAdvertise()`/`stopAdvertise()`.
If you start to advertise you have to set those parameters:
`id1` (string), `id2`(string), `id3`(string), `txPower`(int), `manufacturer`(int), `beaconLayout` (string). An example:

```js
TiBeacons.startAdvertise({
id1: "2f234454-cf6d-4a0f-adf2-f4911ba9ffa6",
id2: "1",
id3: "2",
txPower: -58,
manufacturer: 0x0118,
beaconLayout: "m:2-3=beac,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25"
})

TiBeacons.stopAdvertise();
```

Add `<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE"/>` to your tiapp.xml `<manifest>` block and into your permission request if you use Android 12+

There are two events that will be fired: `success` or `error` (with errorCode).

### Foreground vs. Background

It is is a good idea for apps to reduce their power consumption when placed in the background by
Expand All @@ -283,15 +308,15 @@ however **this module DOES NOT DETECT when your app is sent to the background or
You must manually detect foreground/background events and call the appropriate APIs on this module to tell it
that it is now in the background and should use the background scan periods. Check out [Ben Bahrenburg's excellent
Android Tools](https://github.com/benbahrenburg/benCoding.Android.Tools) for a super-easy way to auto-detect this. Here's an example:
```
```js
var androidPlatformTools = require('bencoding.android.tools').createPlatform();
var isForeground = androidPlatformTools.isInForeground();
console.log("Am I currently in the foreground? " + isForeground);
```
You can call this repeatedly (e.g. every 5 seconds) using `setInterval()` and when foreground vs. background is detected, call `TiBeacons.setBackgroundMode()`. At least that's what I do.

To configure the scan periods for foreground and background:
```
```js
var TiBeacons = require('com.liferay.beacons');
TiBeacons.setScanPeriods({
foregroundScanPeriod: 1000,
Expand All @@ -312,7 +337,7 @@ and the defaults.

Here is a simple `app.js` application that you can use to see if things are working. You may need to modify it a bit to align with your specific beacon UUID.

```
```js
// sample Titanium app.js app to test that things are working,
// this assumes your hardware supports BLE and it's switched on.
// you can use checkAvailability() to see if it's supported, but
Expand Down
2 changes: 1 addition & 1 deletion manifest
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
# limitations under the License.
#

version: 4.0.1
version: 4.1.0
apiversion: 4
architectures: arm64-v8a armeabi-v7a x86 x86_64
description: Enables Titanium apps supporting android to interact with iBeacons
Expand Down
57 changes: 56 additions & 1 deletion src/com/liferay/beacons/LiferayBeaconsModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,19 @@

package com.liferay.beacons;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.bluetooth.le.AdvertiseCallback;
import android.bluetooth.le.AdvertiseSettings;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.RemoteException;
import com.radiusnetworks.ibeacon.*;

import org.altbeacon.beacon.Beacon;
import org.altbeacon.beacon.BeaconParser;
import org.altbeacon.beacon.BeaconTransmitter;
import org.appcelerator.kroll.KrollDict;
import org.appcelerator.kroll.KrollModule;
import org.appcelerator.kroll.annotations.Kroll;
Expand All @@ -33,12 +40,13 @@
import org.appcelerator.titanium.util.TiConvert;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;

/**
* Events: enteredRegion, exitedRegion, determinedRegionState, beaconProximity
* Events: enteredRegion, exitedRegion, determinedRegionState, beaconProximity, success, error
*/
@Kroll.module(name="LiferayBeacons", id="com.liferay.beacons")
public class LiferayBeaconsModule extends KrollModule implements IBeaconConsumer
Expand All @@ -51,6 +59,7 @@ public class LiferayBeaconsModule extends KrollModule implements IBeaconConsumer

private boolean autoRange = true;
private boolean ready = false;
private BeaconTransmitter beaconTransmitter;

public LiferayBeaconsModule() {
super();
Expand Down Expand Up @@ -504,5 +513,51 @@ public boolean bindService(Intent intent, ServiceConnection serviceConnection, i
Log.d(TAG, "bindService");
return super.getActivity().bindService(intent, serviceConnection, i);
}

@SuppressLint("NewApi")
@Kroll.method
public void startAdvertise(KrollDict kd) {
String id1 = TiConvert.toString(kd.get("id1"), "");
String id2 = TiConvert.toString(kd.get("id2"), "");
String id3 = TiConvert.toString(kd.get("id3"), "");
String beaconLayout = TiConvert.toString(kd.get("beaconLayout"), "");
int manu = TiConvert.toInt(kd.get("manufacturer"), 0);
int txPower = TiConvert.toInt(kd.get("txPower"), 0);

Beacon beacon = new Beacon.Builder()
.setId1(id1)
.setId2(id2)
.setId3(id3)
.setManufacturer(manu)
.setTxPower(txPower)
.setDataFields(Arrays.asList(new Long[] {0l}))
.build();
BeaconParser beaconParser = new BeaconParser().setBeaconLayout(beaconLayout);
beaconTransmitter = new BeaconTransmitter(getApplicationContext(), beaconParser);
beaconTransmitter.startAdvertising(beacon, new AdvertiseCallback() {
@Override
public void onStartSuccess(AdvertiseSettings settingsInEffect) {
super.onStartSuccess(settingsInEffect);
KrollDict kdSuccess = new KrollDict();
fireEvent("success", kdSuccess);
}

@Override
public void onStartFailure(int errorCode) {
super.onStartFailure(errorCode);
KrollDict kdError = new KrollDict();
kdError.put("errorCode", errorCode);
fireEvent("error", kdError);
}
});
}

@Kroll.method
public void stopAdvertise() {
if (beaconTransmitter != null) {
beaconTransmitter.stopAdvertising();
beaconTransmitter = null;
}
}
}