Skip to content

Commit 7b35496

Browse files
authored
Merge pull request #99 from NordicSemiconductor/develop
Version 1.4.5
2 parents e56d7f1 + e861cdd commit 7b35496

File tree

15 files changed

+482
-425
lines changed

15 files changed

+482
-425
lines changed

README.md

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ The compat library may be found on Maven Central repository. Add it to your proj
2828
following dependency:
2929

3030
```Groovy
31-
implementation 'no.nordicsemi.android.support.v18:scanner:1.4.4'
31+
implementation 'no.nordicsemi.android.support.v18:scanner:1.4.5'
3232
```
3333

3434
Projects not migrated to Android Jetpack should use version 1.3.1, which is feature-equal to 1.4.0.
@@ -144,12 +144,12 @@ your application has been killed (the receiver must be added in *AndroidManifest
144144

145145
Starting from version 1.3.0, this library may emulate such feature on older Android versions.
146146
In order to do that, a background service will be started after calling
147-
`scanner.startScan(filters, settings, context, pendingIntent)`, which will be scanning in
147+
`scanner.startScan(filters, settings, context, pendingIntent, requestCode)`, which will be scanning in
148148
background with given settings and will send the given `PendingIntent` when a device
149149
matching filter is found. To lower battery consumption it is recommended to set
150150
`ScanSettings.SCAN_MODE_LOW_POWER` scanning mode and use filter, but even with those conditions fulfilled
151151
**the battery consumption will be significantly higher than on Oreo+**. To stop scanning call
152-
`scanner.stopScan(context, pendingIntent)` with
152+
`scanner.stopScan(context, pendingIntent, requestCode)` with
153153
[the same](https://developer.android.com/reference/android/app/PendingIntent) intent in parameter.
154154
The service will be stopped when the last scan was stopped.
155155

@@ -167,7 +167,7 @@ To use this feature:
167167
Intent intent = new Intent(context, MyReceiver.class); // explicite intent
168168
intent.setAction("com.example.ACTION_FOUND");
169169
intent.putExtra("some.extra", value); // optional
170-
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, id, intent, PendingIntent.FLAG_UPDATE_CURRENT);
170+
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);
171171

172172
BluetoothLeScannerCompat scanner = BluetoothLeScannerCompat.getScanner();
173173
ScanSettings settings = new ScanSettings.Builder()
@@ -176,7 +176,7 @@ To use this feature:
176176
.build();
177177
List<ScanFilter> filters = new ArrayList<>();
178178
filters.add(new ScanFilter.Builder().setServiceUuid(mUuid).build());
179-
scanner.startScan(filters, settings, context, pendingIntent);
179+
scanner.startScan(filters, settings, context, pendingIntent, requestCode);
180180
```
181181

182182
Add your `MyRecever` to *AndroidManifest*, as the application context might have been released
@@ -185,18 +185,22 @@ and all broadcast receivers registered to it together with it.
185185
To stop scanning call:
186186

187187
```java
188-
// To stop scanning use the same or an equal PendingIntent (check PendingIntent documentation)
188+
// To stop scanning use the same PendingIntent and request code as one used to start scanning.
189189
Intent intent = new Intent(context, MyReceiver.class);
190190
intent.setAction("com.example.ACTION_FOUND");
191-
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, id, intent, PendingIntent.FLAG_CANCEL_CURRENT);
191+
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, requestCode, intent, PendingIntent.FLAG_CANCEL_CURRENT);
192192

193193
BluetoothLeScannerCompat scanner = BluetoothLeScannerCompat.getScanner();
194-
scanner.stopScan(context, pendingIntent);
194+
scanner.stopScan(context, pendingIntent, requestCode);
195195
```
196196

197197
**Note:** Android versions 6 and 7 will not report any advertising packets when in Doze mode.
198198
Read more about it here: https://developer.android.com/training/monitoring-device-state/doze-standby
199199

200+
**Note 2:** An additional parameter called `requestCode` was added in version 1.4.5 to the above API.
201+
It is to ensure that the scanning would be correctly stopped. If not provided, a request code equal
202+
to 0 will be used preventing from having multiple scanning tasks.
203+
200204
## Background scanning guidelines
201205

202206
To save power it is recommended to use as low power settings as possible and and use filters.

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
# org.gradle.parallel=true
1919
android.useAndroidX=true
2020

21-
VERSION_NAME=1.4.4
21+
VERSION_NAME=1.4.5
2222
GROUP=no.nordicsemi.android.support.v18
2323

2424
POM_DESCRIPTION=Android Bluetooth LE Scanner Compat library

scanner/build.gradle

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,29 +19,31 @@ android {
1919
testCoverageEnabled true
2020
}
2121
}
22+
compileOptions {
23+
sourceCompatibility JavaVersion.VERSION_1_7
24+
targetCompatibility JavaVersion.VERSION_1_7
25+
}
2226
}
2327

2428
dependencies {
2529
implementation 'androidx.annotation:annotation:1.2.0'
2630

27-
androidTestImplementation 'androidx.test:runner:1.4.0-beta01'
28-
androidTestImplementation 'androidx.test:rules:1.4.0-beta01'
29-
androidTestImplementation 'org.hamcrest:hamcrest-library:2.1'
30-
androidTestImplementation ('junit:junit:4.13.2') {
31-
exclude module: 'hamcrest-core'
32-
}
31+
// Core library
32+
androidTestImplementation 'androidx.test:core:1.3.0'
3333

34-
testImplementation 'org.hamcrest:hamcrest-library:2.1'
35-
testImplementation ('junit:junit:4.13.2') {
36-
exclude module: 'hamcrest-core'
37-
}
38-
testImplementation "org.mockito:mockito-core:2.18.0"
39-
testImplementation "org.powermock:powermock-module-junit4:1.7.4"
40-
testImplementation "org.powermock:powermock-api-mockito:1.7.4"
34+
// AndroidJUnitRunner
35+
androidTestImplementation 'androidx.test:runner:1.3.0'
36+
37+
// Assertions & AndroidJUnit4
38+
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
39+
40+
// Truth for Unit Testing
41+
androidTestImplementation "com.google.truth:truth:1.1.3"
42+
testImplementation "com.google.truth:truth:1.1.3"
4143
}
4244

4345
// === Maven Central configuration ===
44-
// The following file exists only when Android BLE Library project is opened, but not
46+
// The following file exists only when Android Scanner Compat Library project is opened, but not
4547
// when the module is loaded to a different project.
4648
if (rootProject.file('gradle/publish-module.gradle').exists()) {
4749
ext {

scanner/src/androidTest/java/no/nordicsemi/android/support/v18/scanner/BluetoothLeScannerImplOreoTest.java

Lines changed: 52 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -2,78 +2,74 @@
22

33
import android.bluetooth.BluetoothAdapter;
44
import android.bluetooth.BluetoothDevice;
5-
import android.os.ParcelUuid;
6-
import android.util.SparseArray;
75

86
import org.junit.Test;
97

10-
import java.lang.reflect.Constructor;
11-
import java.util.ArrayList;
12-
import java.util.HashMap;
13-
import java.util.List;
14-
import java.util.Map;
8+
import java.lang.reflect.InvocationTargetException;
9+
import java.lang.reflect.Method;
1510

16-
import static org.junit.Assert.assertArrayEquals;
17-
import static org.junit.Assert.assertEquals;
18-
import static org.junit.Assert.assertNotNull;
19-
import static org.junit.Assert.assertSame;
20-
import static org.junit.Assert.fail;
11+
import static com.google.common.truth.Truth.assertThat;
2112

2213
public class BluetoothLeScannerImplOreoTest {
2314

2415
@Test
2516
public void toImpl() {
2617
// Build mock data
27-
List<ParcelUuid> serviceUuids = new ArrayList<>();
28-
serviceUuids.add(ParcelUuid.fromString("00001809-0000-1000-8000-00805F9B34FB"));
18+
final byte[] bytes = new byte[]{
19+
2, 1, 6, // Flags
20+
5, 8, 'T', 'e', 's', 't', // Shortened Local Name (Test)
21+
6, (byte) 0xFF, 0x59, 0x00, 1, 2, 3, // Manufacturer Data (Nordic Semi -> 0x010203)
22+
3, 0x16, 0x09, 0x18, // Service Data - 16-bit UUID (0x1809)
23+
2, 0x0A, 1 // Tx Power Level (1 dBm)
24+
};
2925

30-
SparseArray<byte[]> manufacturerData = new SparseArray<>();
31-
manufacturerData.append(0x0059, new byte[] { 1, 2, 3});
26+
final BluetoothDevice device =
27+
BluetoothAdapter.getDefaultAdapter().getRemoteDevice("01:02:03:04:05:06");
3228

33-
Map<ParcelUuid, byte[]> serviceData = new HashMap<>();
34-
serviceData.put(ParcelUuid.fromString("00001809-0000-1000-8000-00805F9B34FB"), new byte[] { 0x64 });
29+
final android.bluetooth.le.ScanRecord _record = parseScanRecord(bytes);
3530

36-
final byte[] bytes = new byte[] { 2, 1, 6, 5, 8, 'T', 'e', 's', 't', 6, (byte) 0xFF, 0x59, 0x00, 1, 2, 3, 4, 0x16, 0x09, 0x18, 0x64, 2, 0x0A, 1};
31+
android.bluetooth.le.ScanResult _result = new android.bluetooth.le.ScanResult(device,
32+
0b000001, 1, 2, 0,
33+
android.bluetooth.le.ScanResult.TX_POWER_NOT_PRESENT, -70,
34+
android.bluetooth.le.ScanResult.PERIODIC_INTERVAL_NOT_PRESENT, _record,
35+
123456789L);
3736

38-
try {
39-
BluetoothDevice device =
40-
BluetoothAdapter.getDefaultAdapter().getRemoteDevice("01:02:03:04:05:06");
41-
42-
final Constructor constructor =
43-
android.bluetooth.le.ScanRecord.class.getDeclaredConstructor(List.class,
44-
SparseArray.class, Map.class, int.class, int.class, String.class, byte[].class);
45-
constructor.setAccessible(true);
46-
final android.bluetooth.le.ScanRecord _record = (android.bluetooth.le.ScanRecord)
47-
constructor.newInstance(serviceUuids, manufacturerData, serviceData, 0x06, 1, "Test", bytes);
48-
49-
android.bluetooth.le.ScanResult _result = new android.bluetooth.le.ScanResult(device,
50-
0b000001, 1, 2, 0,
51-
android.bluetooth.le.ScanResult.TX_POWER_NOT_PRESENT, -70,
52-
android.bluetooth.le.ScanResult.PERIODIC_INTERVAL_NOT_PRESENT, _record,
53-
123456789L);
37+
// Convert to support.v18.ScanResult
38+
final BluetoothLeScannerImplOreo impl = new BluetoothLeScannerImplOreo();
39+
final ScanResult result = impl.fromNativeScanResult(_result);
5440

55-
// Convert to support.v18.ScanResult
56-
final BluetoothLeScannerImplOreo impl = new BluetoothLeScannerImplOreo();
57-
final ScanResult result = impl.fromNativeScanResult(_result);
41+
// Validate
42+
assertThat(result).isNotNull();
43+
assertThat(_record).isNotNull();
44+
assertThat(_result.isLegacy()).isEqualTo(result.isLegacy());
45+
assertThat(_result.isConnectable()).isEqualTo(result.isConnectable());
46+
assertThat(result.getDataStatus()).isEqualTo(ScanResult.DATA_COMPLETE);
47+
assertThat(result.getScanRecord()).isNotNull();
48+
final ScanRecord record = result.getScanRecord();
49+
assertThat(record.getAdvertiseFlags()).isEqualTo(6);
50+
assertThat(bytes).isEqualTo(record.getBytes());
51+
assertThat(record.getManufacturerSpecificData(0x0059)).isNotNull();
52+
assertThat(_record.getManufacturerSpecificData(0x0059))
53+
.isEqualTo(record.getManufacturerSpecificData(0x0059));
54+
assertThat(result.getPeriodicAdvertisingInterval())
55+
.isEqualTo(ScanResult.PERIODIC_INTERVAL_NOT_PRESENT);
56+
assertThat(result.getTxPower()).isEqualTo(ScanResult.TX_POWER_NOT_PRESENT);
57+
assertThat(result.getTimestampNanos()).isEqualTo(123456789L);
58+
assertThat(_result.getDevice()).isEqualTo(result.getDevice());
59+
assertThat(device).isEqualTo(result.getDevice());
60+
}
5861

59-
// Validate
60-
assertEquals(_result.isLegacy(), result.isLegacy());
61-
assertEquals(_result.isConnectable(), result.isConnectable());
62-
assertEquals(ScanResult.DATA_COMPLETE, result.getDataStatus());
63-
assertNotNull(result.getScanRecord());
64-
final ScanRecord record = result.getScanRecord();
65-
assertEquals(6, record.getAdvertiseFlags());
66-
assertArrayEquals(bytes, record.getBytes());
67-
assertNotNull(record.getManufacturerSpecificData(0x0059));
68-
assertArrayEquals(_record.getManufacturerSpecificData(0x0059),
69-
record.getManufacturerSpecificData(0x0059));
70-
assertEquals(ScanResult.PERIODIC_INTERVAL_NOT_PRESENT, result.getPeriodicAdvertisingInterval());
71-
assertEquals(ScanResult.TX_POWER_NOT_PRESENT, result.getTxPower());
72-
assertEquals(123456789L, result.getTimestampNanos());
73-
assertSame(_result.getDevice(), result.getDevice());
74-
assertSame(device, result.getDevice());
75-
} catch (Exception e) {
76-
fail(e.getMessage());
62+
/**
63+
* Utility method to call hidden ScanRecord.parseFromBytes method.
64+
*/
65+
static android.bluetooth.le.ScanRecord parseScanRecord(byte[] bytes) {
66+
final Class<?> scanRecordClass = android.bluetooth.le.ScanRecord.class;
67+
try {
68+
final Method method = scanRecordClass.getDeclaredMethod("parseFromBytes", byte[].class);
69+
return (android.bluetooth.le.ScanRecord) method.invoke(null, bytes);
70+
} catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException
71+
| InvocationTargetException e) {
72+
return null;
7773
}
7874
}
7975
}

scanner/src/androidTest/java/no/nordicsemi/android/support/v18/scanner/BluetoothUuidTest.java

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,28 +17,30 @@
1717
package no.nordicsemi.android.support.v18.scanner;
1818

1919
import android.os.ParcelUuid;
20+
2021
import org.junit.Test;
2122

22-
import static junit.framework.Assert.assertEquals;
23+
import static com.google.common.truth.Truth.assertThat;
2324

2425
public class BluetoothUuidTest {
2526

26-
@Test public void testUuidParser() {
27-
byte[] uuid16 = new byte[] {
28-
0x0B, 0x11
29-
};
30-
assertEquals(ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"),
31-
BluetoothUuid.parseUuidFrom(uuid16));
32-
byte[] uuid32 = new byte[] {
33-
0x0B, 0x11, 0x33, (byte) 0xFE
34-
};
35-
assertEquals(ParcelUuid.fromString("FE33110B-0000-1000-8000-00805F9B34FB"),
36-
BluetoothUuid.parseUuidFrom(uuid32));
37-
byte[] uuid128 = new byte[] {
38-
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
39-
(byte) 0xFF
40-
};
41-
assertEquals(ParcelUuid.fromString("FF0F0E0D-0C0B-0A09-0807-0060504030201"),
42-
BluetoothUuid.parseUuidFrom(uuid128));
43-
}
27+
@Test
28+
public void testUuidParser() {
29+
final byte[] uuid16 = new byte[]{
30+
0x0B, 0x11
31+
};
32+
assertThat(BluetoothUuid.parseUuidFrom(uuid16))
33+
.isEqualTo(ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"));
34+
final byte[] uuid32 = new byte[]{
35+
0x0B, 0x11, 0x33, (byte) 0xFE
36+
};
37+
assertThat(BluetoothUuid.parseUuidFrom(uuid32))
38+
.isEqualTo(ParcelUuid.fromString("FE33110B-0000-1000-8000-00805F9B34FB"));
39+
final byte[] uuid128 = new byte[]{
40+
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
41+
(byte) 0xFF
42+
};
43+
assertThat(BluetoothUuid.parseUuidFrom(uuid128))
44+
.isEqualTo(ParcelUuid.fromString("FF0F0E0D-0C0B-0A09-0807-0060504030201"));
45+
}
4446
}

0 commit comments

Comments
 (0)