package com.sirius.botasky.bledemo; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCallback; import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattDescriptor; import android.bluetooth.BluetoothGattService; import android.bluetooth.BluetoothManager; import android.bluetooth.BluetoothProfile; import android.content.Context; import android.os.Bundle; import android.support.design.widget.Snackbar; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.ExpandableListView; import android.widget.SimpleExpandableListAdapter; import android.widget.TextView; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.UUID; /** * 连接到GATT服务端,服务端/主机不是手机,是设备 */ public class ConnectActivity extends AppCompatActivity { private static final String TAG = ConnectActivity.class.getSimpleName(); public static final String DEVICE_ADDRESS = "device_address"; public static final String DEVICE_NAME = "device_name"; private TextView mTvDeviceName; private Button connect; private Button disconnect; private Button write; private TextView mTvData; private BluetoothManager mBluetoothManager; private BluetoothAdapter mBluetoothAdapter; private BluetoothGatt mBluetoothGatt; private String mDeviceAddress; private int mConnectState; private ExpandableListView mExpanList; private ArrayList<ArrayList<BluetoothGattCharacteristic>> mGattCharacteristics = new ArrayList<ArrayList<BluetoothGattCharacteristic>>(); private BluetoothGattCharacteristic mNotifyCharacteristic; public final static UUID UUID_HEART_RATE_MEASUREMENT = UUID.fromString(SampleGattAttributes.HEART_RATE_MEASUREMENT); public final static UUID UUID_INSOLE_MEASUREMENT = UUID.fromString(SampleGattAttributes.UUID_NOTIFY); //断开状态 private static final int STATE_DISCONNECTED = 0; //连接中状态 private static final int STATE_CONNECTING = 1; //已连接状态 private static final int STATE_CONNECTED = 2; private final String LIST_NAME = "NAME"; private final String LIST_UUID = "UUID"; /** * 连接到GATT服务端时,由BLE设备做主机, * 并返回一个BluetoothGatt实例, * 然后你可以使用这个实例来进行GATT客户端操作。 * 请求方(Android app)是GATT客户端。 * BluetoothGattCallback用于传递结果给用户, * 例如连接状态,以及任何进一步GATT客户端操作。 */ private final BluetoothGattCallback mBluetoothGattcallback = new BluetoothGattCallback() { /** * 服务端的蓝牙设备状态改变时的回调,连接上/断开 * @param gatt * @param status * @param newState */ @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { super.onConnectionStateChange(gatt, status, newState); Log.e(TAG, "state change " + newState); if (newState == BluetoothProfile.STATE_CONNECTED) { mConnectState = STATE_CONNECTED; //调用去获取服务信息 mBluetoothGatt.discoverServices(); } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { mConnectState = STATE_DISCONNECTED; } } /** * 当发现服务端的新的服务/特征/描述的时候的回调 * @param gatt * @param status */ @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { super.onServicesDiscovered(gatt, status); Log.e(TAG, "GattServiceDiscover" + status); if (status == BluetoothGatt.GATT_SUCCESS) { //展示获取到的服务 displayGattServices(); } Log.e(TAG, "GATTCallback Service discover " + status); } /** * 返回一个特征的读操作的结果 * @param gatt * @param characteristic * @param status */ @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicRead(gatt, characteristic, status); Log.e(TAG, " onCharacteristicRead call back" ); if (status == BluetoothGatt.GATT_SUCCESS){ displayData(characteristic); } } /** * 当服务端的特征改变时触发的回调 * @param gatt * @param characteristic */ @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { super.onCharacteristicChanged(gatt, characteristic); Log.e(TAG, " onCharacteristicChange call back" ); displayData(characteristic); } /** * 返回一个写特征的操作结果 * @param gatt * @param characteristic * @param status */ @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicWrite(gatt, characteristic, status); Log.e(TAG, "onCharacteristicWrite " +characteristic.getUuid() + " " + status); } }; /** * 将获取到的服务展示出来,根据SampleGattAttributes对比,看有;没有符合的服务 */ private void displayGattServices() { List<BluetoothGattService> services = getGattService(); if (services == null) return; String uuid = null; String unknownServiceString = "未知服务"; String unknownCharaString = "未知特征"; ArrayList<HashMap<String, String>> gattServiceData = new ArrayList<HashMap<String, String>>(); ArrayList<ArrayList<HashMap<String, String>>> gattCharacteristicData = new ArrayList<ArrayList<HashMap<String, String>>>(); mGattCharacteristics = new ArrayList<ArrayList<BluetoothGattCharacteristic>>(); for (BluetoothGattService gattService : services) { HashMap<String, String> currentServiceData = new HashMap<String, String>(); uuid = gattService.getUuid().toString(); currentServiceData.put( LIST_NAME, SampleGattAttributes.lookup(uuid, unknownServiceString)); currentServiceData.put(LIST_UUID, uuid); gattServiceData.add(currentServiceData); ArrayList<HashMap<String, String>> gattCharacteristicGroupData = new ArrayList<HashMap<String, String>>(); List<BluetoothGattCharacteristic> gattCharacteristics = gattService.getCharacteristics(); ArrayList<BluetoothGattCharacteristic> charas = new ArrayList<BluetoothGattCharacteristic>(); // Loops through available Characteristics. for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) { charas.add(gattCharacteristic); HashMap<String, String> currentCharaData = new HashMap<String, String>(); uuid = gattCharacteristic.getUuid().toString(); currentCharaData.put( LIST_NAME, SampleGattAttributes.lookup(uuid, unknownCharaString)); currentCharaData.put(LIST_UUID, uuid); gattCharacteristicGroupData.add(currentCharaData); } mGattCharacteristics.add(charas); gattCharacteristicData.add(gattCharacteristicGroupData); } final SimpleExpandableListAdapter gattServiceAdapter = new SimpleExpandableListAdapter( this, gattServiceData, android.R.layout.simple_expandable_list_item_2, new String[]{LIST_NAME, LIST_UUID}, new int[]{android.R.id.text1, android.R.id.text2}, gattCharacteristicData, android.R.layout.simple_expandable_list_item_2, new String[]{LIST_NAME, LIST_UUID}, new int[]{android.R.id.text1, android.R.id.text2} ); runOnUiThread(new Runnable() { @Override public void run() { mExpanList.setAdapter(gattServiceAdapter); } }); } /** * 获取服务列表 * * @return */ private List<BluetoothGattService> getGattService() { if (mBluetoothGatt != null) { return mBluetoothGatt.getServices(); } Log.e(TAG, "Gatt is null"); return null; } /** * 展示READ或者NOTIFY特征返回的信息 * * @param */ private void displayData(BluetoothGattCharacteristic characteristic) { /** * 通用心率协议的NOTIFY特征需要特殊处理 */ // 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)); setDataMainThread(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) stringBuilder.append(String.format("%02X ", byteChar)); setDataMainThread(new String(data) + "\n" + stringBuilder.toString()); } } } /** * 数据展示要放在主线程展示,否则会报错 * * @param data */ private void setDataMainThread(final String data){ runOnUiThread(new Runnable() { @Override public void run() { mTvData.setText(data); } }); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_connect); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); Log.e(TAG, "DeviceAddress" + getIntent().getStringExtra(DEVICE_ADDRESS)); mDeviceAddress = getIntent().getStringExtra(DEVICE_ADDRESS); initiailizeView(); initiailizeBle(); } private void initiailizeView() { // mTvDeviceName = (TextView) findViewById(R.id.tv_device_name); mTvDeviceName.setText(getIntent().getStringExtra(DEVICE_NAME)); mTvData = (TextView) findViewById(R.id.data); mExpanList = (ExpandableListView) findViewById(R.id.gatt_services_list); mExpanList.setOnChildClickListener(servicesListClickListner); // connect = (Button) findViewById(R.id.connect); disconnect = (Button) findViewById(R.id.diconnect); write = (Button) findViewById(R.id.write); connect.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { connect(); } }); disconnect.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { disconnect(); } }); write.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { } }); } // If a given GATT characteristic is selected, check for supported features. This sample // demonstrates 'Read' and 'Notify' features. See // http://d.android.com/reference/android/bluetooth/BluetoothGatt.html for the complete // list of supported characteristic features. /** * 如果一个GATT的特征被选中, */ private final ExpandableListView.OnChildClickListener servicesListClickListner = new ExpandableListView.OnChildClickListener() { @Override public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) { if (mGattCharacteristics != null) { final BluetoothGattCharacteristic characteristic = mGattCharacteristics.get(groupPosition).get(childPosition); final int charaProp = characteristic.getProperties(); if ((charaProp & BluetoothGattCharacteristic.PROPERTY_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. //如果当前有一个NOTIFY的特征正在活动,要先关闭NOTIFY特征,再去开启READ特征 if (mNotifyCharacteristic != null) { setCharacteristicNotification(mNotifyCharacteristic, false); mNotifyCharacteristic = null; } //开启读特征 readCharacteristic(characteristic); } if ((charaProp & BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) { mNotifyCharacteristic = characteristic; setCharacteristicNotification(characteristic, true); } if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) != 0 && (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) != 0){ Log.e(TAG, "Characteristic this is a write characteristic"); writeCharacteristic(characteristic); } return true; } return false; } }; /** * 写特征 * @return */ private boolean writeCharacteristic(BluetoothGattCharacteristic charac){ //check mBluetoothGatt is available if (mBluetoothGatt == null) { Log.e(TAG, "lost connection"); return false; } // BluetoothGattService Service = mBluetoothGatt.getService(your Services); // if (Service == null) { // Log.e(TAG, "service not found!"); // return false; // } // BluetoothGattCharacteristic charac = Service // .getCharacteristic(your characteristic); if (charac == null) { Log.e(TAG, "char not found!"); return false; } String start = "ES"; byte[] value = start.getBytes(); charac.setValue(value); //这边的status是指发送命令的状态,不是指Write成功或者失败的状态, 所以还要一个onChartacteristicWrite的回调,在GattCallBack里面 boolean status = mBluetoothGatt.writeCharacteristic(charac); Log.e(TAG, " write status " + status); return status; } /** * Request a read on a given {@code BluetoothGattCharacteristic}. The read result is reported * asynchronously through the {@code BluetoothGattCallback#onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int)} * callback. * * @param characteristic The characteristic to read from. */ public void readCharacteristic(BluetoothGattCharacteristic characteristic) { if (mBluetoothAdapter == null || mBluetoothGatt == null) { Log.w(TAG, "BluetoothAdapter not initialized"); return; } mBluetoothGatt.readCharacteristic(characteristic); } /** * Enables or disables notification on a give characteristic. * * @param characteristic Characteristic to act on. * @param enabled If true, enable notification. False otherwise. */ public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) { if (mBluetoothAdapter == null || mBluetoothGatt == null) { Log.w(TAG, "BluetoothAdapter not initialized"); return; } mBluetoothGatt.setCharacteristicNotification(characteristic, enabled); //通用协议心率的NOTIFY特征 // This is specific to Heart Rate Measurement. if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid()) || UUID_INSOLE_MEASUREMENT.equals(characteristic.getUuid())) { BluetoothGattDescriptor descriptor = characteristic.getDescriptor( UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG)); descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); mBluetoothGatt.writeDescriptor(descriptor); } } /** * 初始化 */ private void initiailizeBle() { if (mBluetoothManager == null) { mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); if (mBluetoothManager == null) { Log.e(TAG, "Error " + "Unable to initialize BluetoothManager."); return; } mBluetoothAdapter = mBluetoothManager.getAdapter(); mConnectState = STATE_DISCONNECTED;//设置当前连接状态为未连接 } } private void connect() { //获取设备 final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(mDeviceAddress); //判断是否获取到设备 if (device == null) { Snackbar.make(mTvDeviceName, "Device not found, Unable to connect", Snackbar.LENGTH_LONG).show(); } //直连设备,获取设备的Gatt mBluetoothGatt = device.connectGatt(this, false, mBluetoothGattcallback); mConnectState = STATE_CONNECTING;//设置当前状态为连接中 } private void disconnect() { if (mBluetoothGatt != null && mBluetoothAdapter != null) { mBluetoothGatt.disconnect(); } } @Override protected void onDestroy() { super.onDestroy(); /** * 使用完蓝牙后要调用close,释放系统资源 */ if (mBluetoothGatt != null) { mBluetoothGatt.close(); mBluetoothGatt = null; } } }