Android 蓝牙电话联系人同步之蓝牙状态检测

最近做的一个语音项目,要求车机端可以利用蓝牙,语音打电话给手机通讯录里的联系人。

实现这个功能需要将手机端的通讯录上传到语音的远程服务器端,并且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;
}

 

 

### Android 设备间通过蓝牙传输联系人数据 为了实现在 Android 设备之间通过蓝牙同步联系人数据,开发者可以利用 `BluetoothAdapter` 及其相关类来完成这一目标。首先,应用程序需获取本地蓝牙适配器并确保它处于开启状态[^2]。 #### 初始化蓝牙适配器 ```java // 获取默认的蓝牙适配器实例 final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); mBluetoothAdapter = bluetoothManager.getAdapter(); ``` 一旦确认蓝牙功能可用,则可继续执行设备间的配对流程以及创建用于通信的服务端或客户端角色。对于本案例而言,建议采用基于 RFCOMM 协议的服务端模式以等待来自另一方发起的数据请求;而对方则作为客户端主动尝试建立连接。 当两个装置成功建立了安全可靠的链路之后,就可以准备发送具体的联系人记录了。考虑到效率与兼容性的因素,在实际编码过程中应当先将 VCard 格式的字符串序列化成字节数组形式再经由 `OutputStream.write()` 方法写出至远程节点: #### 发送VCard格式的联系人信息 ```java private void sendContact(BluetoothSocket socket, String vcardString){ try { OutputStream outputStream = socket.getOutputStream(); byte[] buffer = vcardString.getBytes(); // 将vCard转换为byte数组 synchronized (outputStream) { outputStream.write(buffer); // 向socket写入数据 } } catch (IOException e) { Log.e(TAG, "Error occurred when sending data", e); } } ``` 接收侧同样需要监听输入流的变化情况,并及时解析收到的消息内容以便更新本地地址簿条目。这里推荐使用第三方库如 Ez-vcard 来简化 vCard 解析工作,从而提高开发速度和稳定性[^4]。 #### 接收并保存联系人信息 ```java private void receiveContacts(BluetoothSocket socket){ StringBuilder totalMessage = new StringBuilder(); while(true){ try{ InputStream inputStream = socket.getInputStream(); byte[] buffer = new byte[1024]; int bytes; while ((bytes = inputStream.read(buffer)) != -1){ totalMessage.append(new String(buffer, 0, bytes)); if(totalMessage.toString().endsWith("\r\nEND:VCARD\r\n")){ break; // 完整接收到一条完整的vcf文件后退出循环 } } // 处理接收到的信息... saveToPhoneBook(totalMessage.toString()); }catch(IOException e){ Log.d(TAG,"Input stream was disconnected",e); break; } } } private void saveToPhoneBook(String vcfData){ ContentValues values = new ContentValues(); ContentResolver contentResolver=getContentResolver(); Uri rawContactUri=contentResolver.insert(RawContacts.CONTENT_URI,values); ArrayList<ContentProviderOperation> ops=new ArrayList<>(); ops.add(ContentProviderOperation.newInsert(Data.CONTENT_URI) .withValueBackReference(Data.RAW_CONTACT_ID, Integer.parseInt(rawContactUri.getLastPathSegment())) .withValue(Data.MIMETYPE,VndBuilder.VCARD.getMimeType()) .withValue(Data.DATA1,vcfData).build()); try{ contentResolver.applyBatch(ContactsContract.AUTHORITY,ops); }catch(Exception ex){ ex.printStackTrace(); } } ``` 上述代码片段展示了如何构建一个简单的应用层协议来进行跨平台、异构系统的个人资料交换活动。值得注意的是,出于隐私保护的目的,在设计此类特性之前务必充分考虑用户授权机制的设计,确保所有敏感操作均获得明确同意后再予以实施[^1]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值