| <uses-permission android:name="android.permission.INTERNET" /> | <uses-permission android:name="android.permission.INTERNET" /> | ||||
| <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> | <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> | ||||
| <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> | <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> | ||||
| <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" | |||||
| android:maxSdkVersion="30"/> | |||||
| <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" | |||||
| android:maxSdkVersion="30"/> | |||||
| <uses-permission | |||||
| android:name="android.permission.ACCESS_COARSE_LOCATION" | |||||
| android:maxSdkVersion="30" /> | |||||
| <uses-permission | |||||
| android:name="android.permission.ACCESS_FINE_LOCATION" | |||||
| android:maxSdkVersion="30" /> | |||||
| <!--OTA需要读写文件--> | <!--OTA需要读写文件--> | ||||
| <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> | <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> | ||||
| <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> | <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> | ||||
| android:name="android.permission.BLUETOOTH_ADMIN" | android:name="android.permission.BLUETOOTH_ADMIN" | ||||
| android:maxSdkVersion="30" /> | android:maxSdkVersion="30" /> | ||||
| <!--android12还需要增加如下权限,也需求动态申请--> | <!--android12还需要增加如下权限,也需求动态申请--> | ||||
| <uses-permission android:name="android.permission.BLUETOOTH_SCAN" | |||||
| <uses-permission | |||||
| android:name="android.permission.BLUETOOTH_SCAN" | |||||
| android:usesPermissionFlags="neverForLocation" | android:usesPermissionFlags="neverForLocation" | ||||
| tools:targetApi="s"/> | |||||
| tools:targetApi="s" /> | |||||
| <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" /> | <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" /> | ||||
| <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> | <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> | ||||
| <!-- 声明前台服务连接设备的权限(非必须) --> | <!-- 声明前台服务连接设备的权限(非必须) --> | ||||
| android:usesCleartextTraffic="true" | android:usesCleartextTraffic="true" | ||||
| tools:targetApi="q"> | tools:targetApi="q"> | ||||
| <activity android:name=".home.MainActivity" | |||||
| <activity | |||||
| android:name=".home.MainActivity" | |||||
| android:exported="true"> | android:exported="true"> | ||||
| <intent-filter> | <intent-filter> | ||||
| <action android:name="android.intent.action.VIEW" /> | <action android:name="android.intent.action.VIEW" /> | ||||
| <activity | <activity | ||||
| android:name=".TransmissionActivity" | android:name=".TransmissionActivity" | ||||
| android:screenOrientation="portrait" /> | android:screenOrientation="portrait" /> | ||||
| <activity | |||||
| android:name=".BroadcastBloodOxygenActivity" | |||||
| android:screenOrientation="portrait" /> | |||||
| <activity | <activity | ||||
| android:name=".modules.toothbrush.ToothBrushWifiOnOnBleActivity" | android:name=".modules.toothbrush.ToothBrushWifiOnOnBleActivity" | ||||
| android:screenOrientation="portrait" /> | android:screenOrientation="portrait" /> | ||||
| <activity | <activity | ||||
| android:name=".modules.toothbrush.ToothBrushWifiNewBleActivity" | android:name=".modules.toothbrush.ToothBrushWifiNewBleActivity" | ||||
| android:screenOrientation="portrait" /> | android:screenOrientation="portrait" /> | ||||
| <activity | |||||
| android:name=".modules.broadcastOxygen.BroadcastBloodOxygenActivity" | |||||
| android:screenOrientation="portrait" /> | |||||
| <provider | <provider | ||||
| android:name="androidx.core.content.FileProvider" | android:name="androidx.core.content.FileProvider" | ||||
| android:name="org.eclipse.paho.android.service.MqttService" | android:name="org.eclipse.paho.android.service.MqttService" | ||||
| android:enabled="true" | android:enabled="true" | ||||
| android:exported="true"></service> | android:exported="true"></service> | ||||
| <service android:name="com.pingwang.bluetoothlib.server.ELinkBleServer" | |||||
| android:foregroundServiceType="connectedDevice" | |||||
| /> | |||||
| </application> | </application> | ||||
| </manifest> | </manifest> |
| import aicare.net.cn.sdk.ailinksdkdemoandroid.BleCmdActivity; | import aicare.net.cn.sdk.ailinksdkdemoandroid.BleCmdActivity; | ||||
| import aicare.net.cn.sdk.ailinksdkdemoandroid.BloodOxygenActivity; | import aicare.net.cn.sdk.ailinksdkdemoandroid.BloodOxygenActivity; | ||||
| import aicare.net.cn.sdk.ailinksdkdemoandroid.BloodSugar4GActivity; | import aicare.net.cn.sdk.ailinksdkdemoandroid.BloodSugar4GActivity; | ||||
| import aicare.net.cn.sdk.ailinksdkdemoandroid.BroadcastBloodOxygenActivity; | |||||
| import aicare.net.cn.sdk.ailinksdkdemoandroid.BroadcastScaleActivity; | import aicare.net.cn.sdk.ailinksdkdemoandroid.BroadcastScaleActivity; | ||||
| import aicare.net.cn.sdk.ailinksdkdemoandroid.ConnectBleTestActivity; | import aicare.net.cn.sdk.ailinksdkdemoandroid.ConnectBleTestActivity; | ||||
| import aicare.net.cn.sdk.ailinksdkdemoandroid.HeightCmdActivity; | import aicare.net.cn.sdk.ailinksdkdemoandroid.HeightCmdActivity; | ||||
| import aicare.net.cn.sdk.ailinksdkdemoandroid.modules.blood_glucose.BloodGlucoseActivity; | import aicare.net.cn.sdk.ailinksdkdemoandroid.modules.blood_glucose.BloodGlucoseActivity; | ||||
| import aicare.net.cn.sdk.ailinksdkdemoandroid.modules.blood_pressure_tc.BloodPressureTcActivity; | import aicare.net.cn.sdk.ailinksdkdemoandroid.modules.blood_pressure_tc.BloodPressureTcActivity; | ||||
| import aicare.net.cn.sdk.ailinksdkdemoandroid.modules.body_scale_4g.BodyScale4GActivity; | import aicare.net.cn.sdk.ailinksdkdemoandroid.modules.body_scale_4g.BodyScale4GActivity; | ||||
| import aicare.net.cn.sdk.ailinksdkdemoandroid.modules.broadcastOxygen.BroadcastBloodOxygenActivity; | |||||
| import aicare.net.cn.sdk.ailinksdkdemoandroid.modules.broadcast_height.BroadcastHeightActivity; | import aicare.net.cn.sdk.ailinksdkdemoandroid.modules.broadcast_height.BroadcastHeightActivity; | ||||
| import aicare.net.cn.sdk.ailinksdkdemoandroid.modules.broadcast_nutrition.BroadNutritionActivity; | import aicare.net.cn.sdk.ailinksdkdemoandroid.modules.broadcast_nutrition.BroadNutritionActivity; | ||||
| import aicare.net.cn.sdk.ailinksdkdemoandroid.modules.broadcast_weight_sacle.BroadcastWeightScaleActivity; | import aicare.net.cn.sdk.ailinksdkdemoandroid.modules.broadcast_weight_sacle.BroadcastWeightScaleActivity; |
| package aicare.net.cn.sdk.ailinksdkdemoandroid; | |||||
| package aicare.net.cn.sdk.ailinksdkdemoandroid.modules.broadcastOxygen; | |||||
| import android.content.Context; | import android.content.Context; | ||||
| import android.os.Bundle; | import android.os.Bundle; | ||||
| import com.pingwang.bluetoothlib.bean.BleValueBean; | import com.pingwang.bluetoothlib.bean.BleValueBean; | ||||
| import com.pingwang.bluetoothlib.config.BleConfig; | import com.pingwang.bluetoothlib.config.BleConfig; | ||||
| import com.pingwang.bluetoothlib.listener.OnBleBroadcastDataListener; | |||||
| import com.pingwang.bluetoothlib.listener.OnCallbackDis; | import com.pingwang.bluetoothlib.listener.OnCallbackDis; | ||||
| import com.pingwang.bluetoothlib.listener.OnScanFilterListener; | |||||
| import com.pingwang.bluetoothlib.utils.BleDensityUtil; | import com.pingwang.bluetoothlib.utils.BleDensityUtil; | ||||
| import com.pingwang.bluetoothlib.utils.BleLog; | import com.pingwang.bluetoothlib.utils.BleLog; | ||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||
| import java.util.List; | import java.util.List; | ||||
| import java.util.UUID; | |||||
| import aicare.net.cn.sdk.ailinksdkdemoandroid.R; | |||||
| import aicare.net.cn.sdk.ailinksdkdemoandroid.base.BleBaseActivity; | import aicare.net.cn.sdk.ailinksdkdemoandroid.base.BleBaseActivity; | ||||
| import aicare.net.cn.sdk.ailinksdkdemoandroid.utils.TimeUtils; | import aicare.net.cn.sdk.ailinksdkdemoandroid.utils.TimeUtils; | ||||
| import cn.net.aicare.modulelibrary.module.BloodOxygen.BroadcastBloodOxygenBleConfig; | import cn.net.aicare.modulelibrary.module.BloodOxygen.BroadcastBloodOxygenBleConfig; | ||||
| import cn.net.aicare.modulelibrary.module.BloodOxygen.BroadcastBloodOxygenDeviceData; | |||||
| /** | /** | ||||
| * 2020/09/09<br> | * 2020/09/09<br> | ||||
| * 广播血氧仪 | * 广播血氧仪 | ||||
| */ | */ | ||||
| public class BroadcastBloodOxygenActivity extends BleBaseActivity implements OnCallbackDis, BroadcastBloodOxygenDeviceData.onNotifyData, OnScanFilterListener, View.OnClickListener { | |||||
| public class BroadcastBloodOxygenActivity extends BleBaseActivity implements OnCallbackDis, OnBleBroadcastDataListener, View.OnClickListener { | |||||
| private static String TAG = BroadcastBloodOxygenActivity.class.getName(); | private static String TAG = BroadcastBloodOxygenActivity.class.getName(); | ||||
| private final int REFRESH_DATA = 3; | private final int REFRESH_DATA = 3; | ||||
| private List<String> mList; | private List<String> mList; | ||||
| private ArrayAdapter listAdapter; | private ArrayAdapter listAdapter; | ||||
| private Context mContext; | private Context mContext; | ||||
| private BroadcastBloodOxygenDeviceData mDevice; | |||||
| private String mAddress = ""; | private String mAddress = ""; | ||||
| private Handler mHandler = new Handler(Looper.getMainLooper()) { | private Handler mHandler = new Handler(Looper.getMainLooper()) { | ||||
| mHandler.sendEmptyMessage(REFRESH_DATA); | mHandler.sendEmptyMessage(REFRESH_DATA); | ||||
| } else if (id == R.id.open) { | } else if (id == R.id.open) { | ||||
| if (mAILinkBleManager != null) { | if (mAILinkBleManager != null) { | ||||
| mAILinkBleManager.startScan(0, UUID.fromString("0000F0A0-0000-1000-8000-00805F9B34FB")); | |||||
| mAILinkBleManager.startScan(0, BleConfig.UUID_SERVER_BROADCAST_AILINK); | |||||
| } | } | ||||
| } else if (id == R.id.stop) { | } else if (id == R.id.stop) { | ||||
| if (mAILinkBleManager != null) { | if (mAILinkBleManager != null) { | ||||
| BleLog.i(TAG, "服务与界面建立连接成功"); | BleLog.i(TAG, "服务与界面建立连接成功"); | ||||
| //与服务建立连接 | //与服务建立连接 | ||||
| if (mAILinkBleManager != null) { | if (mAILinkBleManager != null) { | ||||
| mDevice = BroadcastBloodOxygenDeviceData.getInstance(); | |||||
| mDevice.setOnNotifyData(this); | |||||
| mAILinkBleManager.setOnScanFilterListener(this); | |||||
| mAILinkBleManager.addOnBleBroadcastDataListener(this); | |||||
| mAILinkBleManager.startScan(0, BleConfig.UUID_SERVER_BROADCAST_AILINK); | mAILinkBleManager.startScan(0, BleConfig.UUID_SERVER_BROADCAST_AILINK); | ||||
| } | } | ||||
| } | } | ||||
| @Override | @Override | ||||
| public void unbindServices() { | public void unbindServices() { | ||||
| if (mDevice != null) { | |||||
| mDevice.clear(); | |||||
| mDevice = null; | |||||
| } | |||||
| } | } | ||||
| //-----------------通知------------------- | //-----------------通知------------------- | ||||
| @Override | |||||
| public void getBloodOxygenData(int status, int bloodOxygen, int pulseRate, int plethysmogram, int power) { | public void getBloodOxygenData(int status, int bloodOxygen, int pulseRate, int plethysmogram, int power) { | ||||
| String statusStr = "状态="; | String statusStr = "状态="; | ||||
| mHandler.sendEmptyMessage(REFRESH_DATA); | mHandler.sendEmptyMessage(REFRESH_DATA); | ||||
| } | } | ||||
| @Override | |||||
| public void OnDID(int cid, int vid, int pid) { | public void OnDID(int cid, int vid, int pid) { | ||||
| String didStr = "cid:" + cid + "||vid:" + vid + "||pid:" + pid; | String didStr = "cid:" + cid + "||vid:" + vid + "||pid:" + pid; | ||||
| mList.add(TimeUtils.getTime() + "ID:" + didStr); | mList.add(TimeUtils.getTime() + "ID:" + didStr); | ||||
| @Override | @Override | ||||
| public boolean onFilter(BleValueBean bleValueBean) { | |||||
| return true; | |||||
| public void onBleBroadcastData(BleValueBean bleValueBean, byte[] payload) { | |||||
| if (bleValueBean.isBroadcastModule() && bleValueBean.getCid() == BroadcastBloodOxygenBleConfig.BROADCAST_BLOOD_OXYGEN) { | |||||
| if (TextUtils.isEmpty(mAddress) && bleValueBean.isBroadcastModule()) { | |||||
| mAddress = bleValueBean.getMac(); | |||||
| if (tv_broadcast_mac != null) { | |||||
| tv_broadcast_mac.setText(mAddress); | |||||
| } | |||||
| } | |||||
| //地址相同,并且是广播秤 | |||||
| if (mAddress.equalsIgnoreCase(bleValueBean.getMac()) && bleValueBean.isBroadcastModule()) { | |||||
| byte[] manufacturerData = bleValueBean.getManufacturerData(); | |||||
| int cid = bleValueBean.getCid(); | |||||
| int vid = bleValueBean.getVid(); | |||||
| int pid = bleValueBean.getPid(); | |||||
| OnDID(cid, vid, pid); | |||||
| dataCheck(payload); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| @Override | |||||
| public void onScanRecord(BleValueBean bleValueBean) { | |||||
| if (TextUtils.isEmpty(mAddress) && bleValueBean.isBroadcastModule()) { | |||||
| mAddress = bleValueBean.getMac(); | |||||
| if (tv_broadcast_mac != null) { | |||||
| tv_broadcast_mac.setText(mAddress); | |||||
| private int mOldNumberId = -1; | |||||
| /** | |||||
| * 校验解析数据 | |||||
| * | |||||
| * @param data Payload数据 | |||||
| */ | |||||
| public void dataCheck(byte[] data) { | |||||
| if (data == null) | |||||
| return; | |||||
| if (data.length >= 9) { | |||||
| int numberId = data[0] & 0xff;//数据ID | |||||
| if (mOldNumberId == numberId) { | |||||
| //数据相同,已处理过了.不再处理 | |||||
| return; | |||||
| } | } | ||||
| mOldNumberId = numberId; | |||||
| //0x00 :开始测试 | |||||
| //0x01 :正在测试 | |||||
| //0xFF :测试结束 | |||||
| int status = data[1] & 0xff; | |||||
| //0-100 % | |||||
| // 若该值无效,则为 0xFF | |||||
| int bloodOxygen = data[2] & 0xff;//血氧浓度百分比(SpO2 ,单位 %) | |||||
| //0-254 | |||||
| //若该值无效,则为 0xFF | |||||
| int pulseRate = data[3] & 0xff;//脉率(pulse rate ,单位 bpm) | |||||
| // | |||||
| int plethysmogram = data[4] & 0xff;//PI ( Plethysmogram 无单位 ,1 位小数 ) | |||||
| int power = data[5] & 0xff;//电量(power ,单位%) | |||||
| getBloodOxygenData(status, bloodOxygen, pulseRate, plethysmogram, power); | |||||
| } | } | ||||
| //地址相同,并且是广播秤 | |||||
| if (mAddress.equalsIgnoreCase(bleValueBean.getMac()) && bleValueBean.isBroadcastModule()) { | |||||
| byte[] manufacturerData = bleValueBean.getManufacturerData(); | |||||
| int cid = bleValueBean.getCid(); | |||||
| int vid = bleValueBean.getVid(); | |||||
| int pid = bleValueBean.getPid(); | |||||
| if (mDevice != null) | |||||
| mDevice.onNotifyData("",manufacturerData, cid, vid, pid); | |||||
| } | |||||
| } | } | ||||
| package aicare.net.cn.sdk.ailinksdkdemoandroid.modules.broadcast_nutrition; | package aicare.net.cn.sdk.ailinksdkdemoandroid.modules.broadcast_nutrition; | ||||
| import android.os.Bundle; | import android.os.Bundle; | ||||
| import android.util.Log; | |||||
| import android.view.View; | import android.view.View; | ||||
| import android.widget.ArrayAdapter; | import android.widget.ArrayAdapter; | ||||
| import android.widget.Button; | import android.widget.Button; | ||||
| import com.pingwang.bluetoothlib.bean.BleValueBean; | import com.pingwang.bluetoothlib.bean.BleValueBean; | ||||
| import com.pingwang.bluetoothlib.config.BleConfig; | import com.pingwang.bluetoothlib.config.BleConfig; | ||||
| import com.pingwang.bluetoothlib.listener.OnBleBroadcastDataListener; | import com.pingwang.bluetoothlib.listener.OnBleBroadcastDataListener; | ||||
| import com.pingwang.bluetoothlib.listener.OnScanFilterListener; | |||||
| import com.pingwang.bluetoothlib.utils.BleLog; | |||||
| import com.pingwang.bluetoothlib.utils.BleStrUtils; | |||||
| import com.pinwang.ailinkble.AiLinkPwdUtil; | |||||
| import java.math.BigDecimal; | import java.math.BigDecimal; | ||||
| import java.text.SimpleDateFormat; | import java.text.SimpleDateFormat; | ||||
| import aicare.net.cn.sdk.ailinksdkdemoandroid.R; | import aicare.net.cn.sdk.ailinksdkdemoandroid.R; | ||||
| import aicare.net.cn.sdk.ailinksdkdemoandroid.base.BleBaseActivity; | import aicare.net.cn.sdk.ailinksdkdemoandroid.base.BleBaseActivity; | ||||
| import aicare.net.cn.sdk.ailinksdkdemoandroid.utils.L; | |||||
| public class BroadNutritionActivity extends BleBaseActivity implements View.OnClickListener, OnScanFilterListener, OnBleBroadcastDataListener { | |||||
| public class BroadNutritionActivity extends BleBaseActivity implements View.OnClickListener, OnBleBroadcastDataListener { | |||||
| private ListView list_view; | private ListView list_view; | ||||
| private Button btn_start; | private Button btn_start; | ||||
| @Override | @Override | ||||
| public void onServiceSuccess() { | public void onServiceSuccess() { | ||||
| mAILinkBleManager.setOnScanFilterListener(this); | |||||
| L.i("绑定服务成功"); | |||||
| mAILinkBleManager.addOnBleBroadcastDataListener(this); | mAILinkBleManager.addOnBleBroadcastDataListener(this); | ||||
| } | } | ||||
| } | } | ||||
| @Override | |||||
| public boolean onFilter(BleValueBean bleValueBean) { | |||||
| if (bleValueBean.getCid() == 4) { | |||||
| return true; | |||||
| } | |||||
| return false; | |||||
| } | |||||
| private String mMac; | private String mMac; | ||||
| @Override | @Override | ||||
| public void onBleBroadcastData(BleValueBean bleValueBean, byte[] payload) { | public void onBleBroadcastData(BleValueBean bleValueBean, byte[] payload) { | ||||
| mMac = bleValueBean.getMac(); | |||||
| notifyData(payload); | |||||
| if (bleValueBean.getCid() == 0x04 && bleValueBean.isBroadcastModule()) { | |||||
| mMac = bleValueBean.getMac(); | |||||
| notifyData(payload); | |||||
| } | |||||
| } | } | ||||
| private String mOldText = ""; | private String mOldText = ""; | ||||
| mListAdapter.notifyDataSetChanged(); | mListAdapter.notifyDataSetChanged(); | ||||
| } | } | ||||
| private long mOldNumberId; | |||||
| /** | |||||
| * @param manufacturerData 自定义厂商数据0xFF后面的数据 | |||||
| * @param cid cid 设备类型 | |||||
| * @param vid vid | |||||
| * @param pid pid | |||||
| */ | |||||
| public void onNotifyData(String uuid, byte[] manufacturerData, int cid, int vid, int pid) { | |||||
| if (manufacturerData == null) { | |||||
| BleLog.i("Tag1", "接收到的数据:null"); | |||||
| return; | |||||
| } | |||||
| // if (manufacturerData.length >= 20) { | |||||
| byte sum = manufacturerData[9]; | |||||
| byte[] data = new byte[10]; | |||||
| System.arraycopy(manufacturerData, 10, data, 0, data.length); | |||||
| byte newSum = cmdSum(data); | |||||
| if (newSum == sum) { | |||||
| int numberId = data[0] & 0xff;//数据ID | |||||
| // if (mOldNumberId == numberId) { | |||||
| // //数据相同,已处理过了.不再处理 | |||||
| // return; | |||||
| // } | |||||
| mOldNumberId = numberId; | |||||
| byte[] bytes; | |||||
| if (cid != 0 || vid != 0 || pid != 0) { | |||||
| bytes = AiLinkPwdUtil.decryptBroadcast(cid, vid, pid, data); | |||||
| } else { | |||||
| bytes = data; | |||||
| } | |||||
| Log.i("Tag1", "接收到的数据:" + BleStrUtils.byte2HexStr(bytes)); | |||||
| notifyData(bytes); | |||||
| } else { | |||||
| Log.i("Tag1", "校验和错误"); | |||||
| } | |||||
| // } | |||||
| } | |||||
| /** | |||||
| * 校验累加,从1开始加 | |||||
| */ | |||||
| private byte cmdSum(byte[] data) { | |||||
| byte sum = 0; | |||||
| for (byte datum : data) { | |||||
| sum += datum; | |||||
| } | |||||
| return sum; | |||||
| } | |||||
| /** | /** | ||||
| * 解析数据 | * 解析数据 |