From d00ba14dad51ea407a8e087d1cfeef04f32ab58f Mon Sep 17 00:00:00 2001 From: Martijn van Welie Date: Mon, 21 Oct 2019 21:26:27 +0200 Subject: [PATCH] Update to SDK 29. Check for ACCESS_FINE_LOCATION for SDK 29 and higher but leave it to ACCESS_COARSE_LOCATION for lower ones. --- app/build.gradle | 14 +++--- .../ExampleInstrumentedTest.java | 4 +- app/src/main/AndroidManifest.xml | 1 + .../welie/blessedexample/MainActivity.java | 18 ++++--- app/src/main/res/layout/activity_main.xml | 4 +- blessed/build.gradle | 11 ++--- .../blessed/ExampleInstrumentedTest.java | 4 +- blessed/src/main/AndroidManifest.xml | 2 +- .../com/welie/blessed/BluetoothCentral.java | 15 ++++-- .../welie/blessed/BluetoothCentralTest.java | 47 ++++++++++++++++++- gradle.properties | 2 + 11 files changed, 91 insertions(+), 31 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index ea72400..7f63ba8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,14 +1,14 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 28 + compileSdkVersion 29 defaultConfig { applicationId "com.welie.blessedexample" minSdkVersion 21 - targetSdkVersion 28 + targetSdkVersion 29 versionCode 1 versionName "1.0" - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { release { @@ -20,11 +20,11 @@ android { dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation 'com.android.support:appcompat-v7:28.0.0' - implementation 'com.android.support.constraint:constraint-layout:1.1.3' + implementation 'androidx.appcompat:appcompat:1.1.0' + implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'com.jakewharton.timber:timber:4.7.1' testImplementation 'junit:junit:4.12' - androidTestImplementation 'com.android.support.test:runner:1.0.2' - androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' + androidTestImplementation 'androidx.test:runner:1.2.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' implementation project(':blessed') } diff --git a/app/src/androidTest/java/com/welie/blessedexample/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/welie/blessedexample/ExampleInstrumentedTest.java index ea2aa30..99a112d 100644 --- a/app/src/androidTest/java/com/welie/blessedexample/ExampleInstrumentedTest.java +++ b/app/src/androidTest/java/com/welie/blessedexample/ExampleInstrumentedTest.java @@ -1,8 +1,8 @@ package com.welie.blessedexample; import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index add51ea..9f3332c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,6 +4,7 @@ + = Build.VERSION_CODES.M) { + int targetSdkVersion = getApplicationInfo().targetSdkVersion; + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && targetSdkVersion >= Build.VERSION_CODES.Q) { + if (getApplicationContext().checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { + requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, ACCESS_LOCATION_REQUEST); + return false; + } + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (getApplicationContext().checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { - requestPermissions(new String[] { Manifest.permission.ACCESS_COARSE_LOCATION }, ACCESS_COARSE_LOCATION_REQUEST); + requestPermissions(new String[] { Manifest.permission.ACCESS_COARSE_LOCATION }, ACCESS_LOCATION_REQUEST); return false; } } @@ -86,7 +92,7 @@ private boolean hasPermissions() { @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode) { - case ACCESS_COARSE_LOCATION_REQUEST: + case ACCESS_LOCATION_REQUEST: if(grantResults.length > 0) { if(grantResults[0] == PackageManager.PERMISSION_GRANTED) { initBluetoothHandler(); diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index f260ee5..76e915d 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,5 +1,5 @@ - - \ No newline at end of file + \ No newline at end of file diff --git a/blessed/build.gradle b/blessed/build.gradle index 5857470..a66b999 100644 --- a/blessed/build.gradle +++ b/blessed/build.gradle @@ -1,15 +1,15 @@ apply plugin: 'com.android.library' android { - compileSdkVersion 28 + compileSdkVersion 29 defaultConfig { minSdkVersion 21 - targetSdkVersion 28 + targetSdkVersion 29 versionCode 1 versionName "1.0" - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } @@ -25,12 +25,11 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'com.jakewharton.timber:timber:4.7.1' testImplementation 'junit:junit:4.12' testImplementation "org.robolectric:robolectric:3.8" testImplementation "org.mockito:mockito-core:1.10.19" - androidTestImplementation 'com.android.support.test:runner:1.0.2' - androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' + androidTestImplementation 'androidx.test:runner:1.2.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' } diff --git a/blessed/src/androidTest/java/com/welie/blessed/ExampleInstrumentedTest.java b/blessed/src/androidTest/java/com/welie/blessed/ExampleInstrumentedTest.java index d511398..be68d67 100644 --- a/blessed/src/androidTest/java/com/welie/blessed/ExampleInstrumentedTest.java +++ b/blessed/src/androidTest/java/com/welie/blessed/ExampleInstrumentedTest.java @@ -1,8 +1,8 @@ package com.welie.blessed; import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/blessed/src/main/AndroidManifest.xml b/blessed/src/main/AndroidManifest.xml index 79c9a7f..bb6b2dc 100644 --- a/blessed/src/main/AndroidManifest.xml +++ b/blessed/src/main/AndroidManifest.xml @@ -3,6 +3,6 @@ - + diff --git a/blessed/src/main/java/com/welie/blessed/BluetoothCentral.java b/blessed/src/main/java/com/welie/blessed/BluetoothCentral.java index 535d3f9..baef34e 100644 --- a/blessed/src/main/java/com/welie/blessed/BluetoothCentral.java +++ b/blessed/src/main/java/com/welie/blessed/BluetoothCentral.java @@ -746,10 +746,17 @@ private boolean isBleEnabled() { } private boolean permissionsGranted() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M - && context.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { - Timber.e("no location permission, cannot scan"); - return false; + int targetSdkVersion = context.getApplicationInfo().targetSdkVersion; + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && targetSdkVersion >= Build.VERSION_CODES.Q) { + if(context.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { + Timber.e("no ACCESS_FINE_LOCATION permission, cannot scan"); + return false; + } else return true; + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if(context.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { + Timber.e("no ACCESS_COARSE_LOCATION permission, cannot scan"); + return false; + } else return true; } else { return true; } diff --git a/blessed/src/test/java/com/welie/blessed/BluetoothCentralTest.java b/blessed/src/test/java/com/welie/blessed/BluetoothCentralTest.java index b6e9f04..4e79106 100644 --- a/blessed/src/test/java/com/welie/blessed/BluetoothCentralTest.java +++ b/blessed/src/test/java/com/welie/blessed/BluetoothCentralTest.java @@ -23,6 +23,7 @@ import org.robolectric.annotation.Config; import org.robolectric.shadow.api.Shadow; import org.robolectric.shadows.ShadowApplication; +import org.robolectric.shadows.ShadowBluetoothAdapter; import org.robolectric.shadows.ShadowPackageManager; import java.lang.reflect.Field; @@ -43,6 +44,7 @@ import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -76,7 +78,6 @@ public void setUp() throws Exception { initMocks(this); application = ShadowApplication.getInstance(); - application.grantPermissions(Manifest.permission.ACCESS_COARSE_LOCATION); bluetoothAdapter = Shadow.extract(ShadowBluetoothLEAdapter.getDefaultAdapter()); bluetoothAdapter.setEnabled(true); @@ -94,6 +95,7 @@ public void setUp() throws Exception { @Test public void scanForPeripheralsTest() throws Exception { + application.grantPermissions(Manifest.permission.ACCESS_FINE_LOCATION); central.scanForPeripherals(); verify(scanner).startScan(anyList(), any(ScanSettings.class), any(ScanCallback.class)); @@ -120,6 +122,7 @@ public void scanForPeripheralsTest() throws Exception { @Test public void scanForPeripheralsWithServicesTest() throws Exception { + application.grantPermissions(Manifest.permission.ACCESS_FINE_LOCATION); UUID BLP_SERVICE_UUID = UUID.fromString("00001810-0000-1000-8000-00805f9b34fb"); central.scanForPeripheralsWithServices(new UUID[]{BLP_SERVICE_UUID}); @@ -162,6 +165,7 @@ public void scanForPeripheralsWithServicesTest() throws Exception { @Test public void scanForPeripheralsWithAddressesTest() throws Exception { + application.grantPermissions(Manifest.permission.ACCESS_FINE_LOCATION); String myAddress = "12:23:34:98:76:54"; central.scanForPeripheralsWithAddresses(new String[]{myAddress}); @@ -203,6 +207,7 @@ public void scanForPeripheralsWithAddressesTest() throws Exception { @Test public void scanForPeripheralsWithNamesTest() throws Exception { + application.grantPermissions(Manifest.permission.ACCESS_FINE_LOCATION); String myName = "Polar"; central.scanForPeripheralsWithNames(new String[]{myName}); @@ -239,6 +244,7 @@ public void scanForPeripheralsWithNamesTest() throws Exception { @Test public void scanFailedTest() throws Exception { + application.grantPermissions(Manifest.permission.ACCESS_FINE_LOCATION); central.scanForPeripherals(); verify(scanner).startScan(anyList(), any(ScanSettings.class), any(ScanCallback.class)); @@ -252,8 +258,21 @@ public void scanFailedTest() throws Exception { verify(callback).onScanFailed(SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES); } + @Test + public void scanFailedAutoconnectTest() throws Exception { + // Grab the scan callback that is used + Field field = BluetoothCentral.class.getDeclaredField("autoConnectScanCallback"); + field.setAccessible(true); + ScanCallback scanCallback = (ScanCallback) field.get(central); + + scanCallback.onScanFailed(SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES); + + verify(callback).onScanFailed(SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES); + } + @Test public void scanForNamesFailedTest() throws Exception { + application.grantPermissions(Manifest.permission.ACCESS_FINE_LOCATION); String myName = "Polar"; central.scanForPeripheralsWithNames(new String[]{myName}); @@ -269,6 +288,7 @@ public void scanForNamesFailedTest() throws Exception { @Test public void stopScanTest() throws Exception { + application.grantPermissions(Manifest.permission.ACCESS_FINE_LOCATION); central.scanForPeripherals(); verify(scanner).startScan(anyList(), any(ScanSettings.class), any(ScanCallback.class)); @@ -292,6 +312,7 @@ public void stopScanTest() throws Exception { @Test public void connectPeripheralTest() throws Exception { + application.grantPermissions(Manifest.permission.ACCESS_FINE_LOCATION); BluetoothPeripheral peripheral = mock(BluetoothPeripheral.class); when(peripheral.getAddress()).thenReturn("12:23:34:98:76:54"); when(peripheral.getType()).thenReturn(BluetoothDevice.DEVICE_TYPE_LE); @@ -313,6 +334,8 @@ public void connectPeripheralTest() throws Exception { @Test public void connectionFailedRetryTest() throws Exception { + application.grantPermissions(Manifest.permission.ACCESS_FINE_LOCATION); + BluetoothPeripheral peripheral = mock(BluetoothPeripheral.class); when(peripheral.getAddress()).thenReturn("12:23:34:98:76:54"); when(peripheral.getType()).thenReturn(BluetoothDevice.DEVICE_TYPE_LE); @@ -336,6 +359,8 @@ public void connectionFailedRetryTest() throws Exception { @Test public void connectionFailedAfterRetryTest() throws Exception { + application.grantPermissions(Manifest.permission.ACCESS_FINE_LOCATION); + BluetoothPeripheral peripheral = mock(BluetoothPeripheral.class); when(peripheral.getAddress()).thenReturn("12:23:34:98:76:54"); when(peripheral.getType()).thenReturn(BluetoothDevice.DEVICE_TYPE_LE); @@ -359,6 +384,8 @@ public void connectionFailedAfterRetryTest() throws Exception { @Test public void cancelConnectionPeripheralTest() throws Exception { + application.grantPermissions(Manifest.permission.ACCESS_FINE_LOCATION); + BluetoothPeripheral peripheral = mock(BluetoothPeripheral.class); when(peripheral.getAddress()).thenReturn("12:23:34:98:76:54"); when(peripheral.getType()).thenReturn(BluetoothDevice.DEVICE_TYPE_LE); @@ -388,6 +415,8 @@ public void cancelConnectionPeripheralTest() throws Exception { @Test public void autoconnectTestCached() throws Exception { + application.grantPermissions(Manifest.permission.ACCESS_FINE_LOCATION); + BluetoothPeripheral peripheral = mock(BluetoothPeripheral.class); when(peripheral.getAddress()).thenReturn("12:23:34:98:76:54"); when(peripheral.getType()).thenReturn(BluetoothDevice.DEVICE_TYPE_LE); @@ -409,6 +438,8 @@ public void autoconnectTestCached() throws Exception { @Test public void autoconnectTestUnCached() throws Exception { + application.grantPermissions(Manifest.permission.ACCESS_FINE_LOCATION); + BluetoothDevice device = mock(BluetoothDevice.class); when(device.getAddress()).thenReturn("12:23:34:98:76:54"); when(device.getType()).thenReturn(BluetoothDevice.DEVICE_TYPE_LE); @@ -434,4 +465,18 @@ public void autoconnectTestUnCached() throws Exception { verify(peripheral).connect(); } + + @Test + public void bluetoothOffTest() { + application.grantPermissions(Manifest.permission.ACCESS_FINE_LOCATION); + bluetoothAdapter.setEnabled(false); + central.scanForPeripherals(); + verify(scanner, never()).startScan(anyList(), any(ScanSettings.class), any(ScanCallback.class)); + } + + @Test + public void noPermissionTest() { + central.scanForPeripherals(); + verify(scanner, never()).startScan(anyList(), any(ScanSettings.class), any(ScanCallback.class)); + } } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 82618ce..d546dea 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,6 +6,8 @@ # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. +android.enableJetifier=true +android.useAndroidX=true org.gradle.jvmargs=-Xmx1536m # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit