Skip to content

Commit cd137c1

Browse files
authored
Merge pull request #100 from NordicSemiconductor/develop
Version 1.5.0
2 parents 7b35496 + b838fff commit cd137c1

12 files changed

+123
-142
lines changed

README.md

+6-1
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.5'
31+
implementation 'no.nordicsemi.android.support.v18:scanner:1.5.0'
3232
```
3333

3434
Projects not migrated to Android Jetpack should use version 1.3.1, which is feature-equal to 1.4.0.
@@ -48,6 +48,11 @@ allprojects {
4848
}
4949
```
5050

51+
Since version 1.5 you will need to [enable desugaring of Java 8 language features](https://developer.android.com/studio/write/java8-support.html#supported_features)
52+
if you have not already done so.(And if you are releasing an Android library, then anyone who uses
53+
that library will also have to enable desugaring.) We expect for nearly all Android projects to have
54+
already enabled desugaring. But if this causes problems for you, please use version 1.4.5.
55+
5156
## API
5257

5358
The Scanner Compat API is very similar to the original one, known from Android Oreo.

gradle.properties

+1-1
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.5
21+
VERSION_NAME=1.5.0
2222
GROUP=no.nordicsemi.android.support.v18
2323

2424
POM_DESCRIPTION=Android Bluetooth LE Scanner Compat library

scanner/build.gradle

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ android {
2020
}
2121
}
2222
compileOptions {
23-
sourceCompatibility JavaVersion.VERSION_1_7
24-
targetCompatibility JavaVersion.VERSION_1_7
23+
sourceCompatibility JavaVersion.VERSION_1_8
24+
targetCompatibility JavaVersion.VERSION_1_8
2525
}
2626
}
2727

scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/BluetoothLeScannerCompat.java

+15-22
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
*
6464
* @see ScanFilter
6565
*/
66-
@SuppressWarnings("WeakerAccess")
66+
@SuppressWarnings({"WeakerAccess", "unused"})
6767
public abstract class BluetoothLeScannerCompat {
6868

6969
/**
@@ -129,7 +129,7 @@ public final void startScan(@NonNull final ScanCallback callback) {
129129
throw new IllegalArgumentException("callback is null");
130130
}
131131
final Handler handler = new Handler(Looper.getMainLooper());
132-
startScanInternal(Collections.<ScanFilter>emptyList(), new ScanSettings.Builder().build(),
132+
startScanInternal(Collections.emptyList(), new ScanSettings.Builder().build(),
133133
callback, handler);
134134
}
135135

@@ -156,7 +156,7 @@ public final void startScan(@Nullable final List<ScanFilter> filters,
156156
throw new IllegalArgumentException("callback is null");
157157
}
158158
final Handler handler = new Handler(Looper.getMainLooper());
159-
startScanInternal(filters != null ? filters : Collections.<ScanFilter>emptyList(),
159+
startScanInternal(filters != null ? filters : Collections.emptyList(),
160160
settings != null ? settings : new ScanSettings.Builder().build(),
161161
callback, handler);
162162
}
@@ -185,7 +185,7 @@ public final void startScan(@Nullable final List<ScanFilter> filters,
185185
if (callback == null) {
186186
throw new IllegalArgumentException("callback is null");
187187
}
188-
startScanInternal(filters != null ? filters : Collections.<ScanFilter>emptyList(),
188+
startScanInternal(filters != null ? filters : Collections.emptyList(),
189189
settings != null ? settings : new ScanSettings.Builder().build(),
190190
callback, handler != null ? handler : new Handler(Looper.getMainLooper()));
191191
}
@@ -278,7 +278,7 @@ public final void startScan(@Nullable final List<ScanFilter> filters,
278278
if (context == null) {
279279
throw new IllegalArgumentException("context is null");
280280
}
281-
startScanInternal(filters != null ? filters : Collections.<ScanFilter>emptyList(),
281+
startScanInternal(filters != null ? filters : Collections.emptyList(),
282282
settings != null ? settings : new ScanSettings.Builder().build(),
283283
context, callbackIntent, requestCode);
284284
}
@@ -424,17 +424,6 @@ public final void stopScan(@NonNull final Context context,
424424
/** A collection of scan result of devices in range. */
425425
@NonNull private final Map<String, ScanResult> devicesInRange = new HashMap<>();
426426

427-
@NonNull
428-
private final Runnable flushPendingScanResultsTask = new Runnable() {
429-
@Override
430-
public void run() {
431-
if (!scanningStopped) {
432-
flushPendingScanResults();
433-
handler.postDelayed(this, scanSettings.getReportDelayMillis());
434-
}
435-
}
436-
};
437-
438427
/** A task, called periodically, that notifies about match lost. */
439428
@NonNull
440429
private final Runnable matchLostNotifierTask = new Runnable() {
@@ -448,12 +437,7 @@ public void run() {
448437
final ScanResult result = iterator.next();
449438
if (result.getTimestampNanos() < now - scanSettings.getMatchLostDeviceTimeout()) {
450439
iterator.remove();
451-
handler.post(new Runnable() {
452-
@Override
453-
public void run() {
454-
scanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_MATCH_LOST, result);
455-
}
456-
});
440+
handler.post(() -> scanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_MATCH_LOST, result));
457441
}
458442
}
459443

@@ -488,6 +472,15 @@ public void run() {
488472
final long delay = settings.getReportDelayMillis();
489473
emulateBatching = delay > 0 && (!offloadedBatchingSupported || !settings.getUseHardwareBatchingIfSupported());
490474
if (emulateBatching) {
475+
final Runnable flushPendingScanResultsTask = new Runnable() {
476+
@Override
477+
public void run() {
478+
if (!scanningStopped) {
479+
flushPendingScanResults();
480+
handler.postDelayed(this, scanSettings.getReportDelayMillis());
481+
}
482+
}
483+
};
491484
handler.postDelayed(flushPendingScanResultsTask, delay);
492485
}
493486
}

scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/BluetoothLeScannerImplJB.java

+8-17
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
import android.Manifest;
2626
import android.app.PendingIntent;
2727
import android.bluetooth.BluetoothAdapter;
28-
import android.bluetooth.BluetoothDevice;
2928
import android.content.Context;
3029
import android.content.Intent;
3130
import android.os.Handler;
@@ -234,22 +233,14 @@ private void setPowerSaveSettings() {
234233
}
235234
}
236235

237-
private final BluetoothAdapter.LeScanCallback scanCallback = new BluetoothAdapter.LeScanCallback() {
238-
@Override
239-
public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {
240-
final ScanResult scanResult = new ScanResult(device, ScanRecord.parseFromBytes(scanRecord),
241-
rssi, SystemClock.elapsedRealtimeNanos());
242-
243-
synchronized (wrappers) {
244-
final Collection<ScanCallbackWrapper> scanCallbackWrappers = wrappers.values();
245-
for (final ScanCallbackWrapper wrapper : scanCallbackWrappers) {
246-
wrapper.handler.post(new Runnable() {
247-
@Override
248-
public void run() {
249-
wrapper.handleScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult);
250-
}
251-
});
252-
}
236+
private final BluetoothAdapter.LeScanCallback scanCallback = (device, rssi, scanRecord) -> {
237+
final ScanResult scanResult = new ScanResult(device, ScanRecord.parseFromBytes(scanRecord),
238+
rssi, SystemClock.elapsedRealtimeNanos());
239+
240+
synchronized (wrappers) {
241+
final Collection<ScanCallbackWrapper> scanCallbackWrappers = wrappers.values();
242+
for (final ScanCallbackWrapper wrapper : scanCallbackWrappers) {
243+
wrapper.handler.post(() -> wrapper.handleScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult));
253244
}
254245
}
255246
};

scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/BluetoothLeScannerImplLollipop.java

+44-54
Original file line numberDiff line numberDiff line change
@@ -249,73 +249,63 @@ private ScanCallbackWrapperLollipop(final boolean offloadedBatchingSupported,
249249

250250
@Override
251251
public void onScanResult(final int callbackType, final android.bluetooth.le.ScanResult nativeScanResult) {
252-
handler.post(new Runnable() {
253-
@Override
254-
public void run() {
255-
final BluetoothLeScannerImplLollipop scannerImpl =
256-
(BluetoothLeScannerImplLollipop) BluetoothLeScannerCompat.getScanner();
257-
final ScanResult result = scannerImpl.fromNativeScanResult(nativeScanResult);
258-
handleScanResult(callbackType, result);
259-
}
252+
handler.post(() -> {
253+
final BluetoothLeScannerImplLollipop scannerImpl =
254+
(BluetoothLeScannerImplLollipop) BluetoothLeScannerCompat.getScanner();
255+
final ScanResult result = scannerImpl.fromNativeScanResult(nativeScanResult);
256+
handleScanResult(callbackType, result);
260257
});
261258
}
262259

263260
@Override
264261
public void onBatchScanResults(final List<android.bluetooth.le.ScanResult> nativeScanResults) {
265-
handler.post(new Runnable() {
266-
@Override
267-
public void run() {
268-
// On several phones the onBatchScanResults is called twice for every batch.
269-
// Skip the second call if came to early.
270-
final long now = SystemClock.elapsedRealtime();
271-
if (lastBatchTimestamp > now - scanSettings.getReportDelayMillis() + 5) {
272-
return;
273-
}
274-
lastBatchTimestamp = now;
275-
276-
final BluetoothLeScannerImplLollipop scannerImpl =
277-
(BluetoothLeScannerImplLollipop) BluetoothLeScannerCompat.getScanner();
278-
final List<ScanResult> results = scannerImpl.fromNativeScanResults(nativeScanResults);
279-
handleScanResults(results);
262+
handler.post(() -> {
263+
// On several phones the onBatchScanResults is called twice for every batch.
264+
// Skip the second call if came to early.
265+
final long now = SystemClock.elapsedRealtime();
266+
if (lastBatchTimestamp > now - scanSettings.getReportDelayMillis() + 5) {
267+
return;
280268
}
269+
lastBatchTimestamp = now;
270+
271+
final BluetoothLeScannerImplLollipop scannerImpl =
272+
(BluetoothLeScannerImplLollipop) BluetoothLeScannerCompat.getScanner();
273+
final List<ScanResult> results = scannerImpl.fromNativeScanResults(nativeScanResults);
274+
handleScanResults(results);
281275
});
282276
}
283277

284278
@Override
285279
public void onScanFailed(final int errorCode) {
286-
handler.post(new Runnable() {
287-
@Override
288-
@RequiresPermission(allOf = {Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.BLUETOOTH})
289-
public void run() {
290-
// We were able to determine offloaded batching and filtering before we started scan,
291-
// but there is no method checking if callback types FIRST_MATCH and MATCH_LOST
292-
// are supported. We get an error here it they are not.
293-
if (scanSettings.getUseHardwareCallbackTypesIfSupported()
294-
&& scanSettings.getCallbackType() != ScanSettings.CALLBACK_TYPE_ALL_MATCHES) {
295-
// On Nexus 6 with Android 6.0 (MPA44G, M Pre-release 3) the errorCode = 5 (SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES)
296-
// On Pixel 2 with Android 9.0 the errorCode = 4 (SCAN_FAILED_FEATURE_UNSUPPORTED)
297-
298-
// This feature seems to be not supported on your phone.
299-
// Let's try to do pretty much the same in the code.
300-
scanSettings.disableUseHardwareCallbackTypes();
301-
302-
final BluetoothLeScannerCompat scanner = BluetoothLeScannerCompat.getScanner();
303-
try {
304-
scanner.stopScan(scanCallback);
305-
} catch (final Exception e) {
306-
// Ignore
307-
}
308-
try {
309-
scanner.startScanInternal(filters, scanSettings, scanCallback, handler);
310-
} catch (final Exception e) {
311-
// Ignore
312-
}
313-
return;
280+
handler.post(() -> {
281+
// We were able to determine offloaded batching and filtering before we started scan,
282+
// but there is no method checking if callback types FIRST_MATCH and MATCH_LOST
283+
// are supported. We get an error here it they are not.
284+
if (scanSettings.getUseHardwareCallbackTypesIfSupported()
285+
&& scanSettings.getCallbackType() != ScanSettings.CALLBACK_TYPE_ALL_MATCHES) {
286+
// On Nexus 6 with Android 6.0 (MPA44G, M Pre-release 3) the errorCode = 5 (SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES)
287+
// On Pixel 2 with Android 9.0 the errorCode = 4 (SCAN_FAILED_FEATURE_UNSUPPORTED)
288+
289+
// This feature seems to be not supported on your phone.
290+
// Let's try to do pretty much the same in the code.
291+
scanSettings.disableUseHardwareCallbackTypes();
292+
293+
final BluetoothLeScannerCompat scanner = BluetoothLeScannerCompat.getScanner();
294+
try {
295+
scanner.stopScan(scanCallback);
296+
} catch (final Exception e) {
297+
// Ignore
314298
}
315-
316-
// else, notify user application
317-
handleScanError(errorCode);
299+
try {
300+
scanner.startScanInternal(filters, scanSettings, scanCallback, handler);
301+
} catch (final Exception e) {
302+
// Ignore
303+
}
304+
return;
318305
}
306+
307+
// else, notify user application
308+
handleScanError(errorCode);
319309
});
320310
}
321311
};

scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/BluetoothLeScannerImplOreo.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@
9595
throw new IllegalStateException("BT le scanner not available");
9696

9797
final ScanSettings nonNullSettings = settings != null ? settings : new ScanSettings.Builder().build();
98-
final List<ScanFilter> nonNullFilters = filters != null ? filters : Collections.<ScanFilter>emptyList();
98+
final List<ScanFilter> nonNullFilters = filters != null ? filters : Collections.emptyList();
9999

100100
final android.bluetooth.le.ScanSettings nativeSettings = toNativeScanSettings(adapter, nonNullSettings, false);
101101
List<android.bluetooth.le.ScanFilter> nativeFilters = null;

scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/BluetoothLeUtils.java

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ static <T> String toString(@Nullable final Map<T, byte[]> map) {
6767
while (it.hasNext()) {
6868
final Map.Entry<T, byte[]> entry = it.next();
6969
final Object key = entry.getKey();
70+
//noinspection SuspiciousMethodCalls
7071
buffer.append(key).append("=").append(Arrays.toString(map.get(key)));
7172
if (it.hasNext()) {
7273
buffer.append(", ");

scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/PendingIntentExecutor.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
@Nullable private Context service;
2828

2929
private long lastBatchTimestamp;
30-
private long reportDelay;
30+
private final long reportDelay;
3131

3232
/**
3333
* Creates the {@link PendingIntent} executor that will be used from a

scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/ScanResult.java

+21-26
Original file line numberDiff line numberDiff line change
@@ -79,26 +79,25 @@ public final class ScanResult implements Parcelable {
7979
static final int ET_CONNECTABLE_MASK = 0x01;
8080

8181
// Remote Bluetooth device.
82-
@SuppressWarnings("NullableProblems")
8382
@NonNull
84-
private BluetoothDevice device;
83+
private final BluetoothDevice device;
8584

8685
// Scan record, including advertising data and scan response data.
8786
@Nullable
8887
private ScanRecord scanRecord;
8988

9089
// Received signal strength.
91-
private int rssi;
90+
private final int rssi;
9291

9392
// Device timestamp when the result was last seen.
94-
private long timestampNanos;
93+
private final long timestampNanos;
9594

96-
private int eventType;
97-
private int primaryPhy;
98-
private int secondaryPhy;
99-
private int advertisingSid;
100-
private int txPower;
101-
private int periodicAdvertisingInterval;
95+
private final int eventType;
96+
private final int primaryPhy;
97+
private final int secondaryPhy;
98+
private final int advertisingSid;
99+
private final int txPower;
100+
private final int periodicAdvertisingInterval;
102101

103102
/**
104103
* Constructs a new ScanResult.
@@ -155,7 +154,18 @@ public ScanResult(@NonNull final BluetoothDevice device, final int eventType,
155154
}
156155

157156
private ScanResult(final Parcel in) {
158-
readFromParcel(in);
157+
device = BluetoothDevice.CREATOR.createFromParcel(in);
158+
if (in.readInt() == 1) {
159+
scanRecord = ScanRecord.parseFromBytes(in.createByteArray());
160+
}
161+
rssi = in.readInt();
162+
timestampNanos = in.readLong();
163+
eventType = in.readInt();
164+
primaryPhy = in.readInt();
165+
secondaryPhy = in.readInt();
166+
advertisingSid = in.readInt();
167+
txPower = in.readInt();
168+
periodicAdvertisingInterval = in.readInt();
159169
}
160170

161171
@Override
@@ -177,21 +187,6 @@ public void writeToParcel(final Parcel dest, final int flags) {
177187
dest.writeInt(periodicAdvertisingInterval);
178188
}
179189

180-
private void readFromParcel(final Parcel in) {
181-
device = BluetoothDevice.CREATOR.createFromParcel(in);
182-
if (in.readInt() == 1) {
183-
scanRecord = ScanRecord.parseFromBytes(in.createByteArray());
184-
}
185-
rssi = in.readInt();
186-
timestampNanos = in.readLong();
187-
eventType = in.readInt();
188-
primaryPhy = in.readInt();
189-
secondaryPhy = in.readInt();
190-
advertisingSid = in.readInt();
191-
txPower = in.readInt();
192-
periodicAdvertisingInterval = in.readInt();
193-
}
194-
195190
@Override
196191
public int describeContents() {
197192
return 0;

0 commit comments

Comments
 (0)