最近做的一個語音項目,要求車機端可以利用藍牙,語音打電話給手機通訊錄裏的聯繫人。
實現這個功能需要將手機端的通訊錄上傳到語音的遠程服務器端,並且SDK要求必須把藍牙的狀態實時上報上去。
上報通訊錄其實還是很簡單的,只要監聽車機端的聯繫人provider的變化就可以進行讀取操作和上報。
mContactObserver = new ContactObserver(new Handler());
Uri uri = ContactsContract.Contacts.CONTENT_URI;
mResolver.registerContentObserver(uri, true, mContactObserver);
難點其實在於如何上報藍牙的連接狀態,這裏和電話相關的兩個藍牙協議分別是:
- Headset: 藍牙打電話
- Pbap:聯繫人同步
所以如果能監聽藍牙的這兩個連接狀態的變化就可以上報了,但是很不幸的是這兩個狀態在Android sdk裏沒有提供相應的顯示的廣播,這兩個廣播屬於隱藏。如果要想監聽,就必須使用“反射”大法了。
翻看源碼,先找到這兩個廣播的定義之處,然後定義它們
private final static String REFLECTION_HEADSETCLIENT_CLASS =
"android.bluetooth.BluetoothHeadsetClient";
private final static String REFLECTION_PBAP_CLIENT_CLASS =
"android.bluetooth.BluetoothPbapClient";
然後就是反射它們,這裏我使用了一個反射工具類,自己寫的
public String getAttributeValueOfStringType(String classPath, String attribute) {
String value = "";
try {
Class clazz = Class.forName(classPath);
Field field= clazz.getField(attribute);
value = (String)field.get(clazz);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return value;
}
最後就是註冊監聽這兩個藍牙廣播
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ReflectionUtil.getInstance().getAttributeValueOfStringType(
REFLECTION_HEADSETCLIENT_CLASS,
REFLECTION_CONSTANT_CONNECTION_CHANGED));
intentFilter.addAction(ReflectionUtil.getInstance().getAttributeValueOfStringType(
REFLECTION_PBAP_CLIENT_CLASS,
REFLECTION_CONSTANT_CONNECTION_CHANGED));
mBluetoothReceiver = new BluetoothReceiver();
if (!isRegisterBT) {
XXApplication.getContext().registerReceiver(mBluetoothReceiver, intentFilter);
isRegisterBT = true;
}