diff --git a/lmbluetoothsdk/src/main/java/co/lujun/lmbluetoothsdk/BluetoothLEController.java b/lmbluetoothsdk/src/main/java/co/lujun/lmbluetoothsdk/BluetoothLEController.java index aa02087..ea7a532 100644 --- a/lmbluetoothsdk/src/main/java/co/lujun/lmbluetoothsdk/BluetoothLEController.java +++ b/lmbluetoothsdk/src/main/java/co/lujun/lmbluetoothsdk/BluetoothLEController.java @@ -46,6 +46,7 @@ import java.util.Set; import co.lujun.lmbluetoothsdk.base.BaseController; +import co.lujun.lmbluetoothsdk.base.BluetoothLEListener; import co.lujun.lmbluetoothsdk.base.BluetoothListener; import co.lujun.lmbluetoothsdk.receiver.BlueToothReceiver; import co.lujun.lmbluetoothsdk.service.BluetoothLEService; @@ -59,7 +60,7 @@ public class BluetoothLEController implements BaseController { private BluetoothAdapter mBluetoothAdapter; private BluetoothLeScanner mLEScanner; - private BluetoothListener mBluetoothListener; + private BluetoothLEListener mBluetoothLEListener; private BlueToothReceiver mReceiver; private BluetoothLEService mBluetoothLEService; private ScanSettings mLeSettings; @@ -106,11 +107,11 @@ public BluetoothLEController build(Context context){ * Set bluetooth listener, you can check all bluetooth status and read data with this listener's callback. * @param listener a BluetoothListener */ - public void setBluetoothListener(BluetoothListener listener){ - this.mBluetoothListener = listener; + public void setBluetoothListener(BluetoothLEListener listener){ + this.mBluetoothLEListener = listener; registerReceiver(); if (mBluetoothLEService != null) { -// mBluetoothLEService.setBluetoothListener(mBluetoothListener); + mBluetoothLEService.setBluetoothLEListener(mBluetoothLEListener); } } @@ -118,7 +119,7 @@ public void setBluetoothListener(BluetoothListener listener){ * Register broadcast receiver for current context. */ private void registerReceiver(){ - if (mBluetoothListener == null || mContext == null){ + if (mBluetoothLEListener == null || mContext == null){ return; } @@ -129,7 +130,7 @@ private void registerReceiver(){ filter.addAction(BluetoothDevice.ACTION_FOUND); filter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED); - mReceiver = new BlueToothReceiver(mBluetoothListener); + mReceiver = new BlueToothReceiver(mBluetoothLEListener); mContext.registerReceiver(mReceiver, filter); } @@ -251,17 +252,23 @@ public void startAsServer() {} @Override public void connect(String mac) { - + if (mBluetoothLEService != null) { + mBluetoothLEService.connect(mContext, mBluetoothAdapter.getRemoteDevice(mac)); + } } @Override public void reConnect(String mac) { - + if (mBluetoothLEService != null) { + mBluetoothLEService.reConnect(); + } } @Override public void disconnect() { - + if (mBluetoothLEService != null) { + mBluetoothLEService.disConnect(); + } } @Override @@ -296,8 +303,8 @@ public BluetoothDevice getConnectedDevice() { new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) { - if (mBluetoothListener != null) { - mBluetoothListener.onActionDeviceFound(device); + if (mBluetoothLEListener != null) { + mBluetoothLEListener.onActionDeviceFound(device); } } @@ -310,8 +317,8 @@ private class CBTScanCallback extends ScanCallback { @Override public void onScanResult(int callbackType, ScanResult result) { super.onScanResult(callbackType, result); - if (mBluetoothListener != null) { - mBluetoothListener.onActionDeviceFound(result.getDevice()); + if (mBluetoothLEListener != null) { + mBluetoothLEListener.onActionDeviceFound(result.getDevice()); } } diff --git a/lmbluetoothsdk/src/main/java/co/lujun/lmbluetoothsdk/base/BaseListener.java b/lmbluetoothsdk/src/main/java/co/lujun/lmbluetoothsdk/base/BaseListener.java new file mode 100644 index 0000000..526f505 --- /dev/null +++ b/lmbluetoothsdk/src/main/java/co/lujun/lmbluetoothsdk/base/BaseListener.java @@ -0,0 +1,78 @@ +/* + * The MIT License (MIT) + + * Copyright (c) 2015 LinkMob.cc + + * Contributors: lujun + + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package co.lujun.lmbluetoothsdk.base; + +import android.bluetooth.BluetoothDevice; + +/** + * Author: lujun(http://blog.lujun.co) + * Date: 2016-1-15 10:53 + */ +public interface BaseListener { + + /** + * Callback when bluetooth power state changed. + * + * @param preState previous power state + * @param state current power state + * Possible values are STATE_OFF, STATE_TURNING_ON, + * STATE_ON, STATE_TURNING_OFF in {@link android.bluetooth.BluetoothAdapter} class. + */ + void onActionStateChanged(int preState, int state); + + /** + * Callback when local Bluetooth adapter discovery process state changed. + * @param discoveryState the state of local Bluetooth adapter discovery process. + * Possible values are ACTION_DISCOVERY_STARTED, + * ACTION_DISCOVERY_FINISHED in {@link android.bluetooth.BluetoothAdapter} class. + * + */ + void onActionDiscoveryStateChanged(String discoveryState); + + /** + * Callback when the current scan mode changed. + * @param preScanMode previous scan mode + * @param scanMode current scan mode + * Possible values are SCAN_MODE_NONE, SCAN_MODE_CONNECTABLE, + * SCAN_MODE_CONNECTABLE_DISCOVERABLE in {@link android.bluetooth.BluetoothAdapter} class. + */ + void onActionScanModeChanged(int preScanMode, int scanMode); + + /** + * Callback when the connection state changed. + * @param state connection state + * Possible values are STATE_NONE, STATE_LISTEN, STATE_CONNECTING, STATE_CONNECTED, + * STATE_DISCONNECTED and STATE_UNKNOWN in {@link State} class. + */ + void onBluetoothServiceStateChanged(int state); + + /** + * Callback when found device. + * @param device a remote device + */ + void onActionDeviceFound(BluetoothDevice device); +} diff --git a/lmbluetoothsdk/src/main/java/co/lujun/lmbluetoothsdk/base/BluetoothLEListener.java b/lmbluetoothsdk/src/main/java/co/lujun/lmbluetoothsdk/base/BluetoothLEListener.java new file mode 100644 index 0000000..cfb7f43 --- /dev/null +++ b/lmbluetoothsdk/src/main/java/co/lujun/lmbluetoothsdk/base/BluetoothLEListener.java @@ -0,0 +1,54 @@ +/* + * The MIT License (MIT) + + * Copyright (c) 2015 LinkMob.cc + + * Contributors: lujun + + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package co.lujun.lmbluetoothsdk.base; + +import android.bluetooth.BluetoothGattCharacteristic; + +/** + * Author: lujun(http://blog.lujun.co) + * Date: 2016-1-15 10:53 + */ +public interface BluetoothLEListener extends BaseListener { + + /** + * Read data. + * @param characteristic + */ + void onReadData(BluetoothGattCharacteristic characteristic); + + /** + * When write data to remote BLE device, the notification will send to here. + * @param characteristic + */ + void onWriteData(BluetoothGattCharacteristic characteristic); + + /** + * When data changed, the notification will send to here. + * @param characteristic + */ + void onDataChanged(BluetoothGattCharacteristic characteristic); +} diff --git a/lmbluetoothsdk/src/main/java/co/lujun/lmbluetoothsdk/base/BluetoothListener.java b/lmbluetoothsdk/src/main/java/co/lujun/lmbluetoothsdk/base/BluetoothListener.java index ab3f25e..ece1d9f 100644 --- a/lmbluetoothsdk/src/main/java/co/lujun/lmbluetoothsdk/base/BluetoothListener.java +++ b/lmbluetoothsdk/src/main/java/co/lujun/lmbluetoothsdk/base/BluetoothListener.java @@ -32,49 +32,7 @@ * Author: lujun(http://blog.lujun.co) * Date: 2016-1-15 10:53 */ -public interface BluetoothListener { - - /** - * Callback when bluetooth power state changed. - * - * @param preState previous power state - * @param state current power state - * Possible values are STATE_OFF, STATE_TURNING_ON, - * STATE_ON, STATE_TURNING_OFF in {@link android.bluetooth.BluetoothAdapter} class. - */ - void onActionStateChanged(int preState, int state); - - /** - * Callback when local Bluetooth adapter discovery process state changed. - * @param discoveryState the state of local Bluetooth adapter discovery process. - * Possible values are ACTION_DISCOVERY_STARTED, - * ACTION_DISCOVERY_FINISHED in {@link android.bluetooth.BluetoothAdapter} class. - * - */ - void onActionDiscoveryStateChanged(String discoveryState); - - /** - * Callback when the current scan mode changed. - * @param preScanMode previous scan mode - * @param scanMode current scan mode - * Possible values are SCAN_MODE_NONE, SCAN_MODE_CONNECTABLE, - * SCAN_MODE_CONNECTABLE_DISCOVERABLE in {@link android.bluetooth.BluetoothAdapter} class. - */ - void onActionScanModeChanged(int preScanMode, int scanMode); - - /** - * Callback when the connection state changed. - * @param state connection state - * Possible values are STATE_NONE, STATE_LISTEN, STATE_CONNECTING, STATE_CONNECTED, - * STATE_DISCONNECTED and STATE_UNKNOWN in {@link co.lujun.lmbluetoothsdk.base.State} class. - */ - void onBluetoothServiceStateChanged(int state); - - /** - * Callback when found device. - * @param device a remote device - */ - void onActionDeviceFound(BluetoothDevice device); +public interface BluetoothListener extends BaseListener { /** * Callback when remote device send data to current device. diff --git a/lmbluetoothsdk/src/main/java/co/lujun/lmbluetoothsdk/receiver/BlueToothReceiver.java b/lmbluetoothsdk/src/main/java/co/lujun/lmbluetoothsdk/receiver/BlueToothReceiver.java index 0f17c39..2723aca 100644 --- a/lmbluetoothsdk/src/main/java/co/lujun/lmbluetoothsdk/receiver/BlueToothReceiver.java +++ b/lmbluetoothsdk/src/main/java/co/lujun/lmbluetoothsdk/receiver/BlueToothReceiver.java @@ -32,7 +32,7 @@ import android.content.Context; import android.content.Intent; -import co.lujun.lmbluetoothsdk.base.BluetoothListener; +import co.lujun.lmbluetoothsdk.base.BaseListener; /** * Author: lujun(http://blog.lujun.co) @@ -40,9 +40,9 @@ */ public class BlueToothReceiver extends BroadcastReceiver { - private BluetoothListener mBluetoothListener; + private BaseListener mBluetoothListener; - public BlueToothReceiver(BluetoothListener listener){ + public BlueToothReceiver(BaseListener listener){ mBluetoothListener = listener; } diff --git a/lmbluetoothsdk/src/main/java/co/lujun/lmbluetoothsdk/service/BluetoothLEService.java b/lmbluetoothsdk/src/main/java/co/lujun/lmbluetoothsdk/service/BluetoothLEService.java index 0c7e6df..7f88095 100644 --- a/lmbluetoothsdk/src/main/java/co/lujun/lmbluetoothsdk/service/BluetoothLEService.java +++ b/lmbluetoothsdk/src/main/java/co/lujun/lmbluetoothsdk/service/BluetoothLEService.java @@ -1,10 +1,225 @@ package co.lujun.lmbluetoothsdk.service; +import android.annotation.TargetApi; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCallback; +import android.bluetooth.BluetoothGattCharacteristic; +import android.bluetooth.BluetoothGattService; +import android.bluetooth.BluetoothProfile; +import android.content.Context; +import android.util.Log; + +import java.util.List; + +import co.lujun.lmbluetoothsdk.base.BluetoothLEListener; +import co.lujun.lmbluetoothsdk.base.State; + /** * Author: lujun(http://blog.lujun.co) * Date: 2016-1-21 15:36 */ +@TargetApi(21) public class BluetoothLEService { + private BluetoothLEListener mBluetoothLEListener; + private BluetoothGatt mBluetoothGatt; + private BluetoothGattCharacteristic mWriteCharacteristic, mNotifyCharacteristic; + + private int mState; + + private static final String TAG = "LMBluetoothSdk"; + + public BluetoothLEService(){ + mState = State.STATE_NONE; + } + + /** + * Set bluetoothLE listener. + * @param listener BluetoothLEListener + */ + public synchronized void setBluetoothLEListener(BluetoothLEListener listener) { + this.mBluetoothLEListener = listener; + } + + /** + * Set the current state of the connection. + * @param state An integer defining the current connection state + */ + private synchronized void setState(int state) { + mState = state; + if (mBluetoothLEListener != null){ + mBluetoothLEListener.onBluetoothServiceStateChanged(state); + } + } + + /** + * Get the current state of connection. + * Possible return values are STATE_NONE, STATE_LISTEN, STATE_CONNECTING, STATE_CONNECTED, + * STATE_DISCONNECTED, STATE_UNKNOWN in {@link co.lujun.lmbluetoothsdk.base.State} class. + * @return the connection state + */ + public int getState() { + return mState; + } + + /** + * Connect to a GATT server. + * @param context + * @param device + */ + public void connect(Context context, BluetoothDevice device){ + // mBluetoothGatt is a BluetoothGatt instance, which you can then use to conduct GATT client operations + mBluetoothGatt = device.connectGatt(context, false, mBTGattCallback); + } + + /** + * Reconnect to a GATT server. + */ + public void reConnect(){ + if (mBluetoothGatt != null){ + mBluetoothGatt.connect(); + } + } + + /** + * Disconnect the connection. + */ + public void disConnect(){ + if (mBluetoothGatt != null){ + mBluetoothGatt.disconnect(); + } + } + + /** + * Close GATT client. + */ + public void close(){ + disConnect(); + mBluetoothGatt.close(); + mBluetoothGatt = null; + } + + /** + * Write data to remote device. + * @param data + */ + public void write(byte[] data){ + if (mBluetoothGatt != null){ + mWriteCharacteristic.setValue(data); + mBluetoothGatt.writeCharacteristic(mWriteCharacteristic); + } + } + + private BluetoothGattCallback mBTGattCallback = new BluetoothGattCallback() { + @Override + public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { + super.onConnectionStateChange(gatt, status, newState); + switch (newState){ + case BluetoothProfile.STATE_CONNECTED: + setState(State.STATE_CONNECTED); + gatt.discoverServices(); + break; + + case BluetoothProfile.STATE_DISCONNECTED: + setState(State.STATE_DISCONNECTED); + break; + + default:break; + } + } + + @Override + public void onServicesDiscovered(BluetoothGatt gatt, int status) { + super.onServicesDiscovered(gatt, status); + if (status == BluetoothGatt.GATT_SUCCESS) { + List services = gatt.getServices(); + for (BluetoothGattService service : services) { + List characteristics = service.getCharacteristics(); + for (BluetoothGattCharacteristic characteristic : characteristics) { + final int charaProp = characteristic.getProperties(); + if ((charaProp | BluetoothGattCharacteristic.PERMISSION_READ) > 0){ + // If there is an active notification on a characteristic, clear + // it first so it doesn't update the data field on the user interface. + if (mNotifyCharacteristic != null){ + mBluetoothGatt.setCharacteristicNotification(mNotifyCharacteristic, false); + mNotifyCharacteristic = null; + } + gatt.readCharacteristic(characteristic); + } + if ((charaProp | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0){ + mNotifyCharacteristic = characteristic; + mBluetoothGatt.setCharacteristicNotification(characteristic, true); + } + if (((charaProp & BluetoothGattCharacteristic.PERMISSION_WRITE) + | (charaProp & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE)) > 0){ + mWriteCharacteristic = characteristic; + } + } + } + } + } + + @Override + public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { + super.onCharacteristicRead(gatt, characteristic, status); + Log.d(TAG, "onCharacteristicRead,data:" + parseData(characteristic)); + if (mBluetoothLEListener != null){ + mBluetoothLEListener.onReadData(characteristic); + } + } + + @Override + public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { + super.onCharacteristicWrite(gatt, characteristic, status); + Log.d(TAG, "onCharacteristicWrite,data:" + parseData(characteristic)); + if (mBluetoothLEListener != null){ + mBluetoothLEListener.onWriteData(characteristic); + } + } + + @Override + public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { + super.onCharacteristicChanged(gatt, characteristic); + Log.d(TAG, "onCharacteristicChanged,data:" + parseData(characteristic)); + if (mBluetoothLEListener != null){ + mBluetoothLEListener.onDataChanged(characteristic); + } + } + }; + private String parseData(BluetoothGattCharacteristic characteristic){ + String result = ""; + // This is special handling for the Heart Rate Measurement profile. Data parsing is + // carried out as per profile specifications: + // http://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml +// if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) { +// int flag = characteristic.getProperties(); +// int format = -1; +// if ((flag & 0x01) != 0) { +// format = BluetoothGattCharacteristic.FORMAT_UINT16; +// Log.d(TAG, "Heart rate format UINT16."); +// } else { +// format = BluetoothGattCharacteristic.FORMAT_UINT8; +// Log.d(TAG, "Heart rate format UINT8."); +// } +// final int heartRate = characteristic.getIntValue(format, 1); +// Log.d(TAG, String.format("Received heart rate: %d", heartRate)); +// intent.putExtra(EXTRA_DATA, String.valueOf(heartRate)); +// } else { + // For all other profiles, writes the data formatted in HEX. + final byte[] data = characteristic.getValue(); + if (data != null && data.length > 0) { + final StringBuilder stringBuilder = new StringBuilder(data.length); + for(byte byteChar : data) + // 格式: 转化为16进制,最小两位一组,不足两位前面补0,大于等于两位不管 + stringBuilder.append(String.format("%02X", byteChar)); +// intent.putExtra(EXTRA_DATA, new String(data) + "\n" + stringBuilder.toString()); + // 原内容+ 16进制内容 +// result = new String(data) + "\n" + stringBuilder.toString(); + result = new String(data); + } +// } + return result; + } }