├── .idea ├── .name ├── misc.xml ├── runConfigurations.xml ├── jarRepositories.xml └── codeStyles │ └── Project.xml ├── OkBle ├── .gitignore ├── consumer-rules.pro ├── OkBle.jar ├── src │ ├── main │ │ ├── java │ │ │ └── okble │ │ │ │ └── central │ │ │ │ └── client │ │ │ │ ├── WriteTypeOption.java │ │ │ │ ├── util │ │ │ │ ├── EventUtils.java │ │ │ │ ├── OkBleClientLog.java │ │ │ │ ├── OkBleEventFilter.java │ │ │ │ ├── BluetoothDeviceFilter.java │ │ │ │ ├── RequestUtils.java │ │ │ │ ├── AndFilter.java │ │ │ │ ├── EventFilterOfValues.java │ │ │ │ ├── UiExecutor.java │ │ │ │ ├── EventFilterOfTime.java │ │ │ │ ├── BluetoothUtils.java │ │ │ │ ├── ExecutorProxy.java │ │ │ │ ├── Hex.java │ │ │ │ ├── GattUtils.java │ │ │ │ └── EventFilters.java │ │ │ │ ├── GattState.java │ │ │ │ ├── NotificationType.java │ │ │ │ ├── ServicesState.java │ │ │ │ ├── ConnectionState.java │ │ │ │ ├── NotificationOption.java │ │ │ │ ├── OnCompleteListener.java │ │ │ │ ├── OnSuccessListener.java │ │ │ │ ├── OnDataReceivedListener.java │ │ │ │ ├── OnConnectionUpdatedListener.java │ │ │ │ ├── Source.java │ │ │ │ ├── OnFailedListener.java │ │ │ │ ├── ReconnectionOptions.java │ │ │ │ ├── OnConnectionStateChangedListener.java │ │ │ │ ├── Sink.java │ │ │ │ ├── PacketSender.java │ │ │ │ ├── exception │ │ │ │ ├── TaskCanceledException.java │ │ │ │ ├── GattOperationTimeoutException.java │ │ │ │ ├── GattStatusException.java │ │ │ │ ├── GattOperationFailedException.java │ │ │ │ ├── GattDisconnectedException.java │ │ │ │ └── OkBleException.java │ │ │ │ ├── PacketReceiver.java │ │ │ │ ├── DefaultServiceValidator.java │ │ │ │ ├── GattConnectionState.java │ │ │ │ ├── GattOperationStatus.java │ │ │ │ ├── executor │ │ │ │ ├── RefreshExecutor.java │ │ │ │ ├── RemoveBondExecutor.java │ │ │ │ ├── UpdateConnectionParameterExecutor.java │ │ │ │ ├── ConnectGattExecutor.java │ │ │ │ ├── CreateBondExecutor.java │ │ │ │ ├── DiscoverServicesExecutor.java │ │ │ │ ├── DisconnectionExecutor.java │ │ │ │ ├── RequestExecutor.java │ │ │ │ ├── ReadPhyExecutor.java │ │ │ │ ├── SetMtuExecutor.java │ │ │ │ ├── ReadRemoteRssiExecutor.java │ │ │ │ ├── SetConnectionPriorityExecutor.java │ │ │ │ ├── SetPreferredPhyExecutor.java │ │ │ │ ├── DisableNotificationExecutor.java │ │ │ │ ├── ReadCharacteristicExecutor.java │ │ │ │ ├── ReadDescriptorExecutor.java │ │ │ │ ├── EnableNotificationExecutor.java │ │ │ │ └── WriteDescriptorExecutor.java │ │ │ │ ├── request │ │ │ │ ├── CreateBondRequest.java │ │ │ │ ├── RefreshRequest.java │ │ │ │ ├── RemoveBondRequest.java │ │ │ │ ├── DisconnectionRequest.java │ │ │ │ ├── ReadPhyRequest.java │ │ │ │ ├── DiscoverServicesRequest.java │ │ │ │ ├── ReadRemoteRssiRequest.java │ │ │ │ ├── ConnectGattRequest.java │ │ │ │ ├── SetMtuRequest.java │ │ │ │ ├── SetConnectionPriorityRequest.java │ │ │ │ ├── DisableNotificationRequest.java │ │ │ │ ├── ReadCharacteristicRequest.java │ │ │ │ ├── Request.java │ │ │ │ ├── ReadDescriptorRequest.java │ │ │ │ ├── SetPreferredPhyRequest.java │ │ │ │ ├── EnableNotificationRequest.java │ │ │ │ ├── ConnectionRequest.java │ │ │ │ ├── UpdateConnectionParameterRequest.java │ │ │ │ └── FixedSizePacketSenderFactory.java │ │ │ │ ├── Mtu.java │ │ │ │ ├── Rssi.java │ │ │ │ ├── Requests.java │ │ │ │ ├── DefaultGattConnector.java │ │ │ │ ├── ServiceValidator.java │ │ │ │ ├── NotificationStatus.java │ │ │ │ ├── Phy.java │ │ │ │ ├── GattConnector.java │ │ │ │ ├── PhyOptions.java │ │ │ │ ├── DefaultConnectionOptions.java │ │ │ │ ├── WriteType.java │ │ │ │ ├── ConnectionOptions.java │ │ │ │ ├── ConnectionPriority.java │ │ │ │ ├── GattOperationResult.java │ │ │ │ ├── ConnectionParameter.java │ │ │ │ ├── DebugBluetoothGattCallback.java │ │ │ │ ├── RequestTask.java │ │ │ │ ├── Pipe.java │ │ │ │ ├── PhyMask.java │ │ │ │ ├── OkBleTask.java │ │ │ │ ├── ClientOperation.java │ │ │ │ ├── CharacteristicData.java │ │ │ │ ├── Priority.java │ │ │ │ └── DescriptorData.java │ │ └── AndroidManifest.xml │ ├── test │ │ └── java │ │ │ └── okble │ │ │ └── central │ │ │ └── client │ │ │ └── ExampleUnitTest.java │ └── androidTest │ │ └── java │ │ └── okble │ │ └── central │ │ └── client │ │ └── ExampleInstrumentedTest.java ├── proguard-rules.pro └── build.gradle ├── app ├── .gitignore ├── keys │ └── okble.jks ├── libs │ └── OkBle.jar ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── src │ ├── main │ │ ├── res │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── values │ │ │ │ ├── dimens.xml │ │ │ │ ├── colors.xml │ │ │ │ ├── uuid_item_dimens.xml │ │ │ │ ├── styles.xml │ │ │ │ ├── strings.xml │ │ │ │ └── en_easy_permissions_strings.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ ├── drawable │ │ │ │ ├── ic_home_black_24dp.xml │ │ │ │ ├── ic_dashboard_black_24dp.xml │ │ │ │ └── ic_notifications_black_24dp.xml │ │ │ ├── menu │ │ │ │ └── bottom_nav_menu.xml │ │ │ ├── layout │ │ │ │ ├── layout_list_empty_view.xml │ │ │ │ ├── layout_requests.xml │ │ │ │ ├── layout_scanning_view.xml │ │ │ │ ├── item_log_info.xml │ │ │ │ ├── fragment_about.xml │ │ │ │ ├── fragment_setting.xml │ │ │ │ ├── item_descriptor.xml │ │ │ │ ├── list_item_device.xml │ │ │ │ ├── item_service.xml │ │ │ │ ├── activity_main.xml │ │ │ │ ├── item_characteristic.xml │ │ │ │ ├── activity_device.xml │ │ │ │ ├── fragment_devices.xml │ │ │ │ └── activity_device2.xml │ │ │ ├── navigation │ │ │ │ └── mobile_navigation.xml │ │ │ └── drawable-v24 │ │ │ │ └── ic_launcher_foreground.xml │ │ ├── java │ │ │ └── okble │ │ │ │ └── demo │ │ │ │ ├── OkBleDemoApp.java │ │ │ │ ├── ui │ │ │ │ ├── setting │ │ │ │ │ ├── SettingViewModel.java │ │ │ │ │ └── SettingFragment.java │ │ │ │ ├── device2 │ │ │ │ │ ├── LogInfo.java │ │ │ │ │ ├── LogViewHolder.java │ │ │ │ │ ├── ServiceViewHolder.java │ │ │ │ │ ├── DescriptorViewHolder.java │ │ │ │ │ ├── GattViewHolder.java │ │ │ │ │ ├── CharacteristicViewHolder.java │ │ │ │ │ ├── DescriptorResolver.java │ │ │ │ │ └── ServiceResolver.java │ │ │ │ ├── about │ │ │ │ │ ├── AboutViewModel.java │ │ │ │ │ └── AboutFragment.java │ │ │ │ └── devices │ │ │ │ │ └── DevicesAdapter.java │ │ │ │ ├── util │ │ │ │ ├── OnePacketReceiverFactory.java │ │ │ │ └── UuidUtils.java │ │ │ │ └── MainActivity.java │ │ └── AndroidManifest.xml │ ├── test │ │ └── java │ │ │ └── okble │ │ │ └── demo │ │ │ └── ExampleUnitTest.java │ └── androidTest │ │ └── java │ │ └── okble │ │ └── demo │ │ ├── ExampleInstrumentedTest.java │ │ └── OKBleScannerTest.java ├── proguard-rules.pro ├── gradlew.bat └── build.gradle ├── OkBle.jar ├── settings.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── .gitignore └── gradlew.bat /.idea/.name: -------------------------------------------------------------------------------- 1 | OkBleDemo -------------------------------------------------------------------------------- /OkBle/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /OkBle/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /OkBle.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/okbean/OkBle/HEAD/OkBle.jar -------------------------------------------------------------------------------- /OkBle/OkBle.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/okbean/OkBle/HEAD/OkBle/OkBle.jar -------------------------------------------------------------------------------- /app/keys/okble.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/okbean/OkBle/HEAD/app/keys/okble.jks -------------------------------------------------------------------------------- /app/libs/OkBle.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/okbean/OkBle/HEAD/app/libs/OkBle.jar -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | include ':OkBle' 3 | 4 | rootProject.name = "OkBleDemo" 5 | 6 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/okbean/OkBle/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/okbean/OkBle/HEAD/app/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/okbean/OkBle/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/okbean/OkBle/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/okbean/OkBle/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/WriteTypeOption.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | enum WriteTypeOption { 4 | 5 | 6 | } 7 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/okbean/OkBle/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/okbean/OkBle/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/util/EventUtils.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.util; 2 | 3 | public final class EventUtils { 4 | } 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/okbean/OkBle/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/okbean/OkBle/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/util/OkBleClientLog.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.util; 2 | 3 | public class OkBleClientLog { 4 | } 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/okbean/OkBle/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/okbean/OkBle/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/okbean/OkBle/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/GattState.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | enum GattState { 4 | 5 | Connecting, Connected, Disconnected; 6 | 7 | } 8 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/NotificationType.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | public enum NotificationType { 4 | 5 | Notification,Indication 6 | } 7 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/ServicesState.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | enum ServicesState { 4 | 5 | Discovering, Discovered, Undiscovered; 6 | } 7 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/ConnectionState.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | public enum ConnectionState { 4 | 5 | Connecting, Connected, Disconnected; 6 | 7 | } 8 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/NotificationOption.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | public enum NotificationOption { 4 | Notification, 5 | Indication, 6 | Disabled; 7 | } 8 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/OnCompleteListener.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | public interface OnCompleteListener { 4 | 5 | void onComplete(OkBleTask task); 6 | } -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/OnSuccessListener.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | public interface OnSuccessListener { 4 | 5 | void onSuccess(OkBleTask task, TResult result); 6 | } -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/OnDataReceivedListener.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | public interface OnDataReceivedListener { 4 | 5 | void onDataReceived(OkBleClient client, CharacteristicData data); 6 | 7 | } 8 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/util/OkBleEventFilter.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.util; 2 | 3 | import okble.central.client.OkBleEvent; 4 | 5 | public interface OkBleEventFilter { 6 | 7 | boolean accept(OkBleEvent event); 8 | } 9 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/util/BluetoothDeviceFilter.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.util; 2 | 3 | 4 | import android.bluetooth.BluetoothDevice; 5 | 6 | interface BluetoothDeviceFilter { 7 | boolean accept(BluetoothDevice device); 8 | } 9 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/OnConnectionUpdatedListener.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | public interface OnConnectionUpdatedListener { 4 | 5 | void onConnectionUpdated(OkBleClient client, ConnectionParameter connectionParameter); 6 | } 7 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/Source.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | 5 | 6 | public interface Source { 7 | 8 | T read(long timeout, TimeUnit unit) throws InterruptedException; 9 | 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/okble/demo/OkBleDemoApp.java: -------------------------------------------------------------------------------- 1 | package okble.demo; 2 | 3 | import android.app.Application; 4 | 5 | public class OkBleDemoApp extends Application { 6 | 7 | @Override 8 | public void onCreate() { 9 | super.onCreate(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/OnFailedListener.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | import okble.central.client.exception.OkBleException; 4 | 5 | public interface OnFailedListener { 6 | 7 | void onFailed(OkBleTask task, OkBleException ex); 8 | } 9 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Dec 09 15:19:17 CST 2020 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip 7 | -------------------------------------------------------------------------------- /app/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Dec 15 23:52:58 CST 2020 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip 7 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/ReconnectionOptions.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | interface ReconnectionOptions { 4 | 5 | boolean reconnectOnDisconnection(); 6 | 7 | interface Factory{ 8 | ReconnectionOptions newReconnectionOptions(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/OnConnectionStateChangedListener.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | public interface OnConnectionStateChangedListener { 4 | 5 | void onConnectionStateChanged(final OkBleClient client, final ConnectionState newState, final ConnectionState lastState); 6 | 7 | } -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #6200EE 4 | #3700B3 5 | #03DAC5 6 | 7 | #FFFFFF 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/Sink.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | 4 | public interface Sink { 5 | 6 | boolean write(T value); 7 | 8 | 9 | final class SinkImpl implements Sink{ 10 | 11 | @Override 12 | public boolean write(T value) { 13 | return false; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/res/values/uuid_item_dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 16dp 3 | 14dp 4 | 14dp 5 | 14dp 6 | 14dp 7 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/PacketSender.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | 4 | public interface PacketSender { 5 | 6 | boolean hasNext(); 7 | byte[] nextPacket(); 8 | boolean isDeterminate(); 9 | int size(); 10 | 11 | void reset(); 12 | 13 | interface Factory{ 14 | PacketSender newPacketSender(); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_home_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/exception/TaskCanceledException.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.exception; 2 | 3 | public class TaskCanceledException extends OkBleException{ 4 | 5 | public TaskCanceledException() { 6 | super(TASK_CANCELED); 7 | } 8 | 9 | public TaskCanceledException(String message) { 10 | super(TASK_CANCELED, message); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/PacketReceiver.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | public interface PacketReceiver { 4 | 5 | boolean isFinished(); 6 | boolean isSuccess(); 7 | boolean isFailed(); 8 | T getResult(); 9 | Exception getError(); 10 | void onReceived(byte[] packet); 11 | 12 | interface Factory{ 13 | PacketReceiver newPacketReceiver(); 14 | } 15 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_dashboard_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/exception/GattOperationTimeoutException.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.exception; 2 | 3 | public class GattOperationTimeoutException extends OkBleException { 4 | 5 | public GattOperationTimeoutException() { 6 | super(GATT_OPERATION_TIMEOUT); 7 | } 8 | 9 | public GattOperationTimeoutException(String message) { 10 | super(GATT_OPERATION_TIMEOUT, message); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /app/src/test/java/okble/demo/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package okble.demo; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/DefaultServiceValidator.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | import android.bluetooth.BluetoothGattService; 4 | 5 | import java.util.List; 6 | 7 | final class DefaultServiceValidator implements ServiceValidator{ 8 | 9 | public DefaultServiceValidator(){ 10 | 11 | } 12 | 13 | @Override 14 | public boolean onValidateServices(OkBleClient client, List services) { 15 | return true; 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /OkBle/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/GattConnectionState.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | 4 | final class GattConnectionState { 5 | 6 | private final int value; 7 | 8 | private GattConnectionState(final int value){ 9 | this.value = value; 10 | } 11 | 12 | public static GattConnectionState of(final int value){ 13 | return new GattConnectionState(value); 14 | } 15 | 16 | public int value(){ 17 | return this.value; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/GattOperationStatus.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | 4 | final class GattOperationStatus { 5 | 6 | private final int value; 7 | 8 | private GattOperationStatus(final int value){ 9 | this.value = value; 10 | } 11 | 12 | public static GattOperationStatus of(final int value){ 13 | return new GattOperationStatus(value); 14 | } 15 | 16 | public int value(){ 17 | return this.value; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /OkBle/src/test/java/okble/central/client/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /app/src/main/res/menu/bottom_nav_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/executor/RefreshExecutor.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.executor; 2 | 3 | import okble.central.client.ClientOperation; 4 | import okble.central.client.OkBleTask0; 5 | import okble.central.client.request.Request; 6 | 7 | final class RefreshExecutor extends RequestExecutor { 8 | 9 | @Override 10 | public void executeRequest(ClientOperation client, OkBleTask0 task, Request request0) { 11 | throw new RuntimeException("refresh operation not supported now!"); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/executor/RemoveBondExecutor.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.executor; 2 | 3 | import okble.central.client.ClientOperation; 4 | import okble.central.client.OkBleTask0; 5 | import okble.central.client.request.Request; 6 | 7 | final class RemoveBondExecutor extends RequestExecutor { 8 | @Override 9 | public void executeRequest(ClientOperation client, OkBleTask0 task, Request request0) { 10 | throw new RuntimeException("remove bond request not supported now!"); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_notifications_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/request/CreateBondRequest.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.request; 2 | 3 | final class CreateBondRequest extends Request{ 4 | 5 | private CreateBondRequest(final Builder builder){ 6 | super(Type.CreateBondRequest); 7 | } 8 | 9 | public final static class Builder { 10 | public CreateBondRequest build() { 11 | return new CreateBondRequest(this); 12 | } 13 | } 14 | 15 | @Override 16 | public CreateBondRequest getThis() { 17 | return this; 18 | } 19 | } -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/request/RefreshRequest.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.request; 2 | 3 | 4 | public final class RefreshRequest extends Request{ 5 | 6 | private RefreshRequest(final Builder builder){ 7 | super(Type.RefreshRequest); 8 | } 9 | 10 | public final static class Builder { 11 | public RefreshRequest build() { 12 | return new RefreshRequest(this); 13 | } 14 | } 15 | 16 | @Override 17 | public RefreshRequest getThis() { 18 | return this; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/request/RemoveBondRequest.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.request; 2 | 3 | 4 | final class RemoveBondRequest extends Request{ 5 | 6 | private RemoveBondRequest(final Builder builder){ 7 | super(Type.RemoveBondRequest); 8 | } 9 | 10 | public final static class Builder { 11 | public RemoveBondRequest build() { 12 | return new RemoveBondRequest(this); 13 | } 14 | } 15 | 16 | @Override 17 | public RemoveBondRequest getThis() { 18 | return this; 19 | } 20 | } -------------------------------------------------------------------------------- /app/src/main/java/okble/demo/ui/setting/SettingViewModel.java: -------------------------------------------------------------------------------- 1 | package okble.demo.ui.setting; 2 | 3 | import androidx.lifecycle.LiveData; 4 | import androidx.lifecycle.MutableLiveData; 5 | import androidx.lifecycle.ViewModel; 6 | 7 | public class SettingViewModel extends ViewModel { 8 | 9 | private MutableLiveData mText; 10 | 11 | public SettingViewModel() { 12 | mText = new MutableLiveData<>(); 13 | mText.setValue("It's coming soon!"); 14 | } 15 | 16 | public LiveData getText() { 17 | return mText; 18 | } 19 | } -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/executor/UpdateConnectionParameterExecutor.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.executor; 2 | 3 | import okble.central.client.ClientOperation; 4 | import okble.central.client.OkBleTask0; 5 | import okble.central.client.request.Request; 6 | 7 | final class UpdateConnectionParameterExecutor extends RequestExecutor { 8 | @Override 9 | public void executeRequest(ClientOperation client, OkBleTask0 task, Request request0) { 10 | throw new RuntimeException("update connection parameter not supported now!"); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/java/okble/demo/ui/device2/LogInfo.java: -------------------------------------------------------------------------------- 1 | package okble.demo.ui.device2; 2 | 3 | public class LogInfo { 4 | 5 | public final long time; 6 | public final String message; 7 | public final int level; 8 | 9 | public LogInfo(long time, String message, int level) { 10 | this.time = time; 11 | this.message = message; 12 | this.level = level; 13 | } 14 | 15 | public LogInfo(long time, String message) { 16 | this.time = time; 17 | this.message = message; 18 | this.level = 0; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/Mtu.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | public final class Mtu { 4 | 5 | private final int value; 6 | 7 | private Mtu(final int value){ 8 | this.value = value; 9 | } 10 | 11 | public static Mtu of(final int value){ 12 | return new Mtu(value); 13 | } 14 | 15 | public int value(){ 16 | return this.value; 17 | } 18 | 19 | @Override 20 | public String toString() { 21 | return "Mtu{" + 22 | "value=" + value + 23 | '}'; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/util/RequestUtils.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.util; 2 | 3 | import okble.central.client.request.Request; 4 | 5 | public final class RequestUtils { 6 | 7 | public static boolean typeIn(Request.Type type, Request.Type... types ){ 8 | if(types == null){ 9 | return false; 10 | } 11 | for(Request.Type t : types){ 12 | if(t == type){ 13 | return true; 14 | } 15 | } 16 | return false; 17 | } 18 | 19 | private RequestUtils(){} 20 | 21 | } 22 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/Rssi.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | public final class Rssi { 4 | 5 | private final int value; 6 | 7 | private Rssi(final int value){ 8 | this.value = value; 9 | } 10 | 11 | public static Rssi of(final int value){ 12 | return new Rssi(value); 13 | } 14 | 15 | public int value(){ 16 | return this.value; 17 | } 18 | 19 | @Override 20 | public String toString() { 21 | return "Rssi{" + 22 | "value=" + value + 23 | '}'; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/request/DisconnectionRequest.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.request; 2 | 3 | 4 | final class DisconnectionRequest extends Request{ 5 | 6 | private DisconnectionRequest(final Builder builder){ 7 | super(Type.DisconnectionRequest); 8 | } 9 | 10 | private final static class Builder { 11 | public DisconnectionRequest build() { 12 | return new DisconnectionRequest(this); 13 | } 14 | } 15 | 16 | @Override 17 | public DisconnectionRequest getThis() { 18 | return this; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/executor/ConnectGattExecutor.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.executor; 2 | 3 | import okble.central.client.ClientOperation; 4 | import okble.central.client.OkBleTask0; 5 | import okble.central.client.exception.OkBleException; 6 | import okble.central.client.request.Request; 7 | 8 | final class ConnectGattExecutor extends RequestExecutor { 9 | @Override 10 | public void executeRequest(ClientOperation client, OkBleTask0 task, Request request0) throws OkBleException { 11 | throw new OkBleException("connect gatt not supported!"); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/request/ReadPhyRequest.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.request; 2 | 3 | import okble.central.client.Phy; 4 | 5 | 6 | public final class ReadPhyRequest extends Request{ 7 | 8 | private ReadPhyRequest(final Builder builder){ 9 | super(Type.ReadPhyRequest); 10 | } 11 | 12 | public final static class Builder { 13 | public ReadPhyRequest build() { 14 | return new ReadPhyRequest(this); 15 | } 16 | } 17 | 18 | @Override 19 | public ReadPhyRequest getThis() { 20 | return this; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/Requests.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | import okble.central.client.executor.RequestExecutor; 4 | import okble.central.client.executor.RequestExecutors; 5 | import okble.central.client.request.Request; 6 | 7 | 8 | final class Requests { 9 | 10 | 11 | static OkBleTask newTask(OkBleClient client, Request request){ 12 | final RequestExecutor handler = RequestExecutors.fromRequestType(request.type()); 13 | final OkBleTask0 task = new OkBleTask0(client, handler, request); 14 | return task; 15 | } 16 | 17 | 18 | } 19 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/executor/CreateBondExecutor.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.executor; 2 | 3 | import okble.central.client.ClientOperation; 4 | import okble.central.client.OkBleTask0; 5 | import okble.central.client.exception.OkBleException; 6 | import okble.central.client.request.Request; 7 | 8 | final class CreateBondExecutor extends RequestExecutor { 9 | @Override 10 | public void executeRequest(ClientOperation client, OkBleTask0 task, Request request0) throws OkBleException { 11 | throw new OkBleException("create bond request not supported now!"); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/request/DiscoverServicesRequest.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.request; 2 | 3 | 4 | final class DiscoverServicesRequest extends Request{ 5 | 6 | private DiscoverServicesRequest(final Builder builder){ 7 | super(Type.DiscoverServicesRequest); 8 | } 9 | 10 | private final static class Builder { 11 | public DiscoverServicesRequest build() { 12 | return new DiscoverServicesRequest(this); 13 | } 14 | } 15 | 16 | @Override 17 | public DiscoverServicesRequest getThis() { 18 | return this; 19 | } 20 | } -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/executor/DiscoverServicesExecutor.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.executor; 2 | 3 | import okble.central.client.ClientOperation; 4 | import okble.central.client.OkBleTask0; 5 | import okble.central.client.exception.OkBleException; 6 | import okble.central.client.request.Request; 7 | 8 | final class DiscoverServicesExecutor extends RequestExecutor { 9 | @Override 10 | public void executeRequest(ClientOperation client, OkBleTask0 task, Request request0) throws OkBleException { 11 | throw new OkBleException("discover services not supported now!"); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/executor/DisconnectionExecutor.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.executor; 2 | 3 | import okble.central.client.ClientOperation; 4 | import okble.central.client.OkBleTask0; 5 | import okble.central.client.exception.OkBleException; 6 | import okble.central.client.request.Request; 7 | 8 | final class DisconnectionExecutor extends RequestExecutor { 9 | @Override 10 | public void executeRequest(ClientOperation client, OkBleTask0 task, Request request) throws OkBleException { 11 | throw new OkBleException("disconnection request not supported now!"); 12 | 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/DefaultGattConnector.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | import android.bluetooth.BluetoothDevice; 4 | import android.bluetooth.BluetoothGatt; 5 | import android.bluetooth.BluetoothGattCallback; 6 | import android.content.Context; 7 | 8 | final class DefaultGattConnector implements GattConnector{ 9 | 10 | public DefaultGattConnector(){ 11 | 12 | } 13 | 14 | @Override 15 | public BluetoothGatt connectGatt(BluetoothDevice device, Context context, BluetoothGattCallback callback) { 16 | 17 | return device.connectGatt(context, false, callback); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/request/ReadRemoteRssiRequest.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.request; 2 | 3 | import okble.central.client.Rssi; 4 | 5 | public final class ReadRemoteRssiRequest extends Request{ 6 | 7 | private ReadRemoteRssiRequest(final Builder builder){ 8 | super(Type.ReadRemoteRssiRequest); 9 | } 10 | 11 | public final static class Builder { 12 | public ReadRemoteRssiRequest build() { 13 | return new ReadRemoteRssiRequest(this); 14 | } 15 | } 16 | 17 | @Override 18 | public ReadRemoteRssiRequest getThis() { 19 | return this; 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | OkBleDemo 3 | 4 | Devices 5 | Setting 6 | About 7 | 8 | Start Scan 9 | 10 | Pull down to scan devices. 11 | 12 | 13 | Scanning device should turn on the location setting. 14 | 15 | Ok 16 | Cancel 17 | 18 | 19 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/exception/GattStatusException.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.exception; 2 | 3 | 4 | public class GattStatusException extends OkBleException{ 5 | 6 | public GattStatusException(final int status) { 7 | super(GATT_OPERATION_STATUS_FAILED); 8 | this.status = status; 9 | } 10 | 11 | public GattStatusException(final int status, final String message) { 12 | super(GATT_OPERATION_STATUS_FAILED, message); 13 | this.status = status; 14 | } 15 | 16 | private final int status; 17 | 18 | public int status(){ 19 | return this.status; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/java/okble/demo/ui/about/AboutViewModel.java: -------------------------------------------------------------------------------- 1 | package okble.demo.ui.about; 2 | 3 | import androidx.lifecycle.LiveData; 4 | import androidx.lifecycle.MutableLiveData; 5 | import androidx.lifecycle.ViewModel; 6 | 7 | public class AboutViewModel extends ViewModel { 8 | 9 | private MutableLiveData mText; 10 | 11 | public AboutViewModel() { 12 | mText = new MutableLiveData<>(); 13 | mText.setValue("author:Okbean\n" + 14 | "github:https://github.com/okbean/OkBle\n" + 15 | "email:okbean020@163.com"); 16 | } 17 | 18 | public LiveData getText() { 19 | return mText; 20 | } 21 | } -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/ServiceValidator.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | import android.bluetooth.BluetoothGattService; 4 | 5 | import java.util.List; 6 | 7 | public interface ServiceValidator { 8 | 9 | boolean onValidateServices(OkBleClient client, List services); 10 | 11 | interface Factory{ 12 | 13 | ServiceValidator newServiceValidator(); 14 | 15 | Factory DEFAULT = new Factory(){ 16 | 17 | @Override 18 | public ServiceValidator newServiceValidator() { 19 | return new DefaultServiceValidator(); 20 | } 21 | }; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/res/values/en_easy_permissions_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Settings 5 | Cancel 6 | OkBleDemo needs location permission. 7 | 8 | 9 | Please click the Settings button below to grant OkBleDemo the location permission. 10 | Permission denied 11 | 12 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/exception/GattOperationFailedException.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.exception; 2 | 3 | public class GattOperationFailedException extends OkBleException { 4 | 5 | public GattOperationFailedException() { 6 | super(GATT_OPERATION_FAILED); 7 | } 8 | 9 | public GattOperationFailedException(String message) { 10 | super(GATT_OPERATION_FAILED,message); 11 | } 12 | 13 | public GattOperationFailedException(String message, Throwable cause) { 14 | super(GATT_OPERATION_FAILED,message, cause); 15 | } 16 | 17 | public GattOperationFailedException(Throwable cause) { 18 | super(GATT_OPERATION_FAILED, cause); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/NotificationStatus.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | public enum NotificationStatus { 4 | 5 | NotificationEnabled, 6 | IndicationEnabled, 7 | Disabled, 8 | Unknown; 9 | 10 | public static NotificationStatus getStatus(final byte[] value){ 11 | if(value == null || value.length != 2 || value[1] != 0x00){ 12 | return Unknown; 13 | } 14 | if(value[0] == 0x00){ 15 | return Disabled; 16 | }else if(value[0] == 0x01){ 17 | return NotificationEnabled; 18 | }else if(value[0] == 0x02){ 19 | return IndicationEnabled; 20 | } 21 | return Unknown; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/Phy.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | public final class Phy { 4 | 5 | private final int tx; 6 | private final int rx; 7 | 8 | private Phy(final int tx, final int rx){ 9 | this.rx = rx; 10 | this.tx = tx; 11 | } 12 | 13 | public static Phy of(final int tx, final int rx){ 14 | return new Phy(rx, tx); 15 | } 16 | 17 | public int tx() { 18 | return tx; 19 | } 20 | 21 | public int rx() { 22 | return rx; 23 | } 24 | 25 | @Override 26 | public String toString() { 27 | return "Phy{" + 28 | "tx=" + tx + 29 | ", rx=" + rx + 30 | '}'; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/GattConnector.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | import android.bluetooth.BluetoothDevice; 4 | import android.bluetooth.BluetoothGatt; 5 | import android.bluetooth.BluetoothGattCallback; 6 | import android.content.Context; 7 | 8 | 9 | public interface GattConnector { 10 | 11 | BluetoothGatt connectGatt(BluetoothDevice device, Context ctx, BluetoothGattCallback callback); 12 | 13 | interface Factory{ 14 | 15 | GattConnector newGattConnector(); 16 | 17 | Factory DEFAULT = new Factory(){ 18 | 19 | @Override 20 | public GattConnector newGattConnector() { 21 | return new DefaultGattConnector(); 22 | } 23 | }; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_list_empty_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/util/AndFilter.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.util; 2 | 3 | import okble.central.client.OkBleEvent; 4 | 5 | public final class AndFilter implements OkBleEventFilter { 6 | 7 | final OkBleEventFilter[] mFilters; 8 | public AndFilter(OkBleEventFilter... filters){ 9 | this.mFilters = filters; 10 | } 11 | 12 | @Override 13 | public boolean accept(OkBleEvent event) { 14 | if(mFilters != null){ 15 | for(OkBleEventFilter filter : mFilters){ 16 | if(filter == null){ 17 | continue; 18 | } 19 | if(!filter.accept(event)){ 20 | return false; 21 | } 22 | } 23 | } 24 | return true; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /OkBle/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/exception/GattDisconnectedException.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.exception; 2 | 3 | 4 | public class GattDisconnectedException extends OkBleException{ 5 | 6 | public GattDisconnectedException(final int status, final int state) { 7 | super(GATT_DISCONNECTED); 8 | this.status = status; 9 | this.state = state; 10 | } 11 | 12 | public GattDisconnectedException(final int status,final int state, final String message) { 13 | super(GATT_DISCONNECTED,message); 14 | this.status = status; 15 | this.state = state; 16 | } 17 | 18 | private final int state; 19 | 20 | public int state(){ 21 | return this.state; 22 | } 23 | 24 | private final int status; 25 | 26 | public int status(){ 27 | return this.status; 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/util/EventFilterOfValues.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.util; 2 | 3 | import okble.central.client.OkBleEvent; 4 | 5 | public final class EventFilterOfValues implements OkBleEventFilter{ 6 | 7 | private int[] events; 8 | 9 | 10 | public EventFilterOfValues(final int... events){ 11 | this.events = events; 12 | } 13 | 14 | 15 | public static EventFilterOfValues from(int... events){ 16 | return new EventFilterOfValues(events); 17 | } 18 | 19 | @Override 20 | public boolean accept(OkBleEvent event) { 21 | if(events == null || event == null){ 22 | return false; 23 | } 24 | for(int val : events){ 25 | if(val == event.value()){ 26 | return true; 27 | } 28 | } 29 | return false; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/PhyOptions.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | import static android.bluetooth.BluetoothDevice.PHY_OPTION_NO_PREFERRED; 4 | import static android.bluetooth.BluetoothDevice.PHY_OPTION_S2; 5 | import static android.bluetooth.BluetoothDevice.PHY_OPTION_S8; 6 | 7 | public enum PhyOptions { 8 | NO_PREFERRED(PHY_OPTION_NO_PREFERRED), 9 | S2(PHY_OPTION_S2), 10 | S8(PHY_OPTION_S8); 11 | 12 | private final int value; 13 | PhyOptions(final int val){ 14 | this.value = val; 15 | } 16 | public int value(){ 17 | return this.value; 18 | } 19 | 20 | public static PhyOptions valueOf(final int val){ 21 | for(PhyOptions v : values()){ 22 | if(v.value == val){ 23 | return v; 24 | } 25 | } 26 | return null; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/res/navigation/mobile_navigation.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/DefaultConnectionOptions.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | final class DefaultConnectionOptions implements ConnectionOptions{ 4 | 5 | 6 | public DefaultConnectionOptions(){ 7 | } 8 | 9 | @Override 10 | public int connectionRetryCount() { 11 | return 1; 12 | } 13 | 14 | @Override 15 | public long connectGattDelay() { 16 | return 3_000L; 17 | } 18 | 19 | @Override 20 | public long connectGattInterval() { 21 | return 5_000L; 22 | } 23 | 24 | @Override 25 | public long connectGattTimeout() { 26 | return 32_000L; 27 | } 28 | 29 | @Override 30 | public long discoverServicesDelay() { 31 | return 2_000L; 32 | } 33 | 34 | @Override 35 | public long discoverServicesTimeout() { 36 | return 32_000L; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /app/src/androidTest/java/okble/demo/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package okble.demo; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | assertEquals("okble.demo", appContext.getPackageName()); 25 | } 26 | } -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/util/UiExecutor.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.util; 2 | 3 | import android.os.Handler; 4 | import android.os.Looper; 5 | 6 | import java.util.concurrent.Executor; 7 | 8 | public class UiExecutor implements Executor { 9 | 10 | private final Handler mH = new Handler(Looper.getMainLooper()); 11 | 12 | private final boolean mSync; 13 | 14 | public UiExecutor(boolean sync){ 15 | this.mSync = sync; 16 | } 17 | 18 | public UiExecutor(){ 19 | this(false); 20 | } 21 | 22 | @Override 23 | public void execute(final Runnable command) { 24 | if(command == null){ 25 | throw new IllegalArgumentException("command can not be null!"); 26 | } 27 | if(mSync && Looper.myLooper() == mH.getLooper()){ 28 | command.run(); 29 | }else { 30 | mH.post(command); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /OkBle/src/androidTest/java/okble/central/client/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | assertEquals("okble.central.client.test", appContext.getPackageName()); 25 | } 26 | } -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/WriteType.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | import static android.bluetooth.BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT; 4 | import static android.bluetooth.BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE; 5 | import static android.bluetooth.BluetoothGattCharacteristic.WRITE_TYPE_SIGNED; 6 | 7 | public enum WriteType { 8 | 9 | WriteDefault(WRITE_TYPE_DEFAULT), 10 | WriteNoResponse(WRITE_TYPE_NO_RESPONSE), 11 | WriteSigned(WRITE_TYPE_SIGNED); 12 | 13 | private final int value; 14 | WriteType(final int val){ 15 | this.value = val; 16 | } 17 | public int value(){ 18 | return this.value; 19 | } 20 | 21 | public static WriteType valueOf(final int val){ 22 | for(WriteType v : values()){ 23 | if(v.value == val){ 24 | return v; 25 | } 26 | } 27 | return null; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/util/EventFilterOfTime.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.util; 2 | 3 | import okble.central.client.OkBleEvent; 4 | 5 | public final class EventFilterOfTime implements OkBleEventFilter{ 6 | 7 | private int[] events; 8 | private long time; 9 | 10 | public EventFilterOfTime(long time, final int... events){ 11 | this.events = events; 12 | this.time = time; 13 | } 14 | 15 | public static EventFilterOfTime from(long time, int... events){ 16 | return new EventFilterOfTime(time, events); 17 | } 18 | 19 | @Override 20 | public boolean accept(OkBleEvent event) { 21 | if(events == null || event == null){ 22 | return false; 23 | } 24 | for(int val : events){ 25 | if(val == event.value() && event.time() > this.time){ 26 | return true; 27 | } 28 | } 29 | return false; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_requests.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 14 | 15 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_scanning_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 16 | 17 | 18 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/ConnectionOptions.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | public interface ConnectionOptions { 4 | 5 | // boolean reconnectOnDisconnection(); 6 | // long connectionDelay(); 7 | // long connectionInterval(); 8 | // long connectionTimeout(); 9 | // boolean refreshServicesOnGattConnected(); 10 | // boolean disconnectOnServicesNotValidate(); 11 | 12 | int connectionRetryCount(); 13 | 14 | long connectGattDelay(); 15 | long connectGattInterval(); 16 | long connectGattTimeout(); 17 | 18 | long discoverServicesDelay(); 19 | long discoverServicesTimeout(); 20 | 21 | interface Factory{ 22 | ConnectionOptions newConnectionOptions(); 23 | 24 | Factory DEFAULT = new Factory(){ 25 | @Override 26 | public ConnectionOptions newConnectionOptions() { 27 | return new DefaultConnectionOptions(); 28 | } 29 | 30 | }; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/ConnectionPriority.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | import static android.bluetooth.BluetoothGatt.CONNECTION_PRIORITY_BALANCED; 4 | import static android.bluetooth.BluetoothGatt.CONNECTION_PRIORITY_HIGH; 5 | import static android.bluetooth.BluetoothGatt.CONNECTION_PRIORITY_LOW_POWER; 6 | 7 | public enum ConnectionPriority { 8 | BALANCED(CONNECTION_PRIORITY_BALANCED), 9 | HIGH(CONNECTION_PRIORITY_HIGH), 10 | LOW_POWER(CONNECTION_PRIORITY_LOW_POWER); 11 | 12 | private final int value; 13 | ConnectionPriority(final int value){ 14 | this.value = value; 15 | } 16 | 17 | public int value(){ 18 | return this.value; 19 | } 20 | 21 | 22 | public static ConnectionPriority valueOf(final int val){ 23 | for(ConnectionPriority v : values()){ 24 | if(v.value == val){ 25 | return v; 26 | } 27 | } 28 | return null; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_log_info.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 18 | 19 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/request/ConnectGattRequest.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.request; 2 | 3 | import okble.central.client.GattConnector; 4 | 5 | final class ConnectGattRequest extends Request{ 6 | 7 | private GattConnector gattConnector; 8 | private ConnectGattRequest(final Builder builder){ 9 | super(Type.ConnectGattRequest); 10 | this.gattConnector = builder.gattConnector; 11 | } 12 | 13 | public GattConnector gattConnector() { 14 | return gattConnector; 15 | } 16 | 17 | 18 | private final static class Builder { 19 | private GattConnector gattConnector; 20 | 21 | public Builder connectionOptions(GattConnector gattConnector) { 22 | this.gattConnector = gattConnector; 23 | return this; 24 | } 25 | 26 | public ConnectGattRequest build() { 27 | return new ConnectGattRequest(this); 28 | } 29 | } 30 | 31 | @Override 32 | public ConnectGattRequest getThis() { 33 | return this; 34 | } 35 | } -------------------------------------------------------------------------------- /app/src/main/java/okble/demo/ui/device2/LogViewHolder.java: -------------------------------------------------------------------------------- 1 | package okble.demo.ui.device2; 2 | 3 | import android.view.View; 4 | import android.widget.TextView; 5 | 6 | import androidx.annotation.NonNull; 7 | import androidx.recyclerview.widget.RecyclerView; 8 | 9 | import java.text.SimpleDateFormat; 10 | import java.util.Locale; 11 | 12 | import butterknife.BindView; 13 | import butterknife.ButterKnife; 14 | import okble.demo.R; 15 | 16 | 17 | public class LogViewHolder extends RecyclerView.ViewHolder{ 18 | 19 | private SimpleDateFormat mFormat = new SimpleDateFormat("MM/dd HH:mm:ss", Locale.getDefault()); 20 | 21 | @BindView(R.id.time) 22 | TextView mTimeTv; 23 | 24 | @BindView(R.id.message) 25 | TextView mMessageTv; 26 | public LogViewHolder(@NonNull View itemView) { 27 | super(itemView); 28 | ButterKnife.bind(this, itemView); 29 | } 30 | 31 | public void render(LogInfo data){ 32 | mTimeTv.setText(mFormat.format(data.time)); 33 | mMessageTv.setText(data.message); 34 | } 35 | 36 | 37 | 38 | 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_about.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_setting.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 22 | -------------------------------------------------------------------------------- /app/src/main/java/okble/demo/ui/device2/ServiceViewHolder.java: -------------------------------------------------------------------------------- 1 | package okble.demo.ui.device2; 2 | 3 | import android.bluetooth.BluetoothGattService; 4 | import android.view.View; 5 | import android.widget.TextView; 6 | 7 | import androidx.annotation.NonNull; 8 | 9 | import butterknife.BindView; 10 | import butterknife.ButterKnife; 11 | import okble.demo.R; 12 | 13 | 14 | public class ServiceViewHolder extends GattViewHolder{ 15 | 16 | @BindView(R.id.name) 17 | TextView mTextTv; 18 | 19 | @BindView(R.id.uuid) 20 | TextView mUuidTv; 21 | 22 | @BindView(R.id.type) 23 | TextView mTypeTv; 24 | 25 | public ServiceViewHolder(@NonNull View itemView) { 26 | super(itemView); 27 | ButterKnife.bind(this, itemView); 28 | } 29 | 30 | @Override 31 | public void render(GattData data, final byte[] value){ 32 | final BluetoothGattService service = data.get(); 33 | mTextTv.setText(ServiceResolver.resolveName(service)); 34 | mUuidTv.setText("UUID: " + ServiceResolver.resolveUuid(service)); 35 | mTypeTv.setText(ServiceResolver.resolveType(service)); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/GattOperationResult.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | import android.bluetooth.BluetoothGatt; 4 | 5 | public final class GattOperationResult { 6 | 7 | private final int status; 8 | private final BluetoothGatt gatt; 9 | private final Object data; 10 | public GattOperationResult(final int status, final BluetoothGatt gatt, final Object data){ 11 | this.status = status; 12 | this.data = data; 13 | this.gatt = gatt; 14 | } 15 | 16 | public int status(){ 17 | return this.status; 18 | } 19 | 20 | public BluetoothGatt gatt(){ 21 | return this.gatt; 22 | } 23 | 24 | public T data(){ 25 | return (T)this.data; 26 | } 27 | 28 | public T data(Class clazz){ 29 | if(clazz.isInstance(this.data)){ 30 | return (T)this.data; 31 | } 32 | return null; 33 | } 34 | 35 | @Override 36 | public String toString() { 37 | return "GattOperationResult{" + 38 | "status=" + status + 39 | ", data=" + data + 40 | '}'; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/request/SetMtuRequest.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.request; 2 | 3 | import okble.central.client.Mtu; 4 | 5 | public final class SetMtuRequest extends Request{ 6 | 7 | private int mtu; 8 | private SetMtuRequest(final Builder builder){ 9 | super(Type.SetMtuRequest); 10 | this.mtu = builder.mtu; 11 | } 12 | 13 | public int mtu() { 14 | return mtu; 15 | } 16 | 17 | public final static class Builder { 18 | private int mtu; 19 | 20 | public Builder mtu(int mtu) { 21 | checkMtu(mtu); 22 | this.mtu = mtu; 23 | return this; 24 | } 25 | 26 | public SetMtuRequest build() { 27 | checkMtu(this.mtu); 28 | return new SetMtuRequest(this); 29 | } 30 | } 31 | 32 | @Override 33 | public SetMtuRequest getThis() { 34 | return this; 35 | } 36 | 37 | private static void checkMtu(final int mtu){ 38 | if(mtu < 23 || mtu > 517){ 39 | throw new IllegalArgumentException(String.format("mtu(%s) should less than 518 and more than 22!", mtu)); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/ConnectionParameter.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | 4 | public final class ConnectionParameter { 5 | 6 | private final int interval; 7 | private final int latency; 8 | private final int timeout; 9 | 10 | private ConnectionParameter(final int interval, final int latency, final int timeout){ 11 | this.interval = interval; 12 | this.latency = latency; 13 | this.timeout = timeout; 14 | } 15 | 16 | 17 | public static ConnectionParameter of(final int interval, final int latency, final int timeout){ 18 | return new ConnectionParameter(interval, latency, timeout); 19 | } 20 | 21 | public int interval() { 22 | return interval; 23 | } 24 | 25 | public int latency() { 26 | return latency; 27 | } 28 | 29 | public int timeout() { 30 | return timeout; 31 | } 32 | 33 | @Override 34 | public String toString() { 35 | return "ConnectionParameter{" + 36 | "interval=" + interval + 37 | ", latency=" + latency + 38 | ", timeout=" + timeout + 39 | '}'; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/DebugBluetoothGattCallback.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | public interface DebugBluetoothGattCallback { 4 | 5 | void onConnectionStateChange(OkBleClient client, int status, int newState); 6 | 7 | void onServicesDiscovered(OkBleClient client, int status); 8 | 9 | void onCharacteristicChanged(OkBleClient client, final CharacteristicData data); 10 | 11 | void onCharacteristicWrite(OkBleClient client, final CharacteristicData data, int status); 12 | 13 | void onCharacteristicRead(OkBleClient client, final CharacteristicData data, int status); 14 | 15 | void onDescriptorRead(OkBleClient client, final DescriptorData data, int status); 16 | 17 | void onDescriptorWrite(OkBleClient client, final DescriptorData data, int status); 18 | 19 | void onPhyUpdate(OkBleClient client, int txPhy, int rxPhy, int status); 20 | 21 | void onPhyRead(OkBleClient client, int txPhy, int rxPhy, int status); 22 | 23 | void onMtuChanged(OkBleClient client, int mtu, int status); 24 | 25 | void onReadRemoteRssi(OkBleClient client, int rssi, int status); 26 | 27 | void onConnectionUpdated(OkBleClient client, int interval, int latency, int timeout, int status); 28 | } 29 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/RequestTask.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | import okble.central.client.executor.RequestExecutor; 4 | import okble.central.client.request.Request; 5 | 6 | final class RequestTask{ 7 | 8 | private final OkBleTask0 task; 9 | private final RequestExecutor executor; 10 | 11 | private final Request request; 12 | public OkBleTask0 task() { 13 | return task; 14 | } 15 | 16 | public RequestExecutor executor() { 17 | return executor; 18 | } 19 | 20 | public Priority priority() { 21 | return priority; 22 | } 23 | 24 | private Priority priority; 25 | 26 | public RequestTask(OkBleTask0 task, RequestExecutor executor, Request request, Priority priority) { 27 | this.task = task; 28 | this.executor = executor; 29 | this.priority = priority; 30 | this.request = request; 31 | } 32 | 33 | 34 | private boolean occupied = false; 35 | public synchronized boolean occupy(){ 36 | final boolean val = occupied; 37 | if(!occupied){ 38 | occupied = true; 39 | } 40 | return !val; 41 | } 42 | 43 | public Request request() { 44 | return request; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/request/SetConnectionPriorityRequest.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.request; 2 | 3 | 4 | import okble.central.client.ConnectionPriority; 5 | 6 | public final class SetConnectionPriorityRequest extends Request{ 7 | 8 | private ConnectionPriority connectionPriority; 9 | private SetConnectionPriorityRequest(final Builder builder){ 10 | super(Type.SetConnectionPriorityRequest); 11 | this.connectionPriority = builder.connectionPriority; 12 | } 13 | 14 | public ConnectionPriority connectionPriority() { 15 | return connectionPriority; 16 | } 17 | 18 | public final static class Builder { 19 | private ConnectionPriority connectionPriority; 20 | 21 | public Builder connectionPriority(ConnectionPriority connectionPriority) { 22 | this.connectionPriority = connectionPriority; 23 | return this; 24 | } 25 | 26 | public SetConnectionPriorityRequest build() { 27 | check(connectionPriority != null, "connectionPriority can not be null!"); 28 | 29 | return new SetConnectionPriorityRequest(this); 30 | } 31 | } 32 | 33 | @Override 34 | public SetConnectionPriorityRequest getThis() { 35 | return this; 36 | } 37 | } -------------------------------------------------------------------------------- /app/src/main/java/okble/demo/ui/setting/SettingFragment.java: -------------------------------------------------------------------------------- 1 | package okble.demo.ui.setting; 2 | 3 | import android.os.Bundle; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.TextView; 8 | 9 | import androidx.annotation.NonNull; 10 | import androidx.annotation.Nullable; 11 | import androidx.fragment.app.Fragment; 12 | import androidx.lifecycle.Observer; 13 | import androidx.lifecycle.ViewModelProvider; 14 | 15 | import okble.demo.R; 16 | 17 | public class SettingFragment extends Fragment { 18 | 19 | private SettingViewModel mViewModel; 20 | 21 | public View onCreateView(@NonNull LayoutInflater inflater, 22 | ViewGroup container, Bundle savedInstanceState) { 23 | mViewModel = new ViewModelProvider(this).get(SettingViewModel.class); 24 | View root = inflater.inflate(R.layout.fragment_setting, container, false); 25 | final TextView textView = root.findViewById(R.id.text); 26 | mViewModel.getText().observe(getViewLifecycleOwner(), new Observer() { 27 | @Override 28 | public void onChanged(@Nullable String s) { 29 | textView.setText(s); 30 | } 31 | }); 32 | return root; 33 | } 34 | } -------------------------------------------------------------------------------- /app/src/main/java/okble/demo/util/OnePacketReceiverFactory.java: -------------------------------------------------------------------------------- 1 | package okble.demo.util; 2 | 3 | import okble.central.client.PacketReceiver; 4 | 5 | public class OnePacketReceiverFactory implements PacketReceiver.Factory{ 6 | 7 | @Override 8 | public PacketReceiver newPacketReceiver() { 9 | return new PacketReceiver(){ 10 | boolean isFinished = false; 11 | byte[] result; 12 | @Override 13 | public boolean isFinished() { 14 | return isFinished; 15 | } 16 | 17 | @Override 18 | public boolean isSuccess() { 19 | return isFinished; 20 | } 21 | 22 | @Override 23 | public boolean isFailed() { 24 | return false; 25 | } 26 | 27 | @Override 28 | public byte[] getResult() { 29 | return result; 30 | } 31 | 32 | @Override 33 | public Exception getError() { 34 | return null; 35 | } 36 | 37 | @Override 38 | public void onReceived(byte[] packet) { 39 | result = packet; 40 | isFinished = true; 41 | } 42 | }; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/java/okble/demo/ui/device2/DescriptorViewHolder.java: -------------------------------------------------------------------------------- 1 | package okble.demo.ui.device2; 2 | 3 | import android.bluetooth.BluetoothGattDescriptor; 4 | import android.text.TextUtils; 5 | import android.view.View; 6 | import android.widget.TextView; 7 | 8 | import androidx.annotation.NonNull; 9 | 10 | import butterknife.BindView; 11 | import butterknife.ButterKnife; 12 | import okble.demo.R; 13 | 14 | 15 | public class DescriptorViewHolder extends GattViewHolder{ 16 | 17 | @BindView(R.id.name) 18 | TextView mTextTv; 19 | 20 | @BindView(R.id.uuid) 21 | TextView mUuidTv; 22 | 23 | @BindView(R.id.value) 24 | TextView mValueTv; 25 | 26 | public DescriptorViewHolder(@NonNull View itemView) { 27 | super(itemView); 28 | ButterKnife.bind(this, itemView); 29 | } 30 | 31 | @Override 32 | public void render(GattData data, final byte[] value){ 33 | final BluetoothGattDescriptor descriptor = data.get(); 34 | mTextTv.setText(DescriptorResolver.resolveName(descriptor)); 35 | mUuidTv.setText("UUID: " + DescriptorResolver.resolveUuid(descriptor)); 36 | 37 | final String val = DescriptorResolver.resolveData(data.uuid().toString(), value); 38 | mValueTv.setText("Value: "); 39 | if(!TextUtils.isEmpty(val)){ 40 | mValueTv.append(val); 41 | } 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_descriptor.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 17 | 18 | 25 | 26 | 33 | 34 | 37 | -------------------------------------------------------------------------------- /app/src/main/res/layout/list_item_device.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 22 | 23 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_service.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 17 | 18 | 25 | 26 | 33 | 34 | 37 | -------------------------------------------------------------------------------- /app/src/main/java/okble/demo/ui/about/AboutFragment.java: -------------------------------------------------------------------------------- 1 | package okble.demo.ui.about; 2 | 3 | import android.os.Bundle; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.TextView; 8 | 9 | import androidx.annotation.NonNull; 10 | import androidx.annotation.Nullable; 11 | import androidx.fragment.app.Fragment; 12 | import androidx.lifecycle.Observer; 13 | import androidx.lifecycle.ViewModelProvider; 14 | 15 | import com.qmuiteam.qmui.widget.QMUITopBar; 16 | 17 | import butterknife.BindView; 18 | import butterknife.ButterKnife; 19 | import okble.demo.R; 20 | 21 | public class AboutFragment extends Fragment { 22 | 23 | private AboutViewModel mViewModel; 24 | 25 | @BindView(R.id.text) 26 | TextView mText; 27 | 28 | public View onCreateView(@NonNull LayoutInflater inflater, 29 | ViewGroup container, Bundle savedInstanceState) { 30 | mViewModel = new ViewModelProvider(this).get(AboutViewModel.class); 31 | View root = inflater.inflate(R.layout.fragment_about, container, false); 32 | ButterKnife.bind(this, root); 33 | mViewModel.getText().observe(getViewLifecycleOwner(), new Observer() { 34 | @Override 35 | public void onChanged(@Nullable String s) { 36 | mText.setText(s); 37 | } 38 | }); 39 | return root; 40 | } 41 | } -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/Pipe.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | import java.util.concurrent.BlockingQueue; 4 | import java.util.concurrent.LinkedBlockingQueue; 5 | import java.util.concurrent.TimeUnit; 6 | 7 | public abstract class Pipe { 8 | 9 | public abstract Sink sink(); 10 | public abstract Source source(); 11 | 12 | 13 | public static Pipe create(){ 14 | return new PipeImpl(); 15 | } 16 | 17 | private static class PipeImpl extends Pipe{ 18 | 19 | Sink sink; 20 | Source source; 21 | 22 | public PipeImpl(){ 23 | final BlockingQueue queue = new LinkedBlockingQueue(); 24 | 25 | sink = new Sink() { 26 | @Override 27 | public boolean write(T value) { 28 | return queue.offer(value); 29 | } 30 | }; 31 | 32 | source = new Source() { 33 | @Override 34 | public T read(long timeout, TimeUnit unit) throws InterruptedException { 35 | return queue.poll(timeout, unit); 36 | } 37 | }; 38 | } 39 | 40 | @Override 41 | public Sink sink() { 42 | return sink; 43 | } 44 | 45 | @Override 46 | public Source source() { 47 | return source; 48 | } 49 | } 50 | 51 | 52 | 53 | } 54 | -------------------------------------------------------------------------------- /app/src/main/java/okble/demo/ui/device2/GattViewHolder.java: -------------------------------------------------------------------------------- 1 | package okble.demo.ui.device2; 2 | 3 | import android.view.LayoutInflater; 4 | import android.view.View; 5 | import android.view.ViewGroup; 6 | 7 | import androidx.annotation.NonNull; 8 | import androidx.recyclerview.widget.RecyclerView; 9 | 10 | import okble.demo.R; 11 | 12 | public abstract class GattViewHolder extends RecyclerView.ViewHolder{ 13 | public GattViewHolder(@NonNull View itemView) { 14 | super(itemView); 15 | } 16 | 17 | public abstract void render(GattData data, final byte[] value); 18 | 19 | 20 | 21 | public static GattViewHolder newViewHolder(ViewGroup parent, int viewType){ 22 | GattViewHolder val = null; 23 | final LayoutInflater inflater = LayoutInflater.from(parent.getContext()); 24 | if(viewType == GattData.SERVICE){ 25 | final View itemView = inflater.inflate(R.layout.item_service, parent, false); 26 | val = new ServiceViewHolder(itemView); 27 | }else if(viewType == GattData.CHARACTERISTIC){ 28 | final View itemView = inflater.inflate(R.layout.item_characteristic, parent, false); 29 | val = new CharacteristicViewHolder(itemView); 30 | }else if(viewType == GattData.DESCRIPTOR){ 31 | final View itemView = inflater.inflate(R.layout.item_descriptor, parent, false); 32 | val = new DescriptorViewHolder(itemView); 33 | } 34 | return val; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /OkBle/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'com.github.dcendents.android-maven' 3 | 4 | group='com.github.okbean' 5 | 6 | android { 7 | compileSdkVersion 29 8 | buildToolsVersion "29.0.3" 9 | 10 | defaultConfig { 11 | minSdkVersion 21 12 | targetSdkVersion 29 13 | versionCode 204 14 | versionName "2.1.4" 15 | 16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 17 | consumerProguardFiles "consumer-rules.pro" 18 | } 19 | 20 | buildTypes { 21 | release { 22 | minifyEnabled false 23 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 24 | } 25 | } 26 | 27 | compileOptions { 28 | targetCompatibility JavaVersion.VERSION_1_8 29 | sourceCompatibility JavaVersion.VERSION_1_8 30 | } 31 | } 32 | 33 | dependencies { 34 | implementation fileTree(dir: "libs", include: ["*.jar"]) 35 | implementation 'androidx.appcompat:appcompat:1.2.0' 36 | testImplementation 'junit:junit:4.12' 37 | androidTestImplementation 'androidx.test.ext:junit:1.1.2' 38 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' 39 | } 40 | 41 | 42 | task android_createJar(type: Jar) { 43 | archivesBaseName = 'OkBle' 44 | from(project.zipTree('build/intermediates/aar_main_jar/release/classes.jar')) 45 | destinationDir = file('../') 46 | } 47 | android_createJar.dependsOn('build') 48 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/request/DisableNotificationRequest.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.request; 2 | 3 | import java.util.UUID; 4 | 5 | public final class DisableNotificationRequest extends Request{ 6 | 7 | private UUID service; 8 | private UUID characteristic; 9 | 10 | public UUID service() { 11 | return service; 12 | } 13 | 14 | public UUID characteristic() { 15 | return characteristic; 16 | } 17 | 18 | private DisableNotificationRequest(final Builder builder){ 19 | super(Type.DisableNotificationRequest); 20 | this.service = builder.service; 21 | this.characteristic = builder.characteristic; 22 | } 23 | 24 | public final static class Builder { 25 | private UUID service; 26 | private UUID characteristic; 27 | 28 | public Builder service(UUID service) { 29 | this.service = service; 30 | return this; 31 | } 32 | 33 | public Builder characteristic(UUID characteristic) { 34 | this.characteristic = characteristic; 35 | return this; 36 | } 37 | 38 | public DisableNotificationRequest build() { 39 | check(service != null, "service can not be null!"); 40 | check(characteristic != null, "characteristic can not be null!"); 41 | 42 | return new DisableNotificationRequest(this); 43 | } 44 | } 45 | 46 | @Override 47 | public DisableNotificationRequest getThis() { 48 | return this; 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/request/ReadCharacteristicRequest.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.request; 2 | 3 | import java.util.UUID; 4 | 5 | 6 | public final class ReadCharacteristicRequest extends Request{ 7 | 8 | private UUID service; 9 | private UUID characteristic; 10 | 11 | private ReadCharacteristicRequest(Builder builder){ 12 | super(Type.ReadCharacteristicRequest); 13 | this.service = builder.service; 14 | this.characteristic = builder.characteristic; 15 | } 16 | 17 | 18 | public UUID service() { 19 | return service; 20 | } 21 | 22 | public UUID characteristic() { 23 | return characteristic; 24 | } 25 | 26 | 27 | 28 | public final static class Builder { 29 | private UUID service; 30 | private UUID characteristic; 31 | 32 | public Builder service(UUID service) { 33 | this.service = service; 34 | return this; 35 | } 36 | 37 | public Builder characteristic(UUID characteristic) { 38 | this.characteristic = characteristic; 39 | return this; 40 | } 41 | 42 | public ReadCharacteristicRequest build(){ 43 | check(service != null, "service can not be null!"); 44 | check(characteristic != null, "characteristic can not be null!"); 45 | 46 | return new ReadCharacteristicRequest(this); 47 | } 48 | } 49 | 50 | @Override 51 | public ReadCharacteristicRequest getThis() { 52 | return this; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 19 | 20 | 31 | 32 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/java/okble/demo/ui/device2/CharacteristicViewHolder.java: -------------------------------------------------------------------------------- 1 | package okble.demo.ui.device2; 2 | 3 | import android.bluetooth.BluetoothGattCharacteristic; 4 | import android.text.TextUtils; 5 | import android.view.View; 6 | import android.widget.TextView; 7 | 8 | import androidx.annotation.NonNull; 9 | 10 | import butterknife.BindView; 11 | import butterknife.ButterKnife; 12 | import okble.demo.R; 13 | 14 | public class CharacteristicViewHolder extends GattViewHolder{ 15 | 16 | @BindView(R.id.name) 17 | TextView mTextTv; 18 | 19 | @BindView(R.id.uuid) 20 | TextView mUuidTv; 21 | 22 | @BindView(R.id.properties) 23 | TextView mPropertiesTv; 24 | 25 | @BindView(R.id.value) 26 | TextView mValueTv; 27 | 28 | public CharacteristicViewHolder(@NonNull View itemView) { 29 | super(itemView); 30 | ButterKnife.bind(this, itemView); 31 | } 32 | 33 | @Override 34 | public void render(GattData data, final byte[] value){ 35 | final BluetoothGattCharacteristic characteristic = data.get(); 36 | 37 | mTextTv.setText(CharacteristicResolver.resolveName(characteristic)); 38 | 39 | mUuidTv.setText("UUID: " + CharacteristicResolver.resolveUuid(characteristic)); 40 | 41 | mPropertiesTv.setText("Properties: " + CharacteristicResolver.resolveProperties(characteristic)); 42 | 43 | final String val = CharacteristicResolver.resolveData(data.uuid().toString(), value); 44 | mValueTv.setText("Value: "); 45 | if(!TextUtils.isEmpty(val)){ 46 | mValueTv.append(val); 47 | } 48 | } 49 | 50 | 51 | } 52 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/PhyMask.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | import java.util.ArrayList; 4 | 5 | import static android.bluetooth.BluetoothDevice.PHY_LE_1M_MASK; 6 | import static android.bluetooth.BluetoothDevice.PHY_LE_2M_MASK; 7 | import static android.bluetooth.BluetoothDevice.PHY_LE_CODED_MASK; 8 | 9 | public enum PhyMask { 10 | LE_1M(PHY_LE_1M_MASK), 11 | LE_2M(PHY_LE_2M_MASK), 12 | LE_CODED(PHY_LE_CODED_MASK); 13 | 14 | private final int value; 15 | PhyMask(final int val){ 16 | this.value = val; 17 | } 18 | public int value(){ 19 | return this.value; 20 | } 21 | 22 | public static int value(PhyMask... masks){ 23 | int val = 0; 24 | final int len = masks == null ? 0 : masks.length; 25 | if(len > 0){ 26 | for(PhyMask item : masks){ 27 | if(item != null){ 28 | val = val | item.value; 29 | } 30 | } 31 | } 32 | return val; 33 | } 34 | 35 | 36 | public static PhyMask[] valueOf(final int value){ 37 | final boolean _1M = (PHY_LE_1M_MASK & value) > 0; 38 | final boolean _2M = (PHY_LE_2M_MASK & value) > 0; 39 | final boolean _CODED = (PHY_LE_CODED_MASK & value) > 0; 40 | final ArrayList list = new ArrayList(3); 41 | if(_1M){ 42 | list.add(LE_1M); 43 | } 44 | if(_2M){ 45 | list.add(LE_2M); 46 | } 47 | if(_CODED){ 48 | list.add(LE_CODED); 49 | } 50 | return (PhyMask[])list.toArray(); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_characteristic.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 18 | 19 | 26 | 27 | 34 | 35 | 42 | 43 | 46 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 13 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 27 | 28 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/OkBleTask.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | import okble.central.client.exception.OkBleException; 4 | import okble.central.client.request.Request; 5 | 6 | import java.util.concurrent.TimeUnit; 7 | 8 | public abstract class OkBleTask { 9 | 10 | public abstract boolean isComplete(); 11 | public abstract boolean isSuccess(); 12 | public abstract boolean isFailed(); 13 | public abstract boolean isCanceled(); 14 | public abstract OkBleTask cancel(); 15 | 16 | public abstract OkBleClient client(); 17 | 18 | public abstract Request request(); 19 | 20 | public abstract TResult getResult(); 21 | public abstract OkBleException getException(); 22 | 23 | public abstract OkBleTask enqueue(); 24 | public abstract OkBleTask enqueue(Priority priority); 25 | public abstract TResult execute() throws OkBleException; 26 | public abstract TResult execute(Priority priority) throws OkBleException; 27 | 28 | public abstract OkBleTask await(); 29 | public abstract OkBleTask await(long timeout, TimeUnit unit); 30 | 31 | public abstract OkBleTask addOnSuccessListener(OnSuccessListener listener); 32 | public abstract OkBleTask removeOnSuccessListener(OnSuccessListener listener); 33 | 34 | public abstract OkBleTask addOnFailedListener(OnFailedListener listener); 35 | public abstract OkBleTask removeOnFailedListener(OnFailedListener listener); 36 | 37 | public abstract OkBleTask addOnCompleteListener(OnCompleteListener listener); 38 | public abstract OkBleTask removeOnCompleteListener(OnCompleteListener listener); 39 | 40 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.aar 4 | *.ap_ 5 | *.aab 6 | 7 | # Files for the ART/Dalvik VM 8 | *.dex 9 | 10 | # Java class files 11 | *.class 12 | 13 | # Generated files 14 | bin/ 15 | gen/ 16 | out/ 17 | # Uncomment the following line in case you need and you don't have the release build type files in your app 18 | # release/ 19 | 20 | # Gradle files 21 | .gradle/ 22 | build/ 23 | 24 | # Local configuration file (sdk path, etc) 25 | local.properties 26 | 27 | # Proguard folder generated by Eclipse 28 | proguard/ 29 | 30 | # Log Files 31 | *.log 32 | 33 | # Android Studio Navigation editor temp files 34 | .navigation/ 35 | 36 | # Android Studio captures folder 37 | captures/ 38 | 39 | # IntelliJ 40 | *.iml 41 | .idea/workspace.xml 42 | .idea/tasks.xml 43 | .idea/gradle.xml 44 | .idea/assetWizardSettings.xml 45 | .idea/dictionaries 46 | .idea/libraries 47 | # Android Studio 3 in .gitignore file. 48 | .idea/caches 49 | .idea/modules.xml 50 | # Comment next line if keeping position of elements in Navigation Editor is relevant for you 51 | .idea/navEditor.xml 52 | 53 | # Keystore files 54 | # Uncomment the following lines if you do not want to check your keystore files in. 55 | #*.jks 56 | #*.keystore 57 | 58 | # External native build folder generated in Android Studio 2.2 and later 59 | .externalNativeBuild 60 | .cxx/ 61 | 62 | # Google Services (e.g. APIs or Firebase) 63 | # google-services.json 64 | 65 | # Freeline 66 | freeline.py 67 | freeline/ 68 | freeline_project_description.json 69 | 70 | # fastlane 71 | fastlane/report.xml 72 | fastlane/Preview.html 73 | fastlane/screenshots 74 | fastlane/test_output 75 | fastlane/readme.md 76 | 77 | # Version control 78 | vcs.xml 79 | 80 | # lint 81 | lint/intermediates/ 82 | lint/generated/ 83 | lint/outputs/ 84 | lint/tmp/ 85 | # lint/reports/ 86 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/request/Request.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.request; 2 | 3 | import java.util.concurrent.atomic.AtomicInteger; 4 | 5 | public abstract class Request { 6 | 7 | public enum Type{ 8 | ConnectionRequest, 9 | DisconnectionRequest, 10 | DiscoverServicesRequest, 11 | ConnectGattRequest, 12 | 13 | WriteCharacteristicRequest, 14 | ReadCharacteristicRequest, 15 | WriteCharacteristicForResponseRequest, 16 | 17 | ReadDescriptorRequest, 18 | WriteDescriptorRequest, 19 | 20 | EnableNotificationRequest, 21 | DisableNotificationRequest, 22 | 23 | ReadPhyRequest, 24 | SetPreferredPhyRequest, 25 | 26 | RefreshRequest, 27 | ReadRemoteRssiRequest, 28 | SetMtuRequest, 29 | 30 | SetConnectionPriorityRequest, 31 | UpdateConnectionParameterRequest, 32 | 33 | RemoveBondRequest, 34 | CreateBondRequest, 35 | } 36 | 37 | private final static AtomicInteger sId = new AtomicInteger(0); 38 | 39 | private final Type type; 40 | private final int id; 41 | public Request(final Type type){ 42 | this.type = type; 43 | this.id = sId.getAndAdd(1); 44 | } 45 | 46 | public int id(){ 47 | return this.id; 48 | } 49 | 50 | private Object tag; 51 | public Request tag(final Object tag){ 52 | this.tag = tag; 53 | return getThis(); 54 | } 55 | 56 | public R tag(){ 57 | return (R)tag; 58 | } 59 | 60 | public final Type type(){ 61 | return type; 62 | } 63 | 64 | 65 | public abstract Request getThis(); 66 | 67 | static void check(final boolean validate, final String errorMsg){ 68 | if(!validate){ 69 | throw new IllegalArgumentException(errorMsg); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/ClientOperation.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | import android.bluetooth.BluetoothGatt; 4 | 5 | import java.util.UUID; 6 | import java.util.concurrent.Semaphore; 7 | 8 | import okble.central.client.exception.OkBleException; 9 | 10 | public interface ClientOperation { 11 | 12 | Semaphore connLock(); 13 | 14 | void setConnecting(); 15 | 16 | void setDisconnected(); 17 | 18 | void setConnected(BluetoothGatt gatt) throws OkBleException; 19 | 20 | void doOpen(); 21 | 22 | BluetoothGatt doConnectGatt(final GattConnector gattConnector) throws OkBleException; 23 | 24 | boolean doDiscoverServices(final BluetoothGatt gatt) throws OkBleException; 25 | 26 | boolean doWriteCharacteristic(final UUID service, final UUID characteristic, final byte[] value, final int writeType) throws OkBleException; 27 | 28 | boolean doReadCharacteristic(UUID service, UUID characteristic) throws OkBleException; 29 | 30 | boolean doReadDescriptor(UUID service, UUID characteristic, UUID descriptor) throws OkBleException; 31 | 32 | boolean doWriteDescriptor(UUID service, UUID characteristic, UUID descriptor, byte[] value) throws OkBleException; 33 | 34 | boolean doEnableIndication(UUID service, UUID characteristic) throws OkBleException; 35 | 36 | boolean doEnableNotification(UUID service, UUID characteristic) throws OkBleException; 37 | 38 | boolean doDisableNotification(UUID service, UUID characteristic) throws OkBleException; 39 | 40 | boolean doReadPhy() throws OkBleException; 41 | 42 | boolean doSetPreferredPhy(int txPhy, int rxPhy, int phyOptions) throws OkBleException; 43 | 44 | boolean doSetMtu(final int mtu) throws OkBleException; 45 | 46 | boolean doRefresh() throws OkBleException; 47 | 48 | boolean doReadRemoteRssi() throws OkBleException; 49 | 50 | boolean doSetConnectionPriority(int connectionPriority) throws OkBleException; 51 | } -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/request/ReadDescriptorRequest.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.request; 2 | 3 | import java.util.UUID; 4 | 5 | public final class ReadDescriptorRequest extends Request{ 6 | 7 | private UUID service; 8 | private UUID characteristic; 9 | private UUID descriptor; 10 | 11 | private ReadDescriptorRequest(Builder builder){ 12 | super(Type.ReadDescriptorRequest); 13 | this.service = builder.service; 14 | this.characteristic = builder.characteristic; 15 | this.descriptor = builder.descriptor; 16 | } 17 | 18 | 19 | public UUID service() { 20 | return service; 21 | } 22 | 23 | public UUID characteristic() { 24 | return characteristic; 25 | } 26 | 27 | public UUID descriptor() { 28 | return descriptor; 29 | } 30 | 31 | 32 | public final static class Builder { 33 | private UUID service; 34 | private UUID characteristic; 35 | private UUID descriptor; 36 | 37 | public Builder service(UUID service) { 38 | this.service = service; 39 | return this; 40 | } 41 | 42 | public Builder characteristic(UUID characteristic) { 43 | this.characteristic = characteristic; 44 | return this; 45 | } 46 | 47 | public Builder descriptor(UUID descriptor) { 48 | this.descriptor = descriptor; 49 | return this; 50 | } 51 | 52 | public ReadDescriptorRequest build(){ 53 | check(service != null, "service can not be null!"); 54 | check(characteristic != null, "characteristic can not be null!"); 55 | check(descriptor != null, "descriptor can not be null!"); 56 | 57 | return new ReadDescriptorRequest(this); 58 | } 59 | } 60 | 61 | @Override 62 | public ReadDescriptorRequest getThis() { 63 | return this; 64 | } 65 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_device.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 13 | 14 | 15 | 29 | 30 | 31 | 37 | 38 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/executor/RequestExecutor.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.executor; 2 | 3 | import okble.central.client.ClientOperation; 4 | import okble.central.client.OkBleEvent; 5 | import okble.central.client.util.OkBleEventFilter; 6 | import okble.central.client.OkBleTask0; 7 | import okble.central.client.Pipe; 8 | import okble.central.client.exception.OkBleException; 9 | import okble.central.client.request.Request; 10 | 11 | import java.util.UUID; 12 | import java.util.concurrent.TimeUnit; 13 | 14 | public abstract class RequestExecutor { 15 | 16 | private Pipe pipe = Pipe.create(); 17 | 18 | public abstract void executeRequest(ClientOperation client, OkBleTask0 task, Request request0) throws OkBleException; 19 | 20 | public Pipe pipe(){ 21 | return this.pipe; 22 | } 23 | 24 | OkBleEvent pipeReadNotThrow(OkBleEventFilter filter, long timeout, TimeUnit unit) { 25 | final long duration = unit.toMillis(timeout); 26 | long now = System.currentTimeMillis(); 27 | final long deadline = System.currentTimeMillis() + duration; 28 | while(deadline > now){ 29 | try{ 30 | OkBleEvent event = pipe().source().read(deadline - now, TimeUnit.MILLISECONDS); 31 | if(event == null){ 32 | return null; 33 | } 34 | if(filter.accept(event)){ 35 | return event; 36 | } 37 | }catch (InterruptedException ex){ 38 | ex.printStackTrace(); 39 | } 40 | now = System.currentTimeMillis(); 41 | } 42 | return null; 43 | } 44 | 45 | OkBleEvent pipeReadNotThrow(OkBleEventFilter filter, long timeoutInMills){ 46 | return pipeReadNotThrow(filter, timeoutInMills, TimeUnit.MILLISECONDS); 47 | } 48 | 49 | final static long GATT_OP_TIMEOUT = 32_000L; 50 | 51 | final static UUID CCCD_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"); 52 | 53 | } -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/request/SetPreferredPhyRequest.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.request; 2 | 3 | 4 | import okble.central.client.Phy; 5 | import okble.central.client.PhyOptions; 6 | import okble.central.client.PhyMask; 7 | 8 | public final class SetPreferredPhyRequest extends Request{ 9 | 10 | private PhyMask[] txPhy; 11 | private PhyMask[] rxPhy; 12 | private PhyOptions phyOptions; 13 | 14 | 15 | private SetPreferredPhyRequest(final Builder builder){ 16 | super(Type.SetPreferredPhyRequest); 17 | this.txPhy = builder.txPhy; 18 | this.rxPhy = builder.rxPhy; 19 | this.phyOptions = builder.phyOptions; 20 | } 21 | 22 | public PhyMask[] txPhy() { 23 | return txPhy; 24 | } 25 | 26 | public PhyMask[] rxPhy() { 27 | return rxPhy; 28 | } 29 | 30 | public PhyOptions phyOptions() { 31 | return phyOptions; 32 | } 33 | 34 | public final static class Builder { 35 | private PhyMask[] txPhy; 36 | private PhyMask[] rxPhy; 37 | private PhyOptions phyOptions; 38 | 39 | public Builder txPhy(PhyMask... txPhy) { 40 | this.txPhy = txPhy; 41 | return this; 42 | } 43 | 44 | public Builder rxPhy(PhyMask... rxPhy) { 45 | this.rxPhy = rxPhy; 46 | return this; 47 | } 48 | 49 | public Builder phyOptions(PhyOptions phyOptions) { 50 | this.phyOptions = phyOptions; 51 | return this; 52 | } 53 | 54 | public SetPreferredPhyRequest build() { 55 | 56 | check(txPhy != null && txPhy.length > 0, "txPhy can not be null or zero length!"); 57 | check(rxPhy != null && rxPhy.length > 0, "rxPhy can not be null or zero length!"); 58 | check(phyOptions != null, "phyOptions can not be null!"); 59 | 60 | return new SetPreferredPhyRequest(this); 61 | } 62 | } 63 | 64 | @Override 65 | public SetPreferredPhyRequest getThis() { 66 | return this; 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/request/EnableNotificationRequest.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.request; 2 | 3 | import okble.central.client.NotificationType; 4 | 5 | import java.util.UUID; 6 | 7 | public final class EnableNotificationRequest extends Request{ 8 | 9 | private UUID service; 10 | private UUID characteristic; 11 | private NotificationType notificationType; 12 | 13 | public UUID service() { 14 | return service; 15 | } 16 | 17 | public UUID characteristic() { 18 | return characteristic; 19 | } 20 | 21 | public NotificationType notificationType() { 22 | return this.notificationType; 23 | } 24 | 25 | private EnableNotificationRequest(final Builder builder){ 26 | super(Type.EnableNotificationRequest); 27 | this.service = builder.service; 28 | this.characteristic = builder.characteristic; 29 | this.notificationType = builder.notificationType; 30 | } 31 | 32 | public final static class Builder { 33 | private UUID service; 34 | private UUID characteristic; 35 | private NotificationType notificationType; 36 | 37 | public Builder service(UUID service) { 38 | this.service = service; 39 | return this; 40 | } 41 | 42 | public Builder characteristic(UUID characteristic) { 43 | this.characteristic = characteristic; 44 | return this; 45 | } 46 | 47 | public Builder notificationType(NotificationType type) { 48 | this.notificationType = type; 49 | return this; 50 | } 51 | 52 | public EnableNotificationRequest build() { 53 | check(service != null, "service can not be null!"); 54 | check(characteristic != null, "characteristic can not be null!"); 55 | check(notificationType != null, "notificationType can not be null!"); 56 | 57 | return new EnableNotificationRequest(this); 58 | } 59 | 60 | } 61 | 62 | @Override 63 | public EnableNotificationRequest getThis() { 64 | return this; 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/request/ConnectionRequest.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.request; 2 | 3 | import okble.central.client.ConnectionOptions; 4 | import okble.central.client.GattConnector; 5 | 6 | 7 | public final class ConnectionRequest extends Request{ 8 | 9 | private ConnectionOptions connectionOptions; 10 | private GattConnector gattConnector; 11 | public ConnectionRequest(final Builder builder){ 12 | super(Type.ConnectionRequest); 13 | this.connectionOptions = builder.connectionOptions; 14 | this.gattConnector = builder.gattConnector; 15 | } 16 | 17 | public ConnectionOptions connectionOptions() { 18 | return connectionOptions; 19 | } 20 | 21 | public GattConnector gattConnector() { 22 | return gattConnector; 23 | } 24 | 25 | 26 | public final static class Builder { 27 | private ConnectionOptions connectionOptions = ConnectionOptions.Factory.DEFAULT.newConnectionOptions(); 28 | private GattConnector gattConnector = GattConnector.Factory.DEFAULT.newGattConnector(); 29 | 30 | private Builder connectionOptions(ConnectionOptions connectionOptions) { 31 | if(connectionOptions == null){ 32 | throw new IllegalArgumentException("connectionOptions can not be null!"); 33 | } 34 | this.connectionOptions = connectionOptions; 35 | return this; 36 | } 37 | 38 | private Builder connectionOptions(GattConnector gattConnector) { 39 | if(gattConnector == null){ 40 | throw new IllegalArgumentException("gattConnector can not be null!"); 41 | } 42 | this.gattConnector = gattConnector; 43 | return this; 44 | } 45 | 46 | public ConnectionRequest build() { 47 | check(connectionOptions != null, "connectionOptions can not be null!"); 48 | check(gattConnector != null, "gattConnector can not be null!"); 49 | 50 | return new ConnectionRequest(this); 51 | } 52 | } 53 | 54 | @Override 55 | public ConnectionRequest getThis() { 56 | return this; 57 | } 58 | } -------------------------------------------------------------------------------- /app/src/main/java/okble/demo/ui/device2/DescriptorResolver.java: -------------------------------------------------------------------------------- 1 | package okble.demo.ui.device2; 2 | 3 | import android.bluetooth.BluetoothGattDescriptor; 4 | import android.text.TextUtils; 5 | 6 | import java.util.HashMap; 7 | 8 | import okble.central.client.util.Hex; 9 | import okble.demo.util.UuidUtils; 10 | 11 | public final class DescriptorResolver { 12 | 13 | public static String resolveName(BluetoothGattDescriptor descriptor){ 14 | final String name = sDescriptors.get(descriptor.getUuid().toString().toLowerCase()); 15 | return TextUtils.isEmpty(name) ? "Unknown Descriptor" : name; 16 | } 17 | 18 | public static String resolveUuid(BluetoothGattDescriptor descriptor){ 19 | final String val = UuidUtils.getShortUuid(descriptor.getUuid()); 20 | return TextUtils.isEmpty(val) ? descriptor.getUuid().toString() : ("0x" + val.toUpperCase()); 21 | } 22 | 23 | 24 | 25 | 26 | public static String resolveData(final String uuid, byte[] data){ 27 | if(data != null && data.length > 0){ 28 | return "(0x)" + Hex.toString(data, '-'); 29 | } 30 | return ""; 31 | } 32 | 33 | 34 | private static HashMap sDescriptors = new HashMap(); 35 | static { 36 | sDescriptors.put("00002900-0000-1000-8000-00805f9b34fb", "Characteristic Extended Properties"); 37 | sDescriptors.put("00002901-0000-1000-8000-00805f9b34fb", "Characteristic User Description"); 38 | sDescriptors.put("00002902-0000-1000-8000-00805f9b34fb", "Client Characteristic Configuration"); 39 | sDescriptors.put("00002903-0000-1000-8000-00805f9b34fb", "Server Characteristic Configuration"); 40 | sDescriptors.put("00002904-0000-1000-8000-00805f9b34fb", "Characteristic Presentation Format"); 41 | sDescriptors.put("00002905-0000-1000-8000-00805f9b34fb", "Characteristic Aggregate Format"); 42 | sDescriptors.put("00002906-0000-1000-8000-00805f9b34fb", "Valid Range"); 43 | sDescriptors.put("00002907-0000-1000-8000-00805f9b34fb", "External Report Reference Descriptor"); 44 | sDescriptors.put("00002908-0000-1000-8000-00805f9b34fb", "Report Reference Descriptor"); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/CharacteristicData.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | import android.bluetooth.BluetoothGattCharacteristic; 4 | 5 | import java.util.UUID; 6 | 7 | import okble.central.client.util.Hex; 8 | 9 | public final class CharacteristicData { 10 | 11 | private final UUID service; 12 | private final UUID characteristic; 13 | private final byte[] data; 14 | 15 | 16 | private CharacteristicData(UUID service, UUID characteristic, byte[] data){ 17 | this.service = service; 18 | this.characteristic = characteristic; 19 | this.data = data; 20 | } 21 | 22 | 23 | public UUID service() { 24 | return service; 25 | } 26 | 27 | public UUID characteristic() { 28 | return characteristic; 29 | } 30 | 31 | public byte[] data() { 32 | return data; 33 | } 34 | 35 | 36 | private CharacteristicData of(UUID service, UUID characteristic,UUID descriptor, byte[] data){ 37 | return new CharacteristicData(service, 38 | characteristic, 39 | data); 40 | } 41 | 42 | 43 | public static CharacteristicData of(String service, String characteristic, byte[] data){ 44 | return new CharacteristicData(UUID.fromString(service), 45 | UUID.fromString(characteristic), 46 | data); 47 | } 48 | 49 | public static CharacteristicData from(BluetoothGattCharacteristic characteristic){ 50 | return new CharacteristicData(characteristic.getService().getUuid() , 51 | characteristic.getUuid(), 52 | characteristic.getValue()); 53 | } 54 | 55 | public static CharacteristicData from(BluetoothGattCharacteristic characteristic, byte[] data){ 56 | return new CharacteristicData(characteristic.getService().getUuid() , 57 | characteristic.getUuid(), 58 | data); 59 | } 60 | 61 | @Override 62 | public String toString() { 63 | return "CharacteristicData{" + 64 | "service=" + service + 65 | ", characteristic=" + characteristic + 66 | ", data=" + "(0x)" + Hex.toString(data, '-') + 67 | '}'; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_devices.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | 16 | 25 | 26 | 27 | 28 | 37 | 38 | 39 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_device2.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 13 | 14 | 15 | 29 | 30 | 31 | 39 | 40 | 43 | 44 | 45 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/executor/ReadPhyExecutor.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.executor; 2 | 3 | import android.bluetooth.BluetoothGatt; 4 | 5 | import okble.central.client.ClientOperation; 6 | import okble.central.client.util.EventFilterOfTime; 7 | import okble.central.client.GattOperationResult; 8 | import okble.central.client.OkBleEvent; 9 | import okble.central.client.util.OkBleEventFilter; 10 | import okble.central.client.OkBleTask0; 11 | import okble.central.client.Phy; 12 | import okble.central.client.exception.GattOperationFailedException; 13 | import okble.central.client.exception.GattOperationTimeoutException; 14 | import okble.central.client.exception.OkBleException; 15 | import okble.central.client.request.ReadPhyRequest; 16 | import okble.central.client.request.Request; 17 | 18 | final class ReadPhyExecutor extends RequestExecutor { 19 | 20 | @Override 21 | public void executeRequest(ClientOperation client, OkBleTask0 task, Request request0) { 22 | final ReadPhyRequest request = (ReadPhyRequest)request0; 23 | try{ 24 | final long now = System.currentTimeMillis(); 25 | final boolean success = client.doReadPhy(); 26 | if(!success){ 27 | throw new GattOperationFailedException( "read phy return false!"); 28 | } 29 | final OkBleEventFilter filter = getFilter(now); 30 | final OkBleEvent event = pipeReadNotThrow(filter, GATT_OP_TIMEOUT); 31 | if(event == null){ 32 | throw new GattOperationTimeoutException( "read phy operation timeout!"); 33 | } 34 | final GattOperationResult result = event.data(); 35 | if(result.status() == BluetoothGatt.GATT_SUCCESS){ 36 | final Phy data = result.data(); 37 | task.callOnSuccess(data, false); 38 | }else{ 39 | throw new GattOperationFailedException( "read phy operation failed!"); 40 | } 41 | }catch (OkBleException ex){ 42 | task.callOnFailed(ex, false); 43 | }catch (Exception ex2){ 44 | task.callOnFailed(new OkBleException(ex2), false); 45 | } 46 | } 47 | 48 | 49 | 50 | private static OkBleEventFilter getFilter(final long time){ 51 | final OkBleEventFilter filter = new EventFilterOfTime(time, OkBleEvent.READ_PHY); 52 | return filter; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/exception/OkBleException.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.exception; 2 | 3 | public class OkBleException extends Exception{ 4 | 5 | public final static int UNDEFINED = -1; 6 | 7 | public final static int GATT_OPERATION_STATUS_FAILED = 1000; 8 | public final static int GATT_OPERATION_FAILED = 1001; 9 | public final static int GATT_OPERATION_TIMEOUT = 1002; 10 | public final static int DATA_RECEIVE_TIMEOUT = 1003; 11 | public final static int GATT_DISCONNECTED = 1004; 12 | 13 | public final static int SERVICES_NOT_DISCOVERED = 1005; 14 | public final static int GATT_NOT_FOUND = 1006; 15 | 16 | public final static int GATT_SERVICE_NOT_FOUND = 1007; 17 | public final static int GATT_CHARACTERISTIC_NOT_FOUND = 1008; 18 | public final static int GATT_DESCRIPTOR_NOT_FOUND = 1009; 19 | 20 | 21 | public final static int CLIENT_RELEASED = 1101; 22 | public final static int CLIENT_CLOSED = 1102; 23 | public final static int CLIENT_NOT_CONNECTED = 1103; 24 | 25 | 26 | public final static int BLUETOOTH_NOT_ENABLED = 1200; 27 | 28 | public final static int TASK_CANCELED = 1300; 29 | 30 | public final static int CONNECTION_LOCK_ACQUIRE_FAILED = 1400; 31 | 32 | private final int code; 33 | 34 | 35 | public OkBleException(final int code) { 36 | super(); 37 | this.code = code; 38 | } 39 | 40 | 41 | public OkBleException(final String message) { 42 | super(message); 43 | this.code = UNDEFINED; 44 | } 45 | 46 | public OkBleException(final int code , final String message) { 47 | super(message); 48 | this.code = code; 49 | } 50 | 51 | 52 | public OkBleException(String message, Throwable cause) { 53 | super(message, cause); 54 | this.code = UNDEFINED; 55 | } 56 | public OkBleException(final int code ,String message, Throwable cause) { 57 | super(message, cause); 58 | this.code = code; 59 | } 60 | 61 | 62 | public OkBleException(Throwable cause) { 63 | super(cause); 64 | this.code = UNDEFINED; 65 | } 66 | 67 | public OkBleException(final int code, final Throwable cause) { 68 | super(cause); 69 | this.code = code; 70 | } 71 | 72 | public int code(){ 73 | return this.code; 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/executor/SetMtuExecutor.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.executor; 2 | 3 | import android.bluetooth.BluetoothGatt; 4 | 5 | import okble.central.client.ClientOperation; 6 | import okble.central.client.util.EventFilterOfTime; 7 | import okble.central.client.GattOperationResult; 8 | import okble.central.client.Mtu; 9 | import okble.central.client.OkBleEvent; 10 | import okble.central.client.util.OkBleEventFilter; 11 | import okble.central.client.OkBleTask0; 12 | import okble.central.client.exception.GattOperationFailedException; 13 | import okble.central.client.exception.GattOperationTimeoutException; 14 | import okble.central.client.exception.OkBleException; 15 | import okble.central.client.request.Request; 16 | import okble.central.client.request.SetMtuRequest; 17 | 18 | final class SetMtuExecutor extends RequestExecutor { 19 | 20 | @Override 21 | public void executeRequest(ClientOperation client, OkBleTask0 task, Request request0) { 22 | final SetMtuRequest request = (SetMtuRequest)request0; 23 | 24 | final int mtu = request.mtu(); 25 | 26 | try{ 27 | final long now = System.currentTimeMillis(); 28 | final boolean success = client.doSetMtu(mtu); 29 | if(!success){ 30 | throw new GattOperationFailedException("request mtu return false!"); 31 | } 32 | final OkBleEventFilter filter = getFilter(now); 33 | final OkBleEvent event = pipeReadNotThrow(filter, GATT_OP_TIMEOUT); 34 | if(event == null){ 35 | throw new GattOperationTimeoutException( "request mtu operation timeout!"); 36 | } 37 | final GattOperationResult result = event.data(); 38 | if(result.status() == BluetoothGatt.GATT_SUCCESS){ 39 | final Mtu data = result.data(); 40 | task.callOnSuccess(data, false ); 41 | }else{ 42 | throw new GattOperationFailedException( "request mtu operation failed!"); 43 | } 44 | }catch (OkBleException ex){ 45 | task.callOnFailed(ex, false); 46 | }catch (Exception ex2){ 47 | task.callOnFailed(new OkBleException(ex2), false); 48 | } 49 | } 50 | 51 | 52 | 53 | private static OkBleEventFilter getFilter(final long time){ 54 | final OkBleEventFilter filter = new EventFilterOfTime(time, OkBleEvent.SET_MTU); 55 | return filter; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/util/BluetoothUtils.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.util; 2 | 3 | import android.bluetooth.BluetoothAdapter; 4 | import android.bluetooth.BluetoothDevice; 5 | 6 | import java.lang.reflect.Method; 7 | import java.util.List; 8 | 9 | 10 | public final class BluetoothUtils { 11 | 12 | public static BluetoothDevice toBluetoothDevice(final String address){ 13 | final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 14 | final boolean validate = BluetoothAdapter.checkBluetoothAddress(address); 15 | BluetoothDevice val = null; 16 | if(validate){ 17 | val = adapter.getRemoteDevice(address); 18 | } 19 | return val; 20 | } 21 | 22 | 23 | public static boolean isBluetoothEnabled(){ 24 | final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 25 | final boolean val = adapter != null && adapter.isEnabled(); 26 | return val; 27 | } 28 | 29 | 30 | public static boolean createBond(final BluetoothDevice device){ 31 | final int state = device.getBondState(); 32 | if(state == BluetoothDevice.BOND_BONDED){ 33 | return true; 34 | } 35 | final boolean val = device.createBond(); 36 | return val; 37 | } 38 | 39 | 40 | public static boolean removeBond(final BluetoothDevice device){ 41 | final int state = device.getBondState(); 42 | if(state == BluetoothDevice.BOND_NONE){ 43 | return true; 44 | } 45 | try { 46 | final Method method = removeBondMethod(); 47 | return (boolean)method.invoke(device); 48 | } catch (Exception e) { 49 | e.printStackTrace(); 50 | } 51 | return false; 52 | } 53 | 54 | 55 | public static void removeBond(List devices){ 56 | if(devices != null){ 57 | for(BluetoothDevice device : devices){ 58 | removeBond(device); 59 | } 60 | } 61 | } 62 | 63 | 64 | private static Method sRemoveBondMethod = null; 65 | private static Method removeBondMethod() throws NoSuchMethodException { 66 | if(sRemoveBondMethod == null){ 67 | final Method method = BluetoothDevice.class.getMethod("removeBond"); 68 | sRemoveBondMethod = method; 69 | } 70 | return sRemoveBondMethod; 71 | } 72 | 73 | 74 | private BluetoothUtils(){} 75 | } 76 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/executor/ReadRemoteRssiExecutor.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.executor; 2 | 3 | import android.bluetooth.BluetoothGatt; 4 | 5 | import okble.central.client.ClientOperation; 6 | import okble.central.client.util.EventFilterOfTime; 7 | import okble.central.client.GattOperationResult; 8 | import okble.central.client.OkBleEvent; 9 | import okble.central.client.util.OkBleEventFilter; 10 | import okble.central.client.OkBleTask0; 11 | import okble.central.client.Rssi; 12 | import okble.central.client.exception.GattOperationFailedException; 13 | import okble.central.client.exception.GattOperationTimeoutException; 14 | import okble.central.client.exception.OkBleException; 15 | import okble.central.client.request.ReadRemoteRssiRequest; 16 | import okble.central.client.request.Request; 17 | 18 | final class ReadRemoteRssiExecutor extends RequestExecutor { 19 | 20 | @Override 21 | public void executeRequest(ClientOperation client, OkBleTask0 task, Request request0) { 22 | final ReadRemoteRssiRequest request = (ReadRemoteRssiRequest)request0; 23 | try{ 24 | final long now = System.currentTimeMillis(); 25 | final boolean success = client.doReadRemoteRssi(); 26 | if(!success){ 27 | throw new GattOperationFailedException( "read remote rssi return false!"); 28 | } 29 | final OkBleEventFilter filter = getFilter(now); 30 | final OkBleEvent event = pipeReadNotThrow(filter, GATT_OP_TIMEOUT); 31 | if(event == null){ 32 | throw new GattOperationTimeoutException( "read remote rssi operation timeout!"); 33 | } 34 | final GattOperationResult result = event.data(); 35 | if(result.status() == BluetoothGatt.GATT_SUCCESS){ 36 | final Rssi data = result.data(); 37 | task.callOnSuccess(data, false); 38 | }else{ 39 | throw new GattOperationFailedException( "read remote rssi operation failed!"); 40 | } 41 | }catch (OkBleException ex){ 42 | task.callOnFailed(ex, false); 43 | }catch (Exception ex2){ 44 | task.callOnFailed(new OkBleException(ex2), false); 45 | } 46 | } 47 | 48 | 49 | 50 | private static OkBleEventFilter getFilter(final long time){ 51 | final OkBleEventFilter filter = new EventFilterOfTime(time, OkBleEvent.READ_REMOTE_RSSI); 52 | return filter; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/Priority.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | public final class Priority implements Comparable { 4 | 5 | public enum Level{ 6 | HIGHEST(4), 7 | HIGH(3), 8 | NORMAL(2), 9 | LOW(1), 10 | LOWEST(0); 11 | private int val; 12 | Level(int val){ 13 | this.val = val; 14 | } 15 | public int val(){ 16 | return this.val; 17 | } 18 | } 19 | 20 | 21 | private Level level; 22 | private int value; 23 | 24 | 25 | private Priority(final Level level, final int value){ 26 | this.level = level; 27 | this.value = value; 28 | } 29 | 30 | public int value(){ 31 | return this.value; 32 | } 33 | 34 | public Level level(){ 35 | return this.level; 36 | } 37 | 38 | public static Priority high(final int value){ 39 | return new Priority(Level.HIGH, value); 40 | } 41 | public static Priority high(){ 42 | return new Priority(Level.HIGH, 0); 43 | } 44 | 45 | public static Priority low(final int value){ 46 | return new Priority(Level.LOW, value); 47 | } 48 | public static Priority low(){ 49 | return new Priority(Level.LOW, 0); 50 | } 51 | 52 | public static Priority normal(final int value){ 53 | return new Priority(Level.NORMAL, value); 54 | } 55 | 56 | public static Priority normal(){ 57 | return new Priority(Level.NORMAL, 0); 58 | } 59 | 60 | static Priority highest(){ 61 | return new Priority(Level.HIGHEST, Integer.MAX_VALUE); 62 | } 63 | static Priority highest(final int value){ 64 | return new Priority(Level.HIGHEST, value); 65 | } 66 | 67 | static Priority lowest(){ 68 | return new Priority(Level.LOWEST, Integer.MIN_VALUE); 69 | } 70 | static Priority lowest(final int value){ 71 | return new Priority(Level.LOWEST, value); 72 | } 73 | 74 | @Override 75 | public int compareTo(Priority o) { 76 | if(o == null){ 77 | return 1; 78 | } 79 | final int val = o.level.val - this.level.val; 80 | if(val != 0){ 81 | return val; 82 | } 83 | return o.value - this.value; 84 | } 85 | 86 | 87 | @Override 88 | public String toString() { 89 | return "Priority{" + 90 | "level=" + level + 91 | ", value=" + value + 92 | '}'; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /app/src/main/java/okble/demo/MainActivity.java: -------------------------------------------------------------------------------- 1 | package okble.demo; 2 | 3 | import android.os.Bundle; 4 | 5 | import com.google.android.material.bottomnavigation.BottomNavigationView; 6 | 7 | import androidx.annotation.NonNull; 8 | import androidx.appcompat.app.AppCompatActivity; 9 | import androidx.navigation.NavController; 10 | import androidx.navigation.Navigation; 11 | import androidx.navigation.ui.AppBarConfiguration; 12 | import androidx.navigation.ui.NavigationUI; 13 | 14 | import java.util.List; 15 | 16 | import okble.central.scanner.OkBleScanner; 17 | import okble.demo.util.Utils; 18 | import pub.devrel.easypermissions.EasyPermissions; 19 | 20 | public class MainActivity extends AppCompatActivity implements EasyPermissions.PermissionCallbacks { 21 | 22 | @Override 23 | protected void onCreate(Bundle savedInstanceState) { 24 | super.onCreate(savedInstanceState); 25 | setContentView(R.layout.activity_main); 26 | BottomNavigationView navView = findViewById(R.id.nav_view); 27 | // Passing each menu ID as a set of Ids because each 28 | // menu should be considered as top level destinations. 29 | AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder( 30 | R.id.navigation_devices, R.id.navigation_about) 31 | .build(); 32 | NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment); 33 | NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration); 34 | NavigationUI.setupWithNavController(navView, navController); 35 | } 36 | 37 | 38 | @Override 39 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { 40 | super.onRequestPermissionsResult(requestCode, permissions, grantResults); 41 | Utils.onRequestPermissionsResult(this, requestCode, permissions, grantResults, this); 42 | } 43 | 44 | @Override 45 | public void onPermissionsGranted(int requestCode, @NonNull List perms) { 46 | 47 | } 48 | 49 | @Override 50 | public void onPermissionsDenied(int requestCode, @NonNull List perms) { 51 | if(Utils.somePermissionPermanentlyDenied(this, perms)){ 52 | Utils.promptForSomePermissionPermanentlyDenied(this, requestCode, perms); 53 | } 54 | } 55 | 56 | 57 | @Override 58 | public void onDestroy(){ 59 | super.onDestroy(); 60 | OkBleScanner.getDefault().stopScan(); 61 | } 62 | } -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /app/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /app/src/main/java/okble/demo/util/UuidUtils.java: -------------------------------------------------------------------------------- 1 | package okble.demo.util; 2 | 3 | import java.util.UUID; 4 | 5 | public final class UuidUtils { 6 | 7 | 8 | private final static String UUID_PAR = "0000%s-0000-1000-8000-00805f9b34fb"; 9 | 10 | 11 | private final static String UUID_CCCD = "00002902-0000-1000-8000-00805f9b34fb"; 12 | 13 | 14 | public static boolean isCccd(final String uuid){ 15 | return UUID_CCCD.equalsIgnoreCase(uuid); 16 | } 17 | 18 | public static boolean isCccd(final UUID uuid){ 19 | return isCccd(uuid == null ? null : uuid.toString()); 20 | } 21 | 22 | 23 | public static String getShortUuid(final String uuid){ 24 | if(canBeShortUuid(uuid)){ 25 | return uuid.toLowerCase().substring(4, 8); 26 | } 27 | return ""; 28 | } 29 | 30 | public static String getShortUuid(final UUID uuid){ 31 | return getShortUuid(uuid == null ? null : uuid.toString()); 32 | } 33 | 34 | public static boolean canBeShortUuid(final String uuid){ 35 | if(uuid == null || uuid.length() != 36){ 36 | return false; 37 | } 38 | final String lowerUuid = uuid.toLowerCase(); 39 | if(lowerUuid.startsWith("0000") && 40 | lowerUuid.startsWith("0000-1000-8000-00805f9b34fb", 9)){ 41 | return true; 42 | } 43 | return false; 44 | } 45 | 46 | public static UUID fromShortHex(final String hexStr){ 47 | checkShortHex(hexStr); 48 | final String str = String.format(UUID_PAR, hexStr); 49 | final UUID val = UUID.fromString(str); 50 | return val; 51 | } 52 | 53 | private static void checkShortHex(final String hexStr){ 54 | if(hexStr == null || hexStr.length() == 0 ){ 55 | throw new IllegalArgumentException("hexStr is null or zero length!"); 56 | } 57 | if(hexStr.length() != 4){ 58 | throw new IllegalArgumentException("hexStr should be 4 length string!"); 59 | } 60 | for(int i= 0 ; i<4; i++){ 61 | final char c = hexStr.charAt(i); 62 | if(!isHexDigit(c)){ 63 | throw new IllegalArgumentException("hexStr should only contain hex digit!"); 64 | } 65 | } 66 | } 67 | private static boolean isHexDigit(char c){ 68 | if((c>=0 && c<='9') || 69 | (c>='a' && c<='z') || 70 | (c>='A' && c<='Z')){ 71 | return true; 72 | } 73 | return false; 74 | } 75 | 76 | 77 | private UuidUtils(){} 78 | } 79 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/util/ExecutorProxy.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.util; 2 | 3 | import java.lang.reflect.InvocationHandler; 4 | import java.lang.reflect.Method; 5 | import java.lang.reflect.Proxy; 6 | import java.util.concurrent.CountDownLatch; 7 | import java.util.concurrent.Executor; 8 | 9 | public final class ExecutorProxy implements InvocationHandler{ 10 | 11 | private final Executor mExecutor; 12 | private final Object mTarget; 13 | 14 | private ExecutorProxy(final Object target, final Executor executor){ 15 | this.mExecutor = executor; 16 | this.mTarget = target; 17 | } 18 | 19 | public static T newProxy(final T target, final Executor executor){ 20 | if(executor == null){ 21 | throw new IllegalArgumentException("executor can not be null!"); 22 | } 23 | final Class clazz = target.getClass(); 24 | final ClassLoader loader = clazz.getClassLoader(); 25 | final Class[] interfaces = clazz.getInterfaces(); 26 | final InvocationHandler invocationHandler = new ExecutorProxy(target, executor); 27 | return (T) Proxy.newProxyInstance(loader, interfaces,invocationHandler); 28 | } 29 | 30 | @Override 31 | public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { 32 | final CountDownLatch lock = new CountDownLatch(1); 33 | final boolean[] success = new boolean[1]; 34 | final Object[] result = new Object[1]; 35 | final Exception[] exception = new Exception[1]; 36 | mExecutor.execute(new Runnable() { 37 | @Override 38 | public void run() { 39 | if(lock.getCount() <= 0){ 40 | return; 41 | } 42 | try{ 43 | final Object val = method.invoke(mTarget, args); 44 | success[0] = true; 45 | result[0] = val; 46 | }catch (Exception ex){ 47 | success[0] = false; 48 | exception[0] = ex; 49 | }finally { 50 | lock.countDown(); 51 | } 52 | } 53 | }); 54 | try{ 55 | lock.await(); 56 | }catch (InterruptedException ex){ 57 | if(lock.getCount() > 0){ 58 | lock.countDown(); 59 | } 60 | throw ex; 61 | } 62 | if(!success[0] && exception[0] != null){ 63 | throw exception[0]; 64 | } 65 | return result[0]; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/DescriptorData.java: -------------------------------------------------------------------------------- 1 | package okble.central.client; 2 | 3 | import android.bluetooth.BluetoothGattDescriptor; 4 | 5 | import java.util.UUID; 6 | 7 | import okble.central.client.util.Hex; 8 | 9 | public final class DescriptorData { 10 | 11 | private final UUID service; 12 | private final UUID characteristic; 13 | private UUID descriptor; 14 | private final byte[] data; 15 | 16 | 17 | private DescriptorData(UUID service, UUID characteristic,UUID descriptor, byte[] data){ 18 | this.service = service; 19 | this.characteristic = characteristic; 20 | this.descriptor = descriptor; 21 | this.data = data; 22 | } 23 | 24 | 25 | public UUID service() { 26 | return service; 27 | } 28 | 29 | public UUID characteristic() { 30 | return characteristic; 31 | } 32 | 33 | public UUID descriptor() { 34 | return descriptor; 35 | } 36 | 37 | public byte[] data() { 38 | return data; 39 | } 40 | 41 | 42 | private DescriptorData of(UUID service, UUID characteristic,UUID descriptor, byte[] data){ 43 | return new DescriptorData(service, 44 | characteristic, 45 | descriptor, 46 | data); 47 | } 48 | 49 | 50 | public static DescriptorData of(String service, String characteristic,String descriptor, byte[] data){ 51 | return new DescriptorData(UUID.fromString(service), 52 | UUID.fromString(characteristic), 53 | UUID.fromString(descriptor), 54 | data); 55 | } 56 | 57 | public static DescriptorData from(BluetoothGattDescriptor descriptor){ 58 | return new DescriptorData(descriptor.getCharacteristic().getService().getUuid() , 59 | descriptor.getCharacteristic().getUuid(), 60 | descriptor.getUuid(), 61 | descriptor.getValue()); 62 | } 63 | 64 | public static DescriptorData from(BluetoothGattDescriptor descriptor, byte[] data){ 65 | return new DescriptorData(descriptor.getCharacteristic().getService().getUuid() , 66 | descriptor.getCharacteristic().getUuid(), 67 | descriptor.getUuid(), 68 | data); 69 | } 70 | 71 | 72 | @Override 73 | public String toString() { 74 | return "DescriptorData{" + 75 | "service=" + service + 76 | ", characteristic=" + characteristic + 77 | ", descriptor=" + descriptor + 78 | ", data=" + "(0x)" + Hex.toString(data, '-') + 79 | '}'; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/executor/SetConnectionPriorityExecutor.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.executor; 2 | 3 | import android.bluetooth.BluetoothGatt; 4 | 5 | import okble.central.client.ClientOperation; 6 | import okble.central.client.ConnectionParameter; 7 | import okble.central.client.ConnectionPriority; 8 | import okble.central.client.GattOperationResult; 9 | import okble.central.client.Mtu; 10 | import okble.central.client.OkBleEvent; 11 | import okble.central.client.OkBleTask0; 12 | import okble.central.client.exception.GattOperationFailedException; 13 | import okble.central.client.exception.GattOperationTimeoutException; 14 | import okble.central.client.exception.OkBleException; 15 | import okble.central.client.request.Request; 16 | import okble.central.client.request.SetConnectionPriorityRequest; 17 | import okble.central.client.util.EventFilterOfTime; 18 | import okble.central.client.util.OkBleEventFilter; 19 | 20 | public final class SetConnectionPriorityExecutor extends RequestExecutor { 21 | 22 | @Override 23 | public void executeRequest(ClientOperation client, OkBleTask0 task, Request request0) { 24 | final SetConnectionPriorityRequest request = (SetConnectionPriorityRequest)request0; 25 | final int connectionPriority = request.connectionPriority().value(); 26 | try { 27 | final long now = System.currentTimeMillis(); 28 | final boolean success = client.doSetConnectionPriority(connectionPriority); 29 | if(!success){ 30 | throw new GattOperationFailedException("request connection priority return false!"); 31 | } 32 | final OkBleEventFilter filter = getFilter(now); 33 | final OkBleEvent event = pipeReadNotThrow(filter, GATT_OP_TIMEOUT); 34 | if(event == null){ 35 | throw new GattOperationTimeoutException( "request connection priority timeout!"); 36 | } 37 | final GattOperationResult result = event.data(); 38 | if(result.status() == BluetoothGatt.GATT_SUCCESS){ 39 | final ConnectionParameter data = result.data(); 40 | task.callOnSuccess(null, false ); 41 | }else{ 42 | throw new GattOperationFailedException( "request mtu operation failed!"); 43 | } 44 | } catch (OkBleException ex) { 45 | task.callOnFailed(ex, false); 46 | } 47 | } 48 | 49 | private static OkBleEventFilter getFilter(final long time){ 50 | final OkBleEventFilter filter = new EventFilterOfTime(time, OkBleEvent.SET_CONNECTION_PRIORITY); 51 | return filter; 52 | } 53 | 54 | 55 | } 56 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/executor/SetPreferredPhyExecutor.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.executor; 2 | 3 | import android.bluetooth.BluetoothGatt; 4 | 5 | import okble.central.client.ClientOperation; 6 | import okble.central.client.PhyMask; 7 | import okble.central.client.util.EventFilterOfTime; 8 | import okble.central.client.GattOperationResult; 9 | import okble.central.client.OkBleEvent; 10 | import okble.central.client.util.OkBleEventFilter; 11 | import okble.central.client.OkBleTask0; 12 | import okble.central.client.Phy; 13 | import okble.central.client.exception.GattOperationFailedException; 14 | import okble.central.client.exception.GattOperationTimeoutException; 15 | import okble.central.client.exception.OkBleException; 16 | import okble.central.client.request.Request; 17 | import okble.central.client.request.SetPreferredPhyRequest; 18 | 19 | final class SetPreferredPhyExecutor extends RequestExecutor { 20 | 21 | @Override 22 | public void executeRequest(ClientOperation client, OkBleTask0 task, Request request0) { 23 | final SetPreferredPhyRequest request = (SetPreferredPhyRequest)request0; 24 | 25 | final int txPhy = PhyMask.value(request.txPhy()); 26 | final int rxPhy = PhyMask.value(request.rxPhy()); 27 | final int phyOptions = request.phyOptions().value(); 28 | 29 | try{ 30 | final long now = System.currentTimeMillis(); 31 | final boolean success = client.doSetPreferredPhy(txPhy, rxPhy, phyOptions); 32 | if(!success){ 33 | throw new GattOperationFailedException("set preferred phy return false!"); 34 | } 35 | final OkBleEventFilter filter = getFilter(now); 36 | final OkBleEvent event = pipeReadNotThrow(filter, GATT_OP_TIMEOUT); 37 | if(event == null){ 38 | throw new GattOperationTimeoutException( "set preferred phy operation timeout!"); 39 | } 40 | final GattOperationResult result = event.data(); 41 | if(result.status() == BluetoothGatt.GATT_SUCCESS){ 42 | final Phy data = result.data(); 43 | task.callOnSuccess(data, false ); 44 | }else{ 45 | throw new GattOperationFailedException("set preferred phy operation failed!"); 46 | } 47 | }catch (OkBleException ex){ 48 | task.callOnFailed(ex, false); 49 | }catch (Exception ex2){ 50 | task.callOnFailed(new OkBleException(ex2), false); 51 | } 52 | 53 | } 54 | 55 | 56 | private static OkBleEventFilter getFilter(final long time){ 57 | final OkBleEventFilter filter = new EventFilterOfTime(time, OkBleEvent.SET_PREFERRED_PHY); 58 | return filter; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/executor/DisableNotificationExecutor.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.executor; 2 | 3 | import android.bluetooth.BluetoothGatt; 4 | 5 | import okble.central.client.ClientOperation; 6 | import okble.central.client.util.EventFilters; 7 | import okble.central.client.GattOperationResult; 8 | import okble.central.client.OkBleEvent; 9 | import okble.central.client.util.OkBleEventFilter; 10 | import okble.central.client.OkBleTask0; 11 | import okble.central.client.exception.GattOperationFailedException; 12 | import okble.central.client.exception.GattOperationTimeoutException; 13 | import okble.central.client.exception.OkBleException; 14 | import okble.central.client.request.DisableNotificationRequest; 15 | import okble.central.client.request.Request; 16 | 17 | import java.util.UUID; 18 | 19 | final class DisableNotificationExecutor extends RequestExecutor { 20 | 21 | @Override 22 | public void executeRequest(ClientOperation client, OkBleTask0 task, Request request0) { 23 | final DisableNotificationRequest request = (DisableNotificationRequest)request0; 24 | final UUID service = request.service(); 25 | final UUID characteristic = request.characteristic(); 26 | try{ 27 | final long now = System.currentTimeMillis(); 28 | final boolean success = client.doDisableNotification(service, characteristic); 29 | 30 | if(!success){ 31 | throw new GattOperationFailedException( "write descriptor return false!"); 32 | } 33 | 34 | final OkBleEventFilter filter = getFilter(now, service, characteristic, CCCD_UUID); 35 | final OkBleEvent event = pipeReadNotThrow(filter, GATT_OP_TIMEOUT); 36 | if(event == null){ 37 | throw new GattOperationTimeoutException( "write descriptor operation timeout!"); 38 | } 39 | final GattOperationResult result = event.data(); 40 | if(result.status() == BluetoothGatt.GATT_SUCCESS){ 41 | task.callOnSuccess(null, false); 42 | }else{ 43 | throw new GattOperationFailedException( "write descriptor operation failed!"); 44 | } 45 | }catch (OkBleException ex){ 46 | task.callOnFailed(ex, false); 47 | }catch (Exception ex2){ 48 | task.callOnFailed(new OkBleException(ex2), false); 49 | } 50 | } 51 | 52 | private static OkBleEventFilter getFilter(final long time, final UUID service, final UUID characteristic, final UUID descriptor ){ 53 | final OkBleEventFilter filter1 = EventFilters.getFilter(time, OkBleEvent.WRITE_DESCRIPTOR); 54 | final OkBleEventFilter filter2 = EventFilters.getFilter(OkBleEvent.WRITE_DESCRIPTOR,service,characteristic,descriptor) ; 55 | final OkBleEventFilter filter = EventFilters.andFilter(filter1, filter2); 56 | return filter; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/util/Hex.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.util; 2 | 3 | public final class Hex { 4 | 5 | public static String toString(final byte[] data) { 6 | if(data == null){ 7 | return null; 8 | } 9 | return new String(encodeHex(data)); 10 | } 11 | 12 | public static String toString(final byte[] data, final char delimiter) { 13 | if(data == null){ 14 | return null; 15 | } 16 | final char[] chars = encodeHex(data); 17 | final int len = chars.length; 18 | final StringBuilder sb = new StringBuilder(len * 2 - 1); 19 | 20 | for(int i=0; i>> 4]; 58 | out[j++] = toDigits[0x0F & data[i]]; 59 | } 60 | return out; 61 | } 62 | 63 | protected static char[] encodeHex(final byte[] data, final int offset, final int length, final char[] toDigits) { 64 | final int l = data.length; 65 | final char[] out = new char[length << 1]; 66 | for (int i = offset, j = 0; i < length; i++) { 67 | out[j++] = toDigits[(0xF0 & data[i]) >>> 4]; 68 | out[j++] = toDigits[0x0F & data[i]]; 69 | } 70 | return out; 71 | } 72 | 73 | private static final char[] DIGITS_LOWER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; 74 | 75 | private static final char[] DIGITS_UPPER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; 76 | 77 | private Hex(){} 78 | } 79 | -------------------------------------------------------------------------------- /app/src/main/java/okble/demo/ui/devices/DevicesAdapter.java: -------------------------------------------------------------------------------- 1 | package okble.demo.ui.devices; 2 | 3 | import android.text.TextUtils; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.TextView; 8 | 9 | import androidx.annotation.NonNull; 10 | import androidx.lifecycle.LiveData; 11 | import androidx.recyclerview.widget.RecyclerView; 12 | 13 | import java.util.List; 14 | 15 | import butterknife.BindView; 16 | import butterknife.ButterKnife; 17 | import okble.central.scanner.BleScanResult; 18 | import okble.demo.R; 19 | 20 | 21 | public final class DevicesAdapter extends RecyclerView.Adapter { 22 | 23 | private OnItemClickListener onItemClickListener; 24 | 25 | @FunctionalInterface 26 | public interface OnItemClickListener { 27 | void onItemClick(final BleScanResult device); 28 | } 29 | 30 | public void setOnItemClickListener(final OnItemClickListener listener) { 31 | onItemClickListener = listener; 32 | } 33 | 34 | private List devices; 35 | public DevicesAdapter(@NonNull final DevicesFragment fragment, 36 | @NonNull final LiveData> devicesLiveData) { 37 | devicesLiveData.observe(fragment, newDevices -> { 38 | devices = newDevices; 39 | notifyDataSetChanged(); 40 | }); 41 | } 42 | 43 | @NonNull 44 | @Override 45 | public ViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) { 46 | final View layoutView = LayoutInflater.from(parent.getContext()) 47 | .inflate(R.layout.list_item_device, parent, false); 48 | return new ViewHolder(layoutView); 49 | } 50 | 51 | @Override 52 | public void onBindViewHolder(@NonNull final ViewHolder holder, final int position) { 53 | final BleScanResult device = devices.get(position); 54 | final String deviceName = device.device().getName(); 55 | if (!TextUtils.isEmpty(deviceName)){ 56 | holder.deviceName.setText(deviceName); 57 | }else{ 58 | holder.deviceName.setText("N/A"); 59 | } 60 | holder.deviceAddress.setText(device.device().getAddress()); 61 | 62 | holder.itemView.setOnClickListener(v -> { 63 | if (onItemClickListener != null) { 64 | onItemClickListener.onItemClick(devices.get(position)); 65 | } 66 | }); 67 | } 68 | 69 | @Override 70 | public long getItemId(final int position) { 71 | return devices.get(position).hashCode(); 72 | } 73 | 74 | @Override 75 | public int getItemCount() { 76 | return devices != null ? devices.size() : 0; 77 | } 78 | 79 | public boolean isEmpty() { 80 | return getItemCount() == 0; 81 | } 82 | 83 | final static class ViewHolder extends RecyclerView.ViewHolder { 84 | @BindView(R.id.device_address) 85 | TextView deviceAddress; 86 | @BindView(R.id.device_name) 87 | TextView deviceName; 88 | 89 | private ViewHolder(@NonNull final View view) { 90 | super(view); 91 | ButterKnife.bind(this, view); 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/util/GattUtils.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.util; 2 | 3 | import android.bluetooth.BluetoothGatt; 4 | 5 | import java.lang.reflect.Method; 6 | 7 | public final class GattUtils { 8 | 9 | public static void safeDisconnect(final BluetoothGatt gatt){ 10 | if(gatt != null){ 11 | try{ 12 | gatt.disconnect(); 13 | }catch (Exception ex){ 14 | 15 | } 16 | } 17 | } 18 | 19 | public static void safeClose(final BluetoothGatt gatt){ 20 | if(gatt != null){ 21 | try{ 22 | gatt.close(); 23 | }catch (Exception ex){ 24 | 25 | } 26 | } 27 | } 28 | 29 | 30 | public static boolean refresh(final BluetoothGatt gatt){ 31 | try{ 32 | final Method method = refreshMethod(); 33 | final boolean val = (boolean)method.invoke(gatt); 34 | return val; 35 | }catch (Exception ex){ 36 | ex.printStackTrace(); 37 | } 38 | return false; 39 | } 40 | 41 | 42 | private static Method sRefreshMethod = null; 43 | private static Method refreshMethod() throws NoSuchMethodException { 44 | if(sRefreshMethod == null){ 45 | final Method method = BluetoothGatt.class.getMethod("refresh"); 46 | sRefreshMethod = method; 47 | } 48 | return sRefreshMethod; 49 | } 50 | 51 | 52 | 53 | 54 | public static boolean requestLeConnectionUpdate(final BluetoothGatt gatt, 55 | final int minConnectionInterval, final int maxConnectionInterval, 56 | final int slaveLatency, final int supervisionTimeout, 57 | final int minConnectionEventLen, final int maxConnectionEventLen){ 58 | try{ 59 | final Method method = requestLeConnectionUpdateMethod(); 60 | final boolean val = (boolean)method.invoke(gatt, 61 | minConnectionInterval, maxConnectionInterval, 62 | slaveLatency, supervisionTimeout, 63 | minConnectionEventLen, maxConnectionEventLen); 64 | return val; 65 | }catch (Exception ex){ 66 | ex.printStackTrace(); 67 | } 68 | return false; 69 | } 70 | 71 | 72 | private static Method sRequestLeConnectionUpdateMethod = null; 73 | private static Method requestLeConnectionUpdateMethod() throws NoSuchMethodException { 74 | if(sRequestLeConnectionUpdateMethod == null){ 75 | final Class[] clazzArr = {int.class,int.class,int.class,int.class,int.class,int.class}; 76 | final Method method = BluetoothGatt.class.getMethod("requestLeConnectionUpdate", clazzArr); 77 | sRequestLeConnectionUpdateMethod = method; 78 | } 79 | return sRequestLeConnectionUpdateMethod; 80 | } 81 | 82 | 83 | private GattUtils(){} 84 | } 85 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/request/UpdateConnectionParameterRequest.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.request; 2 | 3 | 4 | final class UpdateConnectionParameterRequest extends Request{ 5 | 6 | int minConnectionInterval; 7 | int maxConnectionInterval; 8 | int slaveLatency; 9 | int supervisionTimeout; 10 | int minConnectionEventLen; 11 | int maxConnectionEventLen; 12 | private UpdateConnectionParameterRequest(final Builder builder){ 13 | super(Type.UpdateConnectionParameterRequest); 14 | this.minConnectionInterval = builder.minConnectionInterval; 15 | this.maxConnectionInterval = builder.maxConnectionInterval; 16 | this.slaveLatency = builder.slaveLatency; 17 | this.supervisionTimeout = builder.supervisionTimeout; 18 | this.minConnectionEventLen = builder.minConnectionEventLen; 19 | this.maxConnectionEventLen = builder.maxConnectionEventLen; 20 | } 21 | 22 | public int minConnectionInterval() { 23 | return minConnectionInterval; 24 | } 25 | 26 | public int maxConnectionInterval() { 27 | return maxConnectionInterval; 28 | } 29 | 30 | public int slaveLatency() { 31 | return slaveLatency; 32 | } 33 | 34 | public int supervisionTimeout() { 35 | return supervisionTimeout; 36 | } 37 | 38 | public int minConnectionEventLen() { 39 | return minConnectionEventLen; 40 | } 41 | 42 | public int maxConnectionEventLen() { 43 | return maxConnectionEventLen; 44 | } 45 | 46 | public final static class Builder { 47 | int minConnectionInterval; 48 | int maxConnectionInterval; 49 | int slaveLatency; 50 | int supervisionTimeout; 51 | int minConnectionEventLen; 52 | int maxConnectionEventLen; 53 | 54 | public Builder minConnectionInterval(int value) { 55 | this.minConnectionInterval = value; 56 | return this; 57 | } 58 | 59 | public Builder maxConnectionInterval(int value) { 60 | this.maxConnectionInterval = value; 61 | return this; 62 | } 63 | 64 | public Builder slaveLatency(int value) { 65 | this.slaveLatency = value; 66 | return this; 67 | } 68 | 69 | public Builder supervisionTimeout(int value) { 70 | this.supervisionTimeout = value; 71 | return this; 72 | } 73 | 74 | public Builder minConnectionEventLen(int value) { 75 | this.minConnectionEventLen = value; 76 | return this; 77 | } 78 | 79 | public Builder maxConnectionEventLen(int value) { 80 | this.maxConnectionEventLen = value; 81 | return this; 82 | } 83 | 84 | public UpdateConnectionParameterRequest build() { 85 | return new UpdateConnectionParameterRequest(this); 86 | } 87 | } 88 | 89 | @Override 90 | public UpdateConnectionParameterRequest getThis() { 91 | return this; 92 | } 93 | } -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/executor/ReadCharacteristicExecutor.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.executor; 2 | 3 | import android.bluetooth.BluetoothGatt; 4 | 5 | import okble.central.client.CharacteristicData; 6 | import okble.central.client.ClientOperation; 7 | import okble.central.client.util.EventFilters; 8 | import okble.central.client.GattOperationResult; 9 | import okble.central.client.OkBleEvent; 10 | import okble.central.client.util.OkBleEventFilter; 11 | import okble.central.client.OkBleTask0; 12 | import okble.central.client.exception.GattOperationFailedException; 13 | import okble.central.client.exception.GattOperationTimeoutException; 14 | import okble.central.client.exception.OkBleException; 15 | import okble.central.client.request.ReadCharacteristicRequest; 16 | import okble.central.client.request.Request; 17 | 18 | import java.util.UUID; 19 | 20 | public final class ReadCharacteristicExecutor extends RequestExecutor { 21 | 22 | @Override 23 | public void executeRequest(ClientOperation client, OkBleTask0 task, Request request0) { 24 | final ReadCharacteristicRequest request = (ReadCharacteristicRequest)request0; 25 | final UUID service = request.service(); 26 | final UUID characteristic = request.characteristic(); 27 | 28 | try{ 29 | final long now = System.currentTimeMillis(); 30 | final boolean success = client.doReadCharacteristic(service, characteristic); 31 | if(!success){ 32 | throw new GattOperationFailedException( "read characteristic return false!"); 33 | } 34 | final OkBleEventFilter filter = getFilter(now, service, characteristic); 35 | final OkBleEvent event = pipeReadNotThrow(filter, GATT_OP_TIMEOUT); 36 | if(event == null){ 37 | throw new GattOperationTimeoutException( "read characteristic operation timeout!"); 38 | } 39 | final GattOperationResult result = event.data(); 40 | if(result.status() == BluetoothGatt.GATT_SUCCESS){ 41 | final CharacteristicData characteristicData = result.data(); 42 | final byte[] packet = characteristicData.data(); 43 | task.callOnSuccess(packet, false); 44 | }else{ 45 | throw new GattOperationFailedException("read characteristic operation failed!"); 46 | } 47 | }catch (OkBleException ex){ 48 | task.callOnFailed(ex, false); 49 | }catch (Exception ex2){ 50 | task.callOnFailed(new OkBleException(ex2), false); 51 | } 52 | } 53 | 54 | 55 | 56 | private static OkBleEventFilter getFilter(final long time, final UUID service, final UUID characteristic ){ 57 | final OkBleEventFilter filter1 = EventFilters.getFilter(time, OkBleEvent.READ_CHARACTERISTIC); 58 | final OkBleEventFilter filter2 = EventFilters.getFilter(OkBleEvent.READ_CHARACTERISTIC,service,characteristic) ; 59 | final OkBleEventFilter filter = EventFilters.andFilter(filter1, filter2); 60 | return filter; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/util/EventFilters.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.util; 2 | 3 | 4 | import android.bluetooth.BluetoothGatt; 5 | 6 | import java.util.UUID; 7 | 8 | import okble.central.client.CharacteristicData; 9 | import okble.central.client.DescriptorData; 10 | import okble.central.client.GattOperationResult; 11 | import okble.central.client.OkBleEvent; 12 | 13 | public final class EventFilters { 14 | 15 | public static OkBleEventFilter getFilter(final long time, final int... events){ 16 | final OkBleEventFilter filter = new EventFilterOfTime(time, events); 17 | return filter; 18 | } 19 | 20 | 21 | public static OkBleEventFilter andFilter(OkBleEventFilter... filters){ 22 | return new AndFilter(filters); 23 | } 24 | 25 | 26 | public static OkBleEventFilter getFilter(final int eventValue, final UUID service, final UUID characteristic){ 27 | final OkBleEventFilter filter = new OkBleEventFilter(){ 28 | @Override 29 | public boolean accept(OkBleEvent event) { 30 | if(event == null){ 31 | return false; 32 | } 33 | if(event.value() == eventValue){ 34 | final GattOperationResult result = event.data(); 35 | if(result.status() == BluetoothGatt.GATT_SUCCESS){ 36 | final CharacteristicData data = result.data(CharacteristicData.class); 37 | if(data != null && 38 | (!service.equals(data.service()) || !characteristic.equals(data.characteristic()))){ 39 | return false; 40 | } 41 | } 42 | } 43 | return true; 44 | } 45 | }; 46 | return filter; 47 | } 48 | 49 | 50 | 51 | 52 | public static OkBleEventFilter getFilter(final int eventValue, final UUID service, final UUID characteristic,final UUID descriptor){ 53 | final OkBleEventFilter filter = new OkBleEventFilter(){ 54 | @Override 55 | public boolean accept(OkBleEvent event) { 56 | if(event == null){ 57 | return false; 58 | } 59 | if(event.value() == eventValue){ 60 | final GattOperationResult result = event.data(); 61 | if(result.status() == BluetoothGatt.GATT_SUCCESS){ 62 | final DescriptorData data = result.data(DescriptorData.class); 63 | if(data != null && 64 | (!service.equals(data.service()) || 65 | !characteristic.equals(data.characteristic()) || 66 | !descriptor.equals(data.descriptor()))){ 67 | return false; 68 | } 69 | } 70 | } 71 | return true; 72 | } 73 | }; 74 | return filter; 75 | } 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | } 84 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/executor/ReadDescriptorExecutor.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.executor; 2 | 3 | import android.bluetooth.BluetoothGatt; 4 | 5 | import okble.central.client.ClientOperation; 6 | import okble.central.client.DescriptorData; 7 | import okble.central.client.util.EventFilters; 8 | import okble.central.client.GattOperationResult; 9 | import okble.central.client.OkBleEvent; 10 | import okble.central.client.util.OkBleEventFilter; 11 | import okble.central.client.OkBleTask0; 12 | import okble.central.client.exception.GattOperationFailedException; 13 | import okble.central.client.exception.GattOperationTimeoutException; 14 | import okble.central.client.exception.OkBleException; 15 | import okble.central.client.request.ReadDescriptorRequest; 16 | import okble.central.client.request.Request; 17 | 18 | import java.util.UUID; 19 | 20 | public final class ReadDescriptorExecutor extends RequestExecutor { 21 | 22 | @Override 23 | public void executeRequest(ClientOperation client, OkBleTask0 task, Request request0) { 24 | final ReadDescriptorRequest request = (ReadDescriptorRequest)request0; 25 | final UUID service = request.service(); 26 | final UUID characteristic = request.characteristic(); 27 | final UUID descriptor = request.descriptor(); 28 | 29 | try{ 30 | final long now = System.currentTimeMillis(); 31 | final boolean success = client.doReadDescriptor(service, characteristic, descriptor); 32 | if(!success){ 33 | throw new GattOperationFailedException( "read descriptor return false!"); 34 | } 35 | final OkBleEventFilter filter = getFilter(now, service, characteristic, descriptor); 36 | final OkBleEvent event = pipeReadNotThrow(filter, GATT_OP_TIMEOUT); 37 | if(event == null){ 38 | throw new GattOperationTimeoutException( "read descriptor operation timeout!"); 39 | } 40 | final GattOperationResult result = event.data(); 41 | if(result.status() == BluetoothGatt.GATT_SUCCESS){ 42 | final DescriptorData descriptorData = result.data(); 43 | final byte[] packet = descriptorData.data(); 44 | task.callOnSuccess(packet, false); 45 | }else{ 46 | throw new GattOperationFailedException( "read descriptor operation failed!"); 47 | } 48 | }catch (OkBleException ex){ 49 | task.callOnFailed(ex, false); 50 | }catch (Exception ex2){ 51 | task.callOnFailed(new OkBleException(ex2), false); 52 | } 53 | } 54 | 55 | 56 | 57 | private static OkBleEventFilter getFilter(final long time, final UUID service, final UUID characteristic, final UUID descriptor ){ 58 | final OkBleEventFilter filter1 = EventFilters.getFilter(time, OkBleEvent.READ_DESCRIPTOR); 59 | final OkBleEventFilter filter2 = EventFilters.getFilter(OkBleEvent.READ_DESCRIPTOR,service,characteristic,descriptor) ; 60 | final OkBleEventFilter filter = EventFilters.andFilter(filter1, filter2); 61 | return filter; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/executor/EnableNotificationExecutor.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.executor; 2 | 3 | import android.bluetooth.BluetoothGatt; 4 | 5 | import okble.central.client.ClientOperation; 6 | import okble.central.client.util.EventFilters; 7 | import okble.central.client.GattOperationResult; 8 | import okble.central.client.NotificationType; 9 | import okble.central.client.OkBleEvent; 10 | import okble.central.client.util.OkBleEventFilter; 11 | import okble.central.client.OkBleTask0; 12 | import okble.central.client.exception.GattOperationFailedException; 13 | import okble.central.client.exception.GattOperationTimeoutException; 14 | import okble.central.client.exception.OkBleException; 15 | import okble.central.client.request.EnableNotificationRequest; 16 | import okble.central.client.request.Request; 17 | 18 | import java.util.UUID; 19 | 20 | final class EnableNotificationExecutor extends RequestExecutor { 21 | 22 | @Override 23 | public void executeRequest(ClientOperation client, OkBleTask0 task, Request request0) { 24 | final EnableNotificationRequest request = (EnableNotificationRequest)request0; 25 | final UUID service = request.service(); 26 | final UUID characteristic = request.characteristic(); 27 | final NotificationType type = request.notificationType(); 28 | try{ 29 | final long now = System.currentTimeMillis(); 30 | final boolean success; 31 | if(type == NotificationType.Indication){ 32 | success = client.doEnableIndication(service, characteristic); 33 | }else { 34 | success = client.doEnableNotification(service, characteristic); 35 | } 36 | if(!success){ 37 | throw new GattOperationFailedException("write descriptor return false!"); 38 | } 39 | 40 | final OkBleEventFilter filter = getFilter(now, service, characteristic, CCCD_UUID); 41 | final OkBleEvent event = pipeReadNotThrow(filter, GATT_OP_TIMEOUT); 42 | if(event == null){ 43 | throw new GattOperationTimeoutException( "write descriptor operation timeout!"); 44 | } 45 | final GattOperationResult result = event.data(); 46 | if(result.status() == BluetoothGatt.GATT_SUCCESS){ 47 | task.callOnSuccess(null, false); 48 | }else{ 49 | throw new GattOperationFailedException( "write descriptor operation failed!"); 50 | } 51 | }catch (OkBleException ex){ 52 | task.callOnFailed(ex, false); 53 | }catch (Exception ex2){ 54 | task.callOnFailed(new OkBleException(ex2), false); 55 | } 56 | } 57 | 58 | private static OkBleEventFilter getFilter(final long time, final UUID service, final UUID characteristic, final UUID descriptor ){ 59 | final OkBleEventFilter filter1 = EventFilters.getFilter(time, OkBleEvent.WRITE_DESCRIPTOR); 60 | final OkBleEventFilter filter2 = EventFilters.getFilter(OkBleEvent.WRITE_DESCRIPTOR,service,characteristic,descriptor) ; 61 | final OkBleEventFilter filter = EventFilters.andFilter(filter1, filter2); 62 | return filter; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /app/src/main/java/okble/demo/ui/device2/ServiceResolver.java: -------------------------------------------------------------------------------- 1 | package okble.demo.ui.device2; 2 | 3 | import android.bluetooth.BluetoothGattService; 4 | import android.text.TextUtils; 5 | 6 | import java.util.HashMap; 7 | 8 | import okble.demo.util.UuidUtils; 9 | 10 | public final class ServiceResolver { 11 | 12 | 13 | public static String resolveType(BluetoothGattService service){ 14 | final int type = service.getType(); 15 | if(type == BluetoothGattService.SERVICE_TYPE_PRIMARY){ 16 | return "PRIMARY SERVICE"; 17 | }else if(type == BluetoothGattService.SERVICE_TYPE_SECONDARY){ 18 | return "SECONDARY SERVICE"; 19 | } 20 | return "Unknown Service Type"; 21 | } 22 | 23 | public static String resolveName(BluetoothGattService service){ 24 | final String name = sServices.get(service.getUuid().toString().toLowerCase()); 25 | return TextUtils.isEmpty(name) ? "Unknown Service" : name; 26 | } 27 | 28 | public static String resolveUuid(BluetoothGattService service){ 29 | final String val = UuidUtils.getShortUuid(service.getUuid()); 30 | return TextUtils.isEmpty(val) ? service.getUuid().toString() : ("0x" + val); 31 | } 32 | 33 | private static HashMap sServices = new HashMap(); 34 | static { 35 | sServices.put("00001811-0000-1000-8000-00805f9b34fb", "Alert Notification Service"); 36 | sServices.put("0000180f-0000-1000-8000-00805f9b34fb", "Battery Service"); 37 | sServices.put("00001810-0000-1000-8000-00805f9b34fb", "Blood Pressure"); 38 | sServices.put("00001805-0000-1000-8000-00805f9b34fb", "Current Time Service"); 39 | sServices.put("00001818-0000-1000-8000-00805f9b34fb", "Cycling Power"); 40 | sServices.put("00001816-0000-1000-8000-00805f9b34fb", "Cycling Speed and Cadence"); 41 | sServices.put("0000180a-0000-1000-8000-00805f9b34fb", "Device Information"); 42 | sServices.put("00001800-0000-1000-8000-00805f9b34fb", "Generic Access"); 43 | sServices.put("00001801-0000-1000-8000-00805f9b34fb", "Generic Attribute"); 44 | sServices.put("00001808-0000-1000-8000-00805f9b34fb", "Glucose"); 45 | sServices.put("00001809-0000-1000-8000-00805f9b34fb", "Health Thermometer"); 46 | sServices.put("0000180d-0000-1000-8000-00805f9b34fb", "Heart Rate"); 47 | sServices.put("00001812-0000-1000-8000-00805f9b34fb", "Human Interface Device"); 48 | sServices.put("00001802-0000-1000-8000-00805f9b34fb", "Immediate Alert"); 49 | sServices.put("00001803-0000-1000-8000-00805f9b34fb", "Link Loss"); 50 | sServices.put("00001819-0000-1000-8000-00805f9b34fb", "Location and Navigation"); 51 | sServices.put("00001807-0000-1000-8000-00805f9b34fb", "Next DST Change Service"); 52 | sServices.put("0000180e-0000-1000-8000-00805f9b34fb", "Phone Alert Status Service"); 53 | sServices.put("00001806-0000-1000-8000-00805f9b34fb", "Reference Time Update Service"); 54 | sServices.put("00001814-0000-1000-8000-00805f9b34fb", "Running Speed and Cadence"); 55 | sServices.put("00001813-0000-1000-8000-00805f9b34fb", "Scan Parameters"); 56 | sServices.put("00001804-0000-1000-8000-00805f9b34fb", "Tx Power"); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /app/src/androidTest/java/okble/demo/OKBleScannerTest.java: -------------------------------------------------------------------------------- 1 | package okble.demo; 2 | 3 | import android.content.Context; 4 | import android.os.SystemClock; 5 | import android.util.Log; 6 | 7 | import androidx.test.ext.junit.runners.AndroidJUnit4; 8 | import androidx.test.platform.app.InstrumentationRegistry; 9 | 10 | import org.junit.FixMethodOrder; 11 | import org.junit.Test; 12 | import org.junit.runner.RunWith; 13 | import org.junit.runners.MethodSorters; 14 | 15 | import java.util.ArrayList; 16 | import java.util.concurrent.TimeUnit; 17 | 18 | import okble.central.scanner.BleScanListener; 19 | import okble.central.scanner.BleScanResult; 20 | import okble.central.scanner.OkBleScanner; 21 | 22 | import static org.junit.Assert.assertNotNull; 23 | import static org.junit.Assert.assertTrue; 24 | 25 | /** 26 | * Instrumented test, which will execute on an Android device. 27 | * 28 | * @see Testing documentation 29 | */ 30 | @RunWith(AndroidJUnit4.class) 31 | @FixMethodOrder(MethodSorters.JVM) 32 | public class OKBleScannerTest { 33 | private final static String TAG = ":" + OKBleScannerTest.class.getSimpleName(); 34 | 35 | private final static String MY_DEVICE_ADDRESS = "4C:59:9A:28:92:0A"; 36 | private final static String HUAWEI_DEVICE_ADDRESS = "20:DA:22:1D:64:A0"; 37 | private final static String TESTED_DEVICE_ADDRESS = HUAWEI_DEVICE_ADDRESS; 38 | 39 | 40 | @Test 41 | public void testAsyncScan(){ 42 | final OkBleScanner scanner = OkBleScanner.getDefault(); 43 | final ArrayList list = new ArrayList(); 44 | scanner.addScanListener(new BleScanListener() { 45 | @Override 46 | public void onScanStart(OkBleScanner scanner) { 47 | Log.d(TAG, "onScanStart"); 48 | } 49 | 50 | @Override 51 | public void onScanIdle(OkBleScanner scanner) { 52 | Log.d(TAG, "onScanIdle"); 53 | } 54 | 55 | @Override 56 | public void onScanning(OkBleScanner scanner) { 57 | Log.d(TAG, "onScanning"); 58 | } 59 | 60 | @Override 61 | public void onScanResult(OkBleScanner scanner, BleScanResult result) { 62 | Log.d(TAG, "onScanResult:" + result); 63 | list.add(result); 64 | } 65 | 66 | @Override 67 | public void onScanComplete(OkBleScanner scanner, int code) { 68 | Log.d(TAG, "onScanComplete code:" + code); 69 | scanner.removeScanListener(this); 70 | } 71 | }); 72 | final Context ctx = InstrumentationRegistry.getInstrumentation().getTargetContext(); 73 | scanner.startScan(ctx); 74 | SystemClock.sleep(80_000L); 75 | scanner.stopScan(); 76 | assertTrue(list.size()>0); 77 | } 78 | 79 | 80 | @Test 81 | public void testSyncScan(){ 82 | final OkBleScanner scanner = OkBleScanner.getDefault(); 83 | final Context ctx = InstrumentationRegistry.getInstrumentation().getTargetContext(); 84 | 85 | final BleScanResult result = scanner.scanForResult(ctx, TESTED_DEVICE_ADDRESS, 60, TimeUnit.SECONDS); 86 | assertNotNull(result); 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 29 5 | buildToolsVersion "29.0.3" 6 | 7 | defaultConfig { 8 | applicationId "okble.demo" 9 | minSdkVersion 21 10 | targetSdkVersion 29 11 | versionCode 1 12 | versionName "1.0.1" 13 | 14 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 15 | } 16 | 17 | 18 | signingConfigs { 19 | release { 20 | storeFile file('./keys/okble.jks') 21 | storePassword 'okble2020' 22 | keyAlias 'okble' 23 | keyPassword 'okble2020' 24 | } 25 | } 26 | 27 | 28 | buildTypes { 29 | release { 30 | minifyEnabled false 31 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 32 | signingConfig signingConfigs.release 33 | } 34 | debug { 35 | minifyEnabled false 36 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 37 | signingConfig signingConfigs.release 38 | } 39 | } 40 | 41 | 42 | compileOptions { 43 | targetCompatibility JavaVersion.VERSION_1_8 44 | sourceCompatibility JavaVersion.VERSION_1_8 45 | } 46 | 47 | 48 | applicationVariants.all { variant -> 49 | variant.outputs.all { 50 | def buildType = variant.buildType.name 51 | if("release".equalsIgnoreCase(buildType)){ 52 | outputFileName = "OkBleDemo_release_${variant.versionName}_${variant.versionCode}_${build_time()}.apk" 53 | }else if("debug".equalsIgnoreCase(buildType)){ 54 | outputFileName = "OkBleDemo_debug_${variant.versionName}_${variant.versionCode}_${build_time()}.apk" 55 | } 56 | } 57 | } 58 | 59 | 60 | 61 | 62 | 63 | } 64 | def build_time() { 65 | return new Date().format("yyyy-MMdd-HHmm", TimeZone.getDefault()) 66 | } 67 | def use_lib = false 68 | dependencies { 69 | // implementation fileTree(dir: "libs", include: ["*.jar"]) 70 | implementation 'androidx.appcompat:appcompat:1.2.0' 71 | implementation 'com.google.android.material:material:1.2.1' 72 | implementation 'androidx.constraintlayout:constraintlayout:2.0.4' 73 | implementation 'androidx.navigation:navigation-fragment:2.3.2' 74 | implementation 'androidx.navigation:navigation-ui:2.3.2' 75 | implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' 76 | implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.0.0' 77 | implementation 'androidx.legacy:legacy-support-core-ui:1.0.0' 78 | testImplementation 'junit:junit:4.12' 79 | androidTestImplementation 'androidx.test.ext:junit:1.1.2' 80 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' 81 | 82 | implementation 'com.jakewharton:butterknife:10.2.1' 83 | annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.1' 84 | 85 | if(use_lib){ 86 | implementation files('libs/OkBle.jar') 87 | }else{ 88 | implementation project(':OkBle') 89 | } 90 | implementation "pub.devrel:easypermissions:2.0.1" 91 | implementation 'com.qmuiteam:qmui:1.4.4' 92 | 93 | implementation 'com.github.okbean:OkBleScanner:1.3.0' 94 | 95 | } -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/request/FixedSizePacketSenderFactory.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.request; 2 | 3 | import okble.central.client.PacketSender; 4 | 5 | final class FixedSizePacketSenderFactory implements PacketSender.Factory{ 6 | 7 | private final byte[] data; 8 | private final int offset; 9 | private final int length; 10 | private final int maxPacketSize; 11 | 12 | private FixedSizePacketSenderFactory(final byte[] data, final int offset, final int length, final int maxPacketSize){ 13 | if(data == null || data.length <= 0){ 14 | throw new IllegalArgumentException("packet can not be null or zero length!"); 15 | } 16 | if(offset < 0 || offset >= data.length){ 17 | throw new IllegalArgumentException(String.format("offset:%s out of bound!", offset)); 18 | } 19 | if((offset + length) > data.length){ 20 | throw new IllegalArgumentException(String.format("(offset:%s + length:%s) more than data.length:%s!", offset, length,data.length)); 21 | } 22 | if(maxPacketSize < 1){ 23 | throw new IllegalArgumentException("maxPacketSize can not be less than 1!"); 24 | } 25 | this.data = data; 26 | this.offset = offset; 27 | this.length = length; 28 | this.maxPacketSize = maxPacketSize; 29 | } 30 | 31 | public static FixedSizePacketSenderFactory from(final byte[] data, final int offset, final int length, final int maxPacketSize){ 32 | return new FixedSizePacketSenderFactory(data, offset, length, maxPacketSize); 33 | } 34 | 35 | public static FixedSizePacketSenderFactory from(final byte[] data, final int offset, final int length){ 36 | return new FixedSizePacketSenderFactory(data, offset, length, 20); 37 | } 38 | 39 | public static FixedSizePacketSenderFactory from(final byte[] data, final int maxPacketSize){ 40 | return new FixedSizePacketSenderFactory(data, 0, data.length, maxPacketSize); 41 | } 42 | 43 | public static FixedSizePacketSenderFactory from(final byte[] data){ 44 | return new FixedSizePacketSenderFactory(data, 0, data.length, 20); 45 | } 46 | 47 | @Override 48 | public PacketSender newPacketSender() { 49 | return new PacketSender(){ 50 | private int index = 0; 51 | @Override 52 | public boolean hasNext() { 53 | final boolean val = index * maxPacketSize < length; 54 | return val; 55 | } 56 | 57 | @Override 58 | public byte[] nextPacket() { 59 | if(hasNext()){ 60 | final int remain = length - (maxPacketSize * index); 61 | final int size = remain < maxPacketSize ? remain : maxPacketSize; 62 | final byte[] val = new byte[size]; 63 | System.arraycopy(data, (offset + (maxPacketSize * index)), val, 0,size); 64 | index ++; 65 | return val; 66 | } 67 | return null; 68 | } 69 | 70 | @Override 71 | public boolean isDeterminate() { 72 | return true; 73 | } 74 | 75 | @Override 76 | public int size() { 77 | return length; 78 | } 79 | 80 | @Override 81 | public void reset() { 82 | this.index = 0; 83 | } 84 | }; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /OkBle/src/main/java/okble/central/client/executor/WriteDescriptorExecutor.java: -------------------------------------------------------------------------------- 1 | package okble.central.client.executor; 2 | 3 | import android.bluetooth.BluetoothGatt; 4 | 5 | import okble.central.client.ClientOperation; 6 | import okble.central.client.util.EventFilters; 7 | import okble.central.client.GattOperationResult; 8 | import okble.central.client.OkBleEvent; 9 | import okble.central.client.util.OkBleEventFilter; 10 | import okble.central.client.OkBleTask0; 11 | import okble.central.client.PacketSender; 12 | import okble.central.client.exception.GattOperationFailedException; 13 | import okble.central.client.exception.GattOperationTimeoutException; 14 | import okble.central.client.exception.OkBleException; 15 | import okble.central.client.request.Request; 16 | import okble.central.client.request.WriteDescriptorRequest; 17 | 18 | import java.util.UUID; 19 | 20 | final class WriteDescriptorExecutor extends RequestExecutor { 21 | 22 | @Override 23 | public void executeRequest(ClientOperation client, OkBleTask0 task, Request request0) { 24 | final WriteDescriptorRequest request = (WriteDescriptorRequest)request0; 25 | final UUID service = request.service(); 26 | final UUID characteristic = request.characteristic(); 27 | final UUID descriptor = request.descriptor(); 28 | final PacketSender sender = request.packetSenderFactory().newPacketSender(); 29 | while (sender.hasNext()){ 30 | try{ 31 | final byte[] packet = sender.nextPacket(); 32 | checkPacket(packet); 33 | final long now = System.currentTimeMillis(); 34 | final boolean success = client.doWriteDescriptor(service, characteristic, descriptor, packet); 35 | if(!success){ 36 | throw new GattOperationFailedException( "write descriptor return false!"); 37 | } 38 | final OkBleEventFilter filter = getFilter(now, service, characteristic, descriptor); 39 | final OkBleEvent event = pipeReadNotThrow(filter, GATT_OP_TIMEOUT); 40 | if(event == null){ 41 | throw new GattOperationTimeoutException( "write descriptor operation timeout!"); 42 | } 43 | final GattOperationResult result = event.data(); 44 | if(result.status() == BluetoothGatt.GATT_SUCCESS){ 45 | continue; 46 | }else{ 47 | throw new GattOperationFailedException( "write descriptor operation failed!"); 48 | } 49 | }catch (OkBleException ex){ 50 | task.callOnFailed(ex, false); 51 | break; 52 | }catch (Exception ex2){ 53 | task.callOnFailed(new OkBleException(ex2), false); 54 | break; 55 | } 56 | } 57 | task.callOnSuccess(null, false ); 58 | try{ 59 | sender.reset(); 60 | }catch (Exception ex){ 61 | } 62 | } 63 | 64 | 65 | 66 | private static void checkPacket(final byte[] packet) throws OkBleException { 67 | if(packet == null || packet.length == 0){ 68 | throw new OkBleException("packet is null or zero length!"); 69 | } 70 | } 71 | 72 | private static OkBleEventFilter getFilter(final long time, final UUID service, final UUID characteristic, final UUID descriptor ){ 73 | final OkBleEventFilter filter1 = EventFilters.getFilter(time, OkBleEvent.WRITE_DESCRIPTOR); 74 | final OkBleEventFilter filter2 = EventFilters.getFilter(OkBleEvent.WRITE_DESCRIPTOR,service,characteristic,descriptor) ; 75 | final OkBleEventFilter filter = EventFilters.andFilter(filter1, filter2); 76 | return filter; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | xmlns:android 14 | 15 | ^$ 16 | 17 | 18 | 19 |
20 |
21 | 22 | 23 | 24 | xmlns:.* 25 | 26 | ^$ 27 | 28 | 29 | BY_NAME 30 | 31 |
32 |
33 | 34 | 35 | 36 | .*:id 37 | 38 | http://schemas.android.com/apk/res/android 39 | 40 | 41 | 42 |
43 |
44 | 45 | 46 | 47 | .*:name 48 | 49 | http://schemas.android.com/apk/res/android 50 | 51 | 52 | 53 |
54 |
55 | 56 | 57 | 58 | name 59 | 60 | ^$ 61 | 62 | 63 | 64 |
65 |
66 | 67 | 68 | 69 | style 70 | 71 | ^$ 72 | 73 | 74 | 75 |
76 |
77 | 78 | 79 | 80 | .* 81 | 82 | ^$ 83 | 84 | 85 | BY_NAME 86 | 87 |
88 |
89 | 90 | 91 | 92 | .* 93 | 94 | http://schemas.android.com/apk/res/android 95 | 96 | 97 | ANDROID_ATTRIBUTE_ORDER 98 | 99 |
100 |
101 | 102 | 103 | 104 | .* 105 | 106 | .* 107 | 108 | 109 | BY_NAME 110 | 111 |
112 |
113 |
114 |
115 |
116 |
--------------------------------------------------------------------------------