- 藍(lán)牙開(kāi)發(fā)「防丟器」的相關(guān)知識(shí)點(diǎn)(一):掃描并識(shí)別設(shè)備
- 藍(lán)牙開(kāi)發(fā)「防丟器」的相關(guān)知識(shí)點(diǎn)(二):連接設(shè)備并檢測(cè)連接狀態(tài)
- 藍(lán)牙開(kāi)發(fā)「防丟器」的相關(guān)知識(shí)點(diǎn)(三):手機(jī)與設(shè)備之間指令傳輸
1.初始化藍(lán)牙連接服務(wù)類(lèi)
在掃描并識(shí)別到自家設(shè)備后,接下來(lái)就是連接設(shè)備,并綁定該設(shè)備到手機(jī)了。在連接設(shè)備之前,我們需要初始化藍(lán)牙連接服務(wù),這個(gè)初始化放在應(yīng)用的Application中進(jìn)行,整個(gè)應(yīng)用與該服務(wù)密切相關(guān)。
public BluetoothLeService mBluetoothLeService; // 藍(lán)牙連接服務(wù)
public ServiceConnection mServiceConnection; // 藍(lán)牙連接服務(wù)
/**
* 初始化BLE相關(guān)
*/
private void initBle() {
// 初始化并綁定服務(wù)
mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(final ComponentName componentName, final IBinder service) {
BuglyLog.d(TAG, "--onServiceConnected--");
mBluetoothLeService = ((BluetoothLeService.LocalBinder) service).getService();
if (!mBluetoothLeService.initialize()) {
BuglyLog.e(TAG, "Unable to initialize Bluetooth");
}
}
@Override
public void onServiceDisconnected(final ComponentName componentName) {
mBluetoothLeService = null;
}
};
Intent gattServiceIntent = new Intent(this, BluetoothLeService.class);
bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);
}
初始化過(guò)程:
private BluetoothManager mBluetoothManager;
private BluetoothAdapter mBluetoothAdapter;
public boolean initialize() {
// For API level 18 and above, get a reference to BluetoothAdapter through
// BluetoothManager.
if (mBluetoothManager == null) {
mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
if (mBluetoothManager == null) {
BuglyLog.e(TAG, "Unable to initialize BluetoothManager.");
return false;
}
}
mBluetoothAdapter = mBluetoothManager.getAdapter();
if (mBluetoothAdapter == null) {
BuglyLog.e(TAG, "Unable to obtain a BluetoothAdapter.");
return false;
}
return true;
}
*以上過(guò)程在Application的onCreate中進(jìn)行,以下我再寫(xiě)點(diǎn)與業(yè)務(wù)邏輯相關(guān)的,可不看!該邏輯主要是用來(lái)定時(shí)檢測(cè)已經(jīng)綁定過(guò)的設(shè)備是否都已經(jīng)連接,實(shí)際場(chǎng)景就是:用戶(hù)可能在錢(qián)包、背包、遙控器上等放置了防丟器,如果超距后會(huì)斷開(kāi)連接,但是重新回到連接范圍后,手機(jī)要發(fā)起重連請(qǐng)求,這依賴(lài)于此定時(shí)檢測(cè)服務(wù)。
// 以下與應(yīng)用業(yè)務(wù)邏輯高度關(guān)聯(lián),可不看
TimerTask task = new TimerTask() {
@Override
public void run() {
BluetoothManager btManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
BluetoothAdapter mBluetoothAdapter = btManager.getAdapter();
if (!mBluetoothAdapter.isEnabled()) {
BuglyLog.i(TAG, "^^^藍(lán)牙沒(méi)打開(kāi)^^^");
if (mBluetoothLeService != null) {
// 如果之前有連接信息,需要清除(不清除的后果就是藍(lán)牙重新打開(kāi),會(huì)檢測(cè)到之前連接過(guò),但是其實(shí)此時(shí)并未重新連接)
mBluetoothLeService.clear();
}
return;
}
if (mBluetoothLeService != null) {
List<Beagle> mBeagles = getDaoSession().getBeagleDao().loadAll();
List<BluetoothDevice> connectDevices = mBluetoothLeService.getConnectedDevices();
for (Beagle beagle : mBeagles) {
BuglyLog.d(TAG, "檢查:" + beagle.getMac());
boolean isConnected = false;
for (BluetoothDevice bd : connectDevices) {
if (TextUtils.equals(bd.getAddress(), beagle.getMac())) {
BuglyLog.d(TAG, "定時(shí)檢測(cè),設(shè)備已經(jīng)處于連接狀態(tài):" + beagle.getMac());
isConnected = true;
break;
}
}
// 未連接時(shí),自動(dòng)重連
if (!isConnected) {
boolean flag = mBluetoothLeService.connect(beagle.getMac());
if (flag) {
BuglyLog.d(TAG, "連接成功:" + beagle.getMac());
} else {
BuglyLog.d(TAG, "連接失?。? + beagle.getMac());
}
}
}
}
}
};
mTimer = new Timer();
mTimer.schedule(task, 2000, 10000);
2.連接BLE設(shè)備
其實(shí)連接設(shè)備的步驟非常簡(jiǎn)單,代碼如下:
getApp().mDeviceAddress = mAdapterList.get(position).getAddress();
getApp().mBluetoothLeService.connect(getApp().mDeviceAddress);
對(duì)應(yīng)的*BluetoothLeService中方法如下:
/**
* 連接藍(lán)牙設(shè)備
*
* @param mac mac
*/
public boolean connect(String mac) {
if (mBluetoothAdapter == null || mac == null) {
BuglyLog.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
return false;
}
List<BluetoothDevice> connectDevices = getConnectedDevices();
for (BluetoothDevice bd : connectDevices) {
if (TextUtils.equals(bd.getAddress(), mac)) {
BuglyLog.d(TAG, "該設(shè)備已經(jīng)連接啦:" + mac);
return true;
}
}
// 使用之前連接過(guò)的
if (mBluetoothGatts.get(mac) != null) {
if (mBluetoothGatts.get(mac).connect()) {
BuglyLog.d(TAG, "使用之前連接過(guò)的:" + mac);
return true;
} else {
return false;
}
}
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(mac);
if (device == null) {
BuglyLog.w(TAG, "Device not found. Unable to connect.");
return false;
}
device.connectGatt(this, false, mGattCallback);
BuglyLog.d(TAG, "創(chuàng)建一個(gè)新連接,MAC=" + mac);
return true;
}
*這里的getApp()就是獲取應(yīng)用的靜態(tài)Application,BluetoothLeService類(lèi)內(nèi)容較多,最后附上,下面拆分簡(jiǎn)單說(shuō)下。
3.處理連接后的回調(diào)事件
在上面的連接設(shè)備方法中,有一個(gè)很關(guān)鍵的參數(shù)mGattCallback,這個(gè)是用來(lái)接收藍(lán)牙連接的各類(lèi)回調(diào)事件的,我們主要的業(yè)務(wù)邏輯都會(huì)在此處理:連接成功后的服務(wù)通道獲取、認(rèn)證、斷開(kāi)連接處理、指令發(fā)送與接收回調(diào)、信號(hào)強(qiáng)度變化等等。
其對(duì)應(yīng)的代碼如下,其實(shí)根據(jù)BluetoothGattCallback需要重寫(xiě)的方法名稱(chēng),也能大概知道其作用:
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
@Override
public void onCharacteristicChanged(final BluetoothGatt gatt, final BluetoothGattCharacteristic
characteristic) {
dealWithData(gatt, characteristic);
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
dealWithData(gatt, characteristic);
} else {
BuglyLog.e(TAG, "讀取通道信息失?。? + characteristic.getUuid());
}
}
@Override
public void onConnectionStateChange(final BluetoothGatt gatt, final int status, final int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
BuglyLog.i(TAG, "BluetoothGatt連接上,MAC:" + gatt.getDevice().getAddress());
gatt.discoverServices();
mBluetoothGatts.put(gatt.getDevice().getAddress(), gatt);
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
dealDeviceDisconnected(gatt);
BuglyLog.w(TAG, "BluetoothGatt斷開(kāi),MAC:" + gatt.getDevice().getAddress());
}
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
String uuidStr = characteristic.getUuid().toString();
BuglyLog.e(TAG, "onCharacteristicWrite-->\nmac:" + gatt.getDevice().getAddress() + "\nuuid:" + uuidStr +
"\n發(fā)送的值:" + ByteUtils.byteArrayToHexString(characteristic.getValue()) + "\nstatus:" + status);
if (TextUtils.equals(uuidStr, BluetoothUtils.UUID_S_EXTRA_C.toString())) { // 設(shè)備信息設(shè)置
StatusEvent e = new StatusEvent();
String action = "";
if (characteristic.getValue() == BluetoothUtils.VALUE_MODE_THETHER) {
action = StatusEvent.ACTION_MODE_THETHER;
} else if (characteristic.getValue() == BluetoothUtils.VALUE_MODE_FIND) {
action = StatusEvent.ACTION_MODE_FIND;
} else if (characteristic.getValue() == BluetoothUtils.VALUE_FIND_LIGHT_ON) {
action = StatusEvent.ACTION_FIND_LIGHT_ON;
} else if (characteristic.getValue() == BluetoothUtils.VALUE_FIND_LIGHT_OFF) {
action = StatusEvent.ACTION_FIND_LIGHT_OFF;
} else if (characteristic.getValue() == BluetoothUtils.VALUE_TETHER_BEEP_ON) {
action = StatusEvent.ACTION_TETHER_BEEP_ON;
} else if (characteristic.getValue() == BluetoothUtils.VALUE_TETHER_BEEP_OFF) {
action = StatusEvent.ACTION_TETHER_BEEP_OFF;
}
e.setAction(action);
e.setMac(gatt.getDevice().getAddress());
EventBus.getDefault().post(e);
} else if (TextUtils.equals(uuidStr, BluetoothUtils.UUID_S_IMMEDIATE_C_ALERT.toString())) { // 立即警報(bào)設(shè)置
StatusEvent e = new StatusEvent();
e.setAction(StatusEvent.ACTION_IMMEDIATE);
e.setMac(gatt.getDevice().getAddress());
EventBus.getDefault().post(e);
} else if (TextUtils.equals(uuidStr, BluetoothUtils.UUID_S_EXTRA_C_LOGIN.toString())) {
// 通知綁定頁(yè)面
BindEvent e = new BindEvent();
e.setAction(BindEvent.ACTION_BIND_SUCCESS);
e.setMac(gatt.getDevice().getAddress());
EventBus.getDefault().post(e);
} else if (TextUtils.equals(uuidStr, BluetoothUtils.UUID_S_EXTRA_C_UNBIND.toString())) { // 解綁命令
QueryBuilder qb = MyApplication.getInstance().getDaoSession().getBeagleDao().queryBuilder();
qb.where(BeagleDao.Properties.Mac.eq(gatt.getDevice().getAddress()));
List<Beagle> mBeagles = qb.list();
Beagle beagle;
if (mBeagles.size() > 0) {
beagle = mBeagles.get(0);
} else {
return;
}
if (beagle.getUpdateStatus() == Beagle.UPDATE_PROCESSING) {
BuglyLog.d(TAG, "解綁--升級(jí)…………");
mBluetoothGatts.remove(gatt.getDevice().getAddress());
} else {
BuglyLog.d(TAG, "解綁?。?!");
MyApplication.getInstance().getDaoSession().getBeagleDao().deleteByKey(beagle.getMac());
StatusEvent e = new StatusEvent();
e.setAction(StatusEvent.ACTION_UNBIND);
e.setMac(gatt.getDevice().getAddress());
EventBus.getDefault().post(e);
}
}
}
@Override
public void onServicesDiscovered(final BluetoothGatt gatt, final int status) {
BuglyLog.w(TAG, "onServicesDiscovered received: " + status);
if (status == BluetoothGatt.GATT_SUCCESS) {
dealServicesDiscoverd(gatt);
} else {
BuglyLog.w(TAG, "onServicesDiscovered received: " + status);
}
}
@Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
StatusEvent e = new StatusEvent();
e.setAction(StatusEvent.ACTION_RSSI);
e.setMac(gatt.getDevice().getAddress());
e.setRssi(rssi);
EventBus.getDefault().post(e);
} else {
BuglyLog.w(TAG, "onReadRemoteRssi received: " + status);
}
}
};
通常在建立連接后,會(huì)進(jìn)入onServicesDiscovered方法,這個(gè)時(shí)候可已根據(jù)需要去發(fā)起一些通道的訂閱(可遍歷所有通道信息,根據(jù)通道的可讀、通知屬性來(lái)訂閱)。我這里,手機(jī)需要向設(shè)備發(fā)起認(rèn)證信息(這個(gè)需要固件配合,認(rèn)證后,其他手機(jī)就掃描不到該設(shè)備了),認(rèn)證的指令自定義約定,此處略。從最后附錄的全部代碼可以看到認(rèn)證指令發(fā)送失敗是立即通知程序頁(yè)面認(rèn)證失敗的,但是成功發(fā)送認(rèn)證指令,并沒(méi)有立即通知,這里要注意,真正的指令是否成功寫(xiě)入設(shè)備,是要通過(guò)onCharacteristicWrite來(lái)判斷的:
- onCharacteristicChanged:接收到通道指令(前提是已經(jīng)訂閱),如設(shè)備的點(diǎn)擊、雙擊、長(zhǎng)按事件;
- onCharacteristicRead:手機(jī)主動(dòng)發(fā)起的read后,設(shè)備回傳過(guò)來(lái)的數(shù)據(jù),如讀取設(shè)備電量、固件版本等;
- onConnectionStateChange:設(shè)備與手機(jī)的連接狀態(tài)變化:連接成功、斷開(kāi)連接;
- onCharacteristicWrite:手機(jī)向設(shè)備寫(xiě)入指令的結(jié)果,該方法中可獲取被寫(xiě)入通道的ID,內(nèi)容,狀態(tài);
- onServicesDiscovered:剛建立連接時(shí),設(shè)備擁有的服務(wù)被發(fā)現(xiàn);
- onReadRemoteRssi:設(shè)備信號(hào)強(qiáng)度變化。
要處理手機(jī)與設(shè)備之間的業(yè)務(wù),BluetoothGattCallback是重點(diǎn),再各個(gè)回調(diào)方法中,結(jié)合自己的業(yè)務(wù)邏輯,基本可完成應(yīng)用的基礎(chǔ)功能:綁定(成功后是要寫(xiě)入手機(jī)本地?cái)?shù)據(jù)庫(kù)的,與藍(lán)牙無(wú)關(guān)就不貼了)、斷開(kāi)報(bào)警、信號(hào)弱時(shí)提醒距離遠(yuǎn)、接收設(shè)備指令來(lái)完成對(duì)應(yīng)動(dòng)作……
4. 附錄:
上面的代碼中有一些方法代碼缺失,下面是BluetoothLeService.java完整代碼:
package com.powerstick.beaglepro.services;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;
import android.support.v7.app.NotificationCompat;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import com.afap.utils.ByteUtils;
import com.powerstick.beaglepro.MyApplication;
import com.powerstick.beaglepro.R;
import com.powerstick.beaglepro.event.BatteryEvent;
import com.powerstick.beaglepro.event.BindEvent;
import com.powerstick.beaglepro.event.FirmwareEvent;
import com.powerstick.beaglepro.event.StatusEvent;
import com.powerstick.beaglepro.greendao.Beagle;
import com.powerstick.beaglepro.greendao.BeagleDao;
import com.powerstick.beaglepro.receiver.GattUpdateReceiver;
import com.powerstick.beaglepro.util.BluetoothUtils;
import com.powerstick.beaglepro.util.Utils;
import com.tencent.bugly.crashreport.BuglyLog;
import org.greenrobot.eventbus.EventBus;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import de.greenrobot.dao.query.QueryBuilder;
public class BluetoothLeService extends Service {
private final static String TAG = "BluetoothLeService";
private final IBinder mBinder = new LocalBinder();
private BluetoothManager mBluetoothManager;
private BluetoothAdapter mBluetoothAdapter;
// 記錄MAC對(duì)應(yīng)的BluetoothGatt,可通過(guò)MAC來(lái)獲取各個(gè)設(shè)備的BluetoothGatt
private Map<String, BluetoothGatt> mBluetoothGatts = new HashMap<>();
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
@Override
public void onCharacteristicChanged(final BluetoothGatt gatt, final BluetoothGattCharacteristic
characteristic) {
dealWithData(gatt, characteristic);
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
dealWithData(gatt, characteristic);
} else {
BuglyLog.e(TAG, "讀取通道信息失敗:" + characteristic.getUuid());
}
}
@Override
public void onConnectionStateChange(final BluetoothGatt gatt, final int status, final int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
BuglyLog.i(TAG, "BluetoothGatt連接上,MAC:" + gatt.getDevice().getAddress());
gatt.discoverServices();
mBluetoothGatts.put(gatt.getDevice().getAddress(), gatt);
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
dealDeviceDisconnected(gatt);
BuglyLog.w(TAG, "BluetoothGatt斷開(kāi),MAC:" + gatt.getDevice().getAddress());
}
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
String uuidStr = characteristic.getUuid().toString();
BuglyLog.e(TAG, "onCharacteristicWrite-->\nmac:" + gatt.getDevice().getAddress() + "\nuuid:" + uuidStr +
"\n發(fā)送的值:" + ByteUtils.byteArrayToHexString(characteristic.getValue()) + "\nstatus:" + status);
if (TextUtils.equals(uuidStr, BluetoothUtils.UUID_S_EXTRA_C.toString())) { // 設(shè)備信息設(shè)置
StatusEvent e = new StatusEvent();
String action = "";
if (characteristic.getValue() == BluetoothUtils.VALUE_MODE_THETHER) {
action = StatusEvent.ACTION_MODE_THETHER;
} else if (characteristic.getValue() == BluetoothUtils.VALUE_MODE_FIND) {
action = StatusEvent.ACTION_MODE_FIND;
} else if (characteristic.getValue() == BluetoothUtils.VALUE_FIND_LIGHT_ON) {
action = StatusEvent.ACTION_FIND_LIGHT_ON;
} else if (characteristic.getValue() == BluetoothUtils.VALUE_FIND_LIGHT_OFF) {
action = StatusEvent.ACTION_FIND_LIGHT_OFF;
} else if (characteristic.getValue() == BluetoothUtils.VALUE_TETHER_BEEP_ON) {
action = StatusEvent.ACTION_TETHER_BEEP_ON;
} else if (characteristic.getValue() == BluetoothUtils.VALUE_TETHER_BEEP_OFF) {
action = StatusEvent.ACTION_TETHER_BEEP_OFF;
}
e.setAction(action);
e.setMac(gatt.getDevice().getAddress());
EventBus.getDefault().post(e);
} else if (TextUtils.equals(uuidStr, BluetoothUtils.UUID_S_IMMEDIATE_C_ALERT.toString())) { // 立即警報(bào)設(shè)置
StatusEvent e = new StatusEvent();
e.setAction(StatusEvent.ACTION_IMMEDIATE);
e.setMac(gatt.getDevice().getAddress());
EventBus.getDefault().post(e);
} else if (TextUtils.equals(uuidStr, BluetoothUtils.UUID_S_EXTRA_C_LOGIN.toString())) {
// 通知綁定頁(yè)面
BindEvent e = new BindEvent();
e.setAction(BindEvent.ACTION_BIND_SUCCESS);
e.setMac(gatt.getDevice().getAddress());
EventBus.getDefault().post(e);
} else if (TextUtils.equals(uuidStr, BluetoothUtils.UUID_S_EXTRA_C_UNBIND.toString())) { // 解綁命令
QueryBuilder qb = MyApplication.getInstance().getDaoSession().getBeagleDao().queryBuilder();
qb.where(BeagleDao.Properties.Mac.eq(gatt.getDevice().getAddress()));
List<Beagle> mBeagles = qb.list();
Beagle beagle;
if (mBeagles.size() > 0) {
beagle = mBeagles.get(0);
} else {
return;
}
// 升級(jí)完畢后的解綁,不做刪除
// if (beagle.getUpdateStatus() == Beagle.UPDATE_COMPLETED) {
// BuglyLog.d(TAG, "解綁--升級(jí)完畢");
// beagle.setUpdateStatus(Beagle.UPDATE_NONE);
// MyApplication.getInstance().getDaoSession().getBeagleDao().update(beagle);
// } else
if (beagle.getUpdateStatus() == Beagle.UPDATE_PROCESSING) {
BuglyLog.d(TAG, "解綁--升級(jí)…………");
mBluetoothGatts.remove(gatt.getDevice().getAddress());
} else {
BuglyLog.d(TAG, "解綁?。?!");
MyApplication.getInstance().getDaoSession().getBeagleDao().deleteByKey(beagle.getMac());
StatusEvent e = new StatusEvent();
e.setAction(StatusEvent.ACTION_UNBIND);
e.setMac(gatt.getDevice().getAddress());
EventBus.getDefault().post(e);
}
}
}
@Override
public void onServicesDiscovered(final BluetoothGatt gatt, final int status) {
BuglyLog.w(TAG, "onServicesDiscovered received: " + status);
if (status == BluetoothGatt.GATT_SUCCESS) {
dealServicesDiscoverd(gatt);
} else {
BuglyLog.w(TAG, "onServicesDiscovered received: " + status);
}
}
@Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
StatusEvent e = new StatusEvent();
e.setAction(StatusEvent.ACTION_RSSI);
e.setMac(gatt.getDevice().getAddress());
e.setRssi(rssi);
EventBus.getDefault().post(e);
} else {
BuglyLog.w(TAG, "onReadRemoteRssi received: " + status);
}
}
};
public boolean initialize() {
// For API level 18 and above, get a reference to BluetoothAdapter through
// BluetoothManager.
if (mBluetoothManager == null) {
mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
if (mBluetoothManager == null) {
BuglyLog.e(TAG, "Unable to initialize BluetoothManager.");
return false;
}
}
mBluetoothAdapter = mBluetoothManager.getAdapter();
if (mBluetoothAdapter == null) {
BuglyLog.e(TAG, "Unable to obtain a BluetoothAdapter.");
return false;
}
return true;
}
public BluetoothGatt getGatt(String mac) {
return mBluetoothGatts.get(mac);
}
public void close() {
for (BluetoothGatt gatt : mBluetoothGatts.values()) {
if (gatt != null) {
gatt.close();
gatt = null;
}
}
}
public void clear() {
if (mBluetoothAdapter == null) {
BuglyLog.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
return;
}
mBluetoothGatts.clear();
}
/**
* 連接藍(lán)牙設(shè)備
*
* @param mac mac
*/
public boolean connect(String mac) {
if (mBluetoothAdapter == null || mac == null) {
BuglyLog.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
return false;
}
List<BluetoothDevice> connectDevices = getConnectedDevices();
for (BluetoothDevice bd : connectDevices) {
if (TextUtils.equals(bd.getAddress(), mac)) {
BuglyLog.d(TAG, "該設(shè)備已經(jīng)連接啦:" + mac);
return true;
}
}
// 使用之前連接過(guò)的
if (mBluetoothGatts.get(mac) != null) {
if (mBluetoothGatts.get(mac).connect()) {
BuglyLog.d(TAG, "使用之前連接過(guò)的:" + mac);
return true;
} else {
return false;
}
}
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(mac);
if (device == null) {
BuglyLog.w(TAG, "Device not found. Unable to connect.");
return false;
}
device.connectGatt(this, false, mGattCallback);
BuglyLog.d(TAG, "創(chuàng)建一個(gè)新連接,MAC=" + mac);
return true;
}
/**
* Disconnects an existing connection or cancel a pending connection. The disconnection result
* is reported asynchronously through the
* {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
* callback.
*/
public void disconnect(String mac) {
if (mBluetoothAdapter == null || mBluetoothGatts.get(mac) == null) {
BuglyLog.w(TAG, "BluetoothAdapter not initialized");
return;
}
BluetoothGatt gatt = mBluetoothGatts.get(mac);
gatt.disconnect();
gatt.close();
mBluetoothGatts.remove(mac);
}
/**
* Retrieves a list of supported GATT services on the connected device. This should be
* invoked only after {@code BluetoothGatt#discoverServices()} completes successfully.
*
* @return A {@code List} of supported services.
*/
public List<BluetoothGattService> getSupportedGattServices(String mac) {
if (mBluetoothGatts.get(mac) == null) {
return null;
}
return mBluetoothGatts.get(mac).getServices();
}
/**
* 讀取目標(biāo)通道數(shù)據(jù)
*
* @param mac 目標(biāo)設(shè)備MAC
* @param characteristic 目標(biāo)通道
*/
public void readCharacteristic(String mac, BluetoothGattCharacteristic characteristic) {
if (mBluetoothAdapter == null || mBluetoothGatts.get(mac) == null) {
BuglyLog.w(TAG, "BluetoothAdapter not initialized");
return;
}
mBluetoothGatts.get(mac).readCharacteristic(characteristic);
}
/**
* 向目標(biāo)通道寫(xiě)入數(shù)據(jù)
*
* @param mac 目標(biāo)設(shè)備MAC
* @param characteristic 目標(biāo)通道
*/
public boolean writeCharacteristic(String mac, BluetoothGattCharacteristic characteristic) {
if (mBluetoothAdapter == null || mBluetoothGatts.get(mac) == null) {
BuglyLog.w(TAG, "BluetoothAdapter not initialized");
return false;
}
return mBluetoothGatts.get(mac).writeCharacteristic(characteristic);
}
/**
* 設(shè)置監(jiān)聽(tīng)通道通知
*
* @param mac 目標(biāo)設(shè)備MAC
* @param characteristic 目標(biāo)通道
* @param enabled 是否接收通知
*/
public void setCharacteristicNotification(String mac, BluetoothGattCharacteristic characteristic, boolean enabled) {
if (mBluetoothAdapter == null || mBluetoothGatts.get(mac) == null) {
BuglyLog.w(TAG, "BluetoothAdapter未初始化");
return;
}
mBluetoothGatts.get(mac).setCharacteristicNotification(characteristic, enabled);
}
public List<BluetoothDevice> getConnectedDevices() {
if (mBluetoothManager == null) {
return null;
}
return mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT_SERVER);
}
public BluetoothGattService getService(String mac, UUID uuid) {
if (mBluetoothGatts.get(mac) == null) {
BuglyLog.w(TAG, "BluetoothGatt未初始化");
return null;
}
return mBluetoothGatts.get(mac).getService(uuid);
}
@Override
public IBinder onBind(final Intent intent) {
return mBinder;
}
@Override
public boolean onUnbind(final Intent intent) {
close();
return super.onUnbind(intent);
}
public class LocalBinder extends Binder {
public BluetoothLeService getService() {
return BluetoothLeService.this;
}
}
/**
* 處理Service被發(fā)現(xiàn)
*/
private void dealServicesDiscoverd(BluetoothGatt gatt) {
String mac = gatt.getDevice().getAddress();
if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
mMediaPlayer.stop();
}
displayGattServices(getSupportedGattServices(mac), mac);
}
/**
* 處理設(shè)備斷開(kāi)的情況
*/
private void dealDeviceDisconnected(BluetoothGatt gatt) {
Context context = getApplicationContext();
String mac = gatt.getDevice().getAddress();
// TODO
BuglyLog.i(TAG, "斷開(kāi)連接 響鈴:" + mac);
mBluetoothGatts.get(mac).close();
mBluetoothGatts.remove(mac);
QueryBuilder qb = MyApplication.getInstance().getDaoSession().getBeagleDao().queryBuilder();
qb.where(BeagleDao.Properties.Mac.eq(mac));
List<Beagle> mBeagles = qb.list();
Beagle beagle;
if (mBeagles.size() > 0) {
beagle = mBeagles.get(0);
} else {
return;
}
if (beagle.getPhoneAlarm() && Utils.isNeedNotify(context, beagle) && beagle.getMode() == 0) {
bellToRemind();
Intent i = new Intent(GattUpdateReceiver.ACTION_CANCEL);
PendingIntent pIntent = PendingIntent.getBroadcast(context, 0, i, PendingIntent.FLAG_UPDATE_CURRENT);
Uri ringUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
Notification notify = new NotificationCompat
.Builder(context)
.setTicker(context.getString(R.string.app_name))
.setContentTitle(context.getString(R.string.app_name))
.setContentText("Device " + beagle.getAlias() + " Disconnected")
.setSmallIcon(R.drawable.ic_notification)
.setAutoCancel(false)
.setOngoing(true)
.setContentIntent(pIntent)
.setSound(ringUri)
.build();
MyApplication.getInstance().getmNotificationManager().notify(GattUpdateReceiver.NOTIFY_ID, notify);
}
// 通知主界面
StatusEvent e = new StatusEvent();
e.setAction(StatusEvent.ACTION_GATT_DISCONNECTED);
e.setMac(mac);
EventBus.getDefault().post(e);
}
private static MediaPlayer mMediaPlayer;
/**
* 處理接收到的數(shù)據(jù)
*/
private void dealWithData(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
BuglyLog.d(TAG, "進(jìn)入方法:dealWithData()");
final UUID uuid = characteristic.getUuid();
final String mac = gatt.getDevice().getAddress();
final byte[] dataArr = characteristic.getValue();
String dataString = new String(dataArr);
BuglyLog.i(TAG, "接收到地址:" + mac);
BuglyLog.i(TAG, "接收到參數(shù):" + ByteUtils.byteArrayToHexString(dataArr));
BuglyLog.i(TAG, "接收到參數(shù):" + dataString);
if (BluetoothUtils.UUID_S_KEY_C_PRESS.equals(uuid)) {
int v = ByteUtils.getIntFromByte(dataArr[0]);
if (v == 1) { // 短按
// 通知主界面
StatusEvent e = new StatusEvent();
e.setAction(StatusEvent.ACTION_PRESS_SHORT);
e.setMac(mac);
EventBus.getDefault().post(e);
} else if (v == 2) { // 長(zhǎng)按
if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
mMediaPlayer.stop();
} else {
bellToRemind();
}
}
} else if (BluetoothUtils.UUID_S_BATTERY_C_LEVEL.equals(uuid)) {
// 通知設(shè)備信息界面
BatteryEvent e = new BatteryEvent();
e.setMac(mac);
e.setLevel(ByteUtils.getIntFromByte(dataArr[0]));
EventBus.getDefault().post(e);
} else if (BluetoothUtils.UUID_S_DEVICEINFO_C_FIRMWARE.equals(uuid)) {
// 通知設(shè)備信息界面
FirmwareEvent e = new FirmwareEvent();
e.setMac(mac);
e.setVersion(dataString);
EventBus.getDefault().post(e);
}
// else if (BluetoothUtils.UUID_S_EXTRA_C_LOGIN.equals(uuid)) {
// // 寫(xiě)入認(rèn)證信息后,設(shè)備返回pass則綁定成功
// String result = new String(dataArr);
// if (TextUtils.equals(result.toLowerCase(), "pass")) {
// // 通知綁定頁(yè)面
// BindEvent e = new BindEvent();
// e.setAction(BindEvent.ACTION_BIND_SUCCESS);
// e.setMac(mac);
// EventBus.getDefault().post(e);
// }
// }
}
void bellToRemind() {
AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
int max = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
am.setStreamVolume(AudioManager.STREAM_MUSIC, max, AudioManager.FLAG_PLAY_SOUND);
if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
mMediaPlayer.stop();
}
mMediaPlayer = MediaPlayer.create(getApplicationContext(), R.raw.helium);
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.setLooping(false); //循環(huán)播放
mMediaPlayer.start();
}
BluetoothGattCharacteristic mNotifyCharacteristic;
private void displayGattServices(List<BluetoothGattService> gattServices, String mac) {
if (gattServices == null) {
return;
}
for (final BluetoothGattService gattService : gattServices) {
final List<BluetoothGattCharacteristic> gattCharacteristics = gattService.getCharacteristics();
String uuid0 = gattService.getUuid().toString();
BuglyLog.i(TAG, "服務(wù)uuid0=" + uuid0);
// Loops through available Characteristics.
for (final BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
UUID uuid = gattCharacteristic.getUuid();
BuglyLog.d(TAG, "服務(wù)uuid=" + uuid);
// 監(jiān)聽(tīng)按鍵指令以及認(rèn)證結(jié)果信息
if (BluetoothUtils.UUID_S_KEY_C_PRESS.equals(uuid) ||
BluetoothUtils.UUID_S_EXTRA_C_LOGIN.equals(uuid)) {
BuglyLog.i(TAG, "啟動(dòng)監(jiān)聽(tīng)通知服務(wù)uuid=" + uuid);
int charaProp = gattCharacteristic.getProperties();
// if ((charaProp | BluetoothGattCharacteristic.PROPERTY_READ) > 0) {
// BuglyLog.i(TAG, "讀取=" + uuid);
// // 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) {
// MyApplication.getInstance().mBluetoothLeService.setCharacteristicNotification
// (mac, mNotifyCharacteristic, true);
// mNotifyCharacteristic = null;
// }
// }
if ((charaProp | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
mNotifyCharacteristic = gattCharacteristic;
setCharacteristicNotification(mac, gattCharacteristic, true);
}
}
}
}
// 建立連接后,搜尋到設(shè)備服務(wù),且該設(shè)備并未找到綁定記錄,則寫(xiě)入認(rèn)證信息進(jìn)行綁定
String imei = ((TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE)).getDeviceId();
byte[] loginValue = new byte[20];
// 認(rèn)證的具體內(nèi)容我就不寫(xiě)了,這個(gè)是要與設(shè)備固件開(kāi)發(fā)約定好的
boolean loginFlag = BluetoothUtils.sendValueToBle(mac, BluetoothUtils.UUID_S_EXTRA, BluetoothUtils
.UUID_S_EXTRA_C_LOGIN, loginValue);
BuglyLog.i(TAG, "寫(xiě)入認(rèn)證信息=" + loginFlag);
if (!loginFlag) {
BindEvent e = new BindEvent();
e.setAction(BindEvent.ACTION_BIND_FAIL);
e.setMac(mac);
EventBus.getDefault().post(e);
}
}
}
我……寫(xiě)的好亂,多包涵。