Skip to content

Commit

Permalink
update to support Android12 or higher
Browse files Browse the repository at this point in the history
  • Loading branch information
Ficat committed Dec 13, 2024
1 parent b396d8f commit 7c69120
Show file tree
Hide file tree
Showing 11 changed files with 191 additions and 69 deletions.
4 changes: 2 additions & 2 deletions easyble/build.gradle
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
apply plugin: 'com.android.library'

android {
compileSdkVersion 28
compileSdkVersion 34



defaultConfig {
minSdkVersion 18
targetSdkVersion 28
targetSdkVersion 33
versionCode 1
versionName "1.0"

Expand Down
32 changes: 29 additions & 3 deletions easyble/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,33 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.ficat.easyble">

<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<!-- 1.Android11(api30) or lower -->
<!-- Request legacy Bluetooth permissions on older devices -->
<uses-permission
android:name="android.permission.BLUETOOTH"
android:maxSdkVersion="30" />
<uses-permission
android:name="android.permission.BLUETOOTH_ADMIN"
android:maxSdkVersion="30" />

<!-- 2.Android6 ~ Android11(api23~30) -->
<!-- Android6 ~ 9(api23 ~ 28): ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION -->
<!-- Android10 ~ 11(api29 ~ 30): ACCESS_FINE_LOCATION -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

<!-- 3.Android12(api31) or higher -->
<!-- BLUETOOTH_SCAN: Used to scan Bluetooth devices. -->
<!-- BLUETOOTH_CONNECT: Used to scan and connect Bluetooth devices. -->
<!-- BLUETOOTH_ADVERTISE: Needed only if your app makes the device discoverable to Bluetooth devices. -->
<uses-permission
android:name="android.permission.BLUETOOTH_SCAN"
android:usesPermissionFlags="neverForLocation"
tools:targetApi="31" />
<uses-permission
android:name="android.permission.BLUETOOTH_CONNECT"
tools:targetApi="31" />
<uses-permission
android:name="android.permission.BLUETOOTH_ADVERTISE"
tools:targetApi="31" />
</manifest>
14 changes: 11 additions & 3 deletions easyble/src/main/java/com/ficat/easyble/BleDevice.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,17 @@ public class BleDevice implements Parcelable {
BleDevice(BluetoothDevice device) {
this.device = device;
this.address = device.getAddress();
this.name = device.getName();
if (TextUtils.isEmpty(name)) {
name = "unknown";

//Android12(api31) or higher,Bluetooth#getName() needs 'BLUETOOTH_CONNECT' permission
try {
this.name = device.getName();
} catch (Exception e) {
Logger.e("Failed to call BluetoothDevice#getName() because of no 'BLUETOOTH_CONNECT' permission");
} finally {
//Device name got from BluetoothDevice#getName() may be null, so check it
if (TextUtils.isEmpty(this.name)){
this.name = "unknown";
}
}
}

Expand Down
106 changes: 79 additions & 27 deletions easyble/src/main/java/com/ficat/easyble/BleManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,20 @@ public void startScan(ScanOptions options, BleScanCallback callback) {
if (callback == null) {
throw new IllegalArgumentException("BleScanCallback is null");
}
if (!isBluetoothOn()) {
callback.onStart(false, "Bluetooth is not turned on");
return;
}
if (!isScanPermissionGranted(mContext)) {
String permission = Build.VERSION.SDK_INT > Build.VERSION_CODES.P ? "location(ACCESS_FINE_LOCATION)" :
"location(ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION)";
callback.onStart(false, "You must grant " + permission + " permission before scanning");
String permission;
if (Build.VERSION.SDK_INT >= 31) { //Android12
permission = "BLUETOOTH_SCAN and BLUETOOTH_CONNECT";
} else if (Build.VERSION.SDK_INT >= 29) {//Android10
permission = "ACCESS_FINE_LOCATION";
} else {//Android6
permission = "ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION";
}
callback.onStart(false, "No scan permission(" + permission + ")");
return;
}
if (options == null) {
Expand Down Expand Up @@ -153,6 +163,20 @@ public void connect(BleDevice device, BleConnectCallback callback) {
}

public void connect(BleDevice device, ConnectOptions options, BleConnectCallback callback) {
if (callback == null) {
throw new IllegalArgumentException("BleConnectCallback is null");
}
if (device == null) {
throw new IllegalArgumentException("BleDevice is null");
}
if (!isBluetoothOn()) {
callback.onStart(false, "Bluetooth is not turned on", device);
return;
}
if (!isConnectionPermissionGranted(mContext)) {
callback.onStart(false, "No connection permission(BLUETOOTH_CONNECT)", device);
return;
}
if (options == null) {
options = ConnectOptions.newInstance();
}
Expand All @@ -170,10 +194,6 @@ public void connect(String address, ConnectOptions options, BleConnectCallback c
checkBluetoothAddress(address);
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
BleDevice bleDevice = newBleDevice(device);
if (bleDevice == null) {
Logger.d("new BleDevice fail!");
return;
}
connect(bleDevice, options, callback);
}

Expand All @@ -195,10 +215,7 @@ public void disconnect(BleDevice device) {
* @param address remote device address
*/
public void disconnect(String address) {
if (!isAddressValid(address)) {
Logger.d("disconnect fail because of invalid address:" + address);
return;
}
checkBluetoothAddress(address);
mGatt.disconnect(address);
}

Expand Down Expand Up @@ -361,9 +378,7 @@ public List<BleDevice> getConnectedDevices() {
* @return true if local device has connected to the specific remote device
*/
public boolean isConnected(String address) {
if (!BluetoothAdapter.checkBluetoothAddress(address)) {
return false;
}
checkBluetoothAddress(address);
List<BleDevice> deviceList = getConnectedDevices();
for (BleDevice d : deviceList) {
if (address.equals(d.address)) {
Expand All @@ -377,9 +392,7 @@ public boolean isConnected(String address) {
* Return true if local device is connecting with the specific remote device
*/
public boolean isConnecting(String address) {
if (!BluetoothAdapter.checkBluetoothAddress(address)) {
return false;
}
checkBluetoothAddress(address);
return mGatt.isConnecting(address);
}

Expand Down Expand Up @@ -429,15 +442,28 @@ public static boolean supportBle(Context context) {

/**
* Turn on local bluetooth, calling the method will show users a request dialog
* to grant or reject,so you can get the result from Activity#onActivityResult()
* to grant or reject,so you can get the result from Activity#onActivityResult().
* <p>
* Note that if Android12(api31) or higher, only the permission
* {@link android.Manifest.permission#BLUETOOTH_CONNECT} has been granted by user,
* calling this method can work.
*
* @param activity activity, note that to get the result whether users have granted
* or rejected to enable bluetooth, you should handle the method
* onActivityResult() of this activity
* @param requestCode enable bluetooth request code
*/
public static void enableBluetooth(Activity activity, int requestCode) {
if (activity == null || requestCode < 0) {
if (activity == null) {
throw new IllegalArgumentException("Activity is null");
}
if (requestCode < 0) {
throw new IllegalArgumentException("Request code cannot be negative");
}
if (Build.VERSION.SDK_INT >= 31 && !PermissionChecker.isPermissionGranted(activity,
Manifest.permission.BLUETOOTH_CONNECT)) {
Logger.e("Android12 or higher, BleManager#enableBluetooth(Activity,int) needs the " +
"permission 'android.permission.BLUETOOTH_CONNECT'");
return;
}
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
Expand All @@ -449,7 +475,11 @@ public static void enableBluetooth(Activity activity, int requestCode) {

/**
* Turn on or off local bluetooth directly without showing users a request
* dialog.
* dialog. Like {@link #enableBluetooth(Activity, int)}, if current version
* is Android12(api31) or higher, before calling this method, make sure the
* permission {@link android.Manifest.permission#BLUETOOTH_CONNECT} has been
* granted by user.
* <p>
* Note that a request dialog may still show when you call this method, due to
* some special Android devices' system may have been modified by manufacturers
*
Expand All @@ -460,12 +490,18 @@ public static void toggleBluetooth(boolean enable) {
if (adapter == null) {
return;
}
if (enable) {
adapter.enable();
} else {
if (adapter.isEnabled()) {
adapter.disable();
try {
if (enable) {
adapter.enable();
} else {
if (adapter.isEnabled()) {
adapter.disable();
}
}
} catch (SecurityException e) {
Logger.e("Android12 or higher, BleManager#toggleBluetooth(boolean) needs the" +
" permission 'android.permission.BLUETOOTH_CONNECT'");
e.printStackTrace();
}
}

Expand Down Expand Up @@ -496,16 +532,32 @@ public static boolean isScanPermissionGranted(Context context) {
if (context == null) {
throw new IllegalArgumentException("Context is null");
}
if (Build.VERSION.SDK_INT >= 29 && getTargetVersion(context) >= 29) {
if (Build.VERSION.SDK_INT >= 31) { //Android12
//BLUETOOTH_SCAN: enable this central device to scan peripheral devices
//BLUETOOTH_CONNECT: used to get peripheral device name (BluetoothDevice#getName())
return PermissionChecker.isPermissionGranted(context, Manifest.permission.BLUETOOTH_SCAN) &&
PermissionChecker.isPermissionGranted(context, Manifest.permission.BLUETOOTH_CONNECT);
} else if (Build.VERSION.SDK_INT >= 29) {//Android10
return PermissionChecker.isPermissionGranted(context, Manifest.permission.ACCESS_FINE_LOCATION);
} else if (Build.VERSION.SDK_INT >= 23) {
} else if (Build.VERSION.SDK_INT >= 23) {//Android6
return PermissionChecker.isPermissionGranted(context, Manifest.permission.ACCESS_COARSE_LOCATION) ||
PermissionChecker.isPermissionGranted(context, Manifest.permission.ACCESS_FINE_LOCATION);
} else {
return true;
}
}

/**
* Check if connection-permission has been granted
*/
public static boolean isConnectionPermissionGranted(Context context) {
if (context == null) {
throw new IllegalArgumentException("Context is null");
}
//Android12(api31) or higher, BLUETOOTH_CONNECT permission is necessary
return Build.VERSION.SDK_INT < 31 || PermissionChecker.isPermissionGranted(context, Manifest.permission.BLUETOOTH_CONNECT);
}

public ScanOptions getScanOptions() {
return mScanOptions == null ? ScanOptions.newInstance() : mScanOptions;
}
Expand Down
4 changes: 2 additions & 2 deletions sample/build.gradle
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
apply plugin: 'com.android.application'

android {
compileSdkVersion 28
compileSdkVersion 34
defaultConfig {
applicationId "com.ficat.sample"
minSdkVersion 18
targetSdkVersion 28
targetSdkVersion 33
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
Expand Down
1 change: 1 addition & 0 deletions sample/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
android:theme="@style/Theme.AppCompat.Light.NoActionBar">
<activity
android:name=".MainActivity"
android:exported="true"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Expand Down
Loading

0 comments on commit 7c69120

Please sign in to comment.