| # 好牙SDK使用说明 - Android | |||||
| 最新版:[](https://jitpack.io/#elinkthings/ToothbrushSDKRepositoryAndroid) | |||||
| [aar包下载地址](https://github.com/elinkthings/ToothbrushSDKRepositoryAndroid/releases) | |||||
| ## 使用条件 | |||||
| 1,最低版本 android4.4(API 19) | |||||
| 2,设备所使用的蓝牙版本需要4.0及以上 | |||||
| 3,依赖环境androidx | |||||
| ## 导入SDK | |||||
| ``` | |||||
| repositories { | |||||
| flatDir { | |||||
| dirs 'libs' | |||||
| } | |||||
| } | |||||
| 步骤1.将JitPack存储库添加到您的构建文件中 | |||||
| 将其添加到存储库末尾的root build.gradle中: | |||||
| allprojects { | |||||
| repositories { | |||||
| ... | |||||
| maven { url 'https://jitpack.io' } | |||||
| } | |||||
| } | |||||
| 步骤2.添加依赖项 | |||||
| dependencies { | |||||
| implementation 'com.github.elinkthings:ToothbrushSDKRepositoryAndroid:+' | |||||
| } | |||||
| 也可以使用aar包依赖,请自行下载放到项目的libs中 | |||||
| ``` | |||||
| ## 权限设置 | |||||
| ``` | |||||
| <!--In most cases, you need to ensure that the device supports BLE.--> | |||||
| <uses-feature | |||||
| android:name="android.hardware.bluetooth_le" | |||||
| android:required="true"/> | |||||
| <uses-permission android:name="android.permission.BLUETOOTH"/> | |||||
| <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> | |||||
| <!--Android 6.0 and above. Bluetooth scanning requires one of the following two permissions. You need to apply at run time.--> | |||||
| <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> | |||||
| <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> | |||||
| <!--Optional. If your app need dfu function.--> | |||||
| <uses-permission android:name="android.permission.INTERNET"/> | |||||
| ``` | |||||
| > 6.0及以上系统必须要定位权限,且需要手动获取权限 | |||||
| ## 开始接入 | |||||
| - 首先给SDK配置key和secret,[申请地址](http://sdk.aicare.net.cn) | |||||
| ``` | |||||
| //在主项目的application中调用 | |||||
| ToothbrushSDK.getInstance().init(this,"key","secret"); | |||||
| ``` | |||||
| - 在AndroidManifest.xml application标签下面增加 | |||||
| ``` | |||||
| <application> | |||||
| ... | |||||
| <service android:name="aicare.net.cn.toothbrushlibrary.toothbrush.ToothbrushService"/> | |||||
| </application> | |||||
| ``` | |||||
| - 你可以直接让你自己的`Activity`类继承`BleProfileServiceReadyActivity` | |||||
| ``` | |||||
| public class MyActivity extends BleProfileServiceReadyActivity | |||||
| @Override | |||||
| protected void onCreate(Bundle savedInstanceState) { | |||||
| super.onCreate(savedInstanceState); | |||||
| //判断手机设备是否支持Ble | |||||
| if (!ensureBLESupported()) { | |||||
| T.showShort(this, R.string.not_support_ble); | |||||
| finish(); | |||||
| } | |||||
| //判断是否有定位权限,此方法没有进行封装具体代码可在demo中获得,也可按照自己方式去调用请求权限方法 | |||||
| initPermissions(); | |||||
| //判断蓝牙是否打开,若需要换样式,可自己去实现 | |||||
| if (!isBLEEnabled()) { | |||||
| showBLEDialog(); | |||||
| } | |||||
| } | |||||
| ``` | |||||
| ## 扫描设备,停止扫描设备,查看扫描状态 | |||||
| 与扫描相关的API如下,详情参考BleProfileServiceReadyActivity类,具体使用参考demo | |||||
| ``` | |||||
| //调用startScan方法开启扫描 | |||||
| startScan(); | |||||
| //getDevice(BrushDevice device)可以获取到符合协议的设备 | |||||
| @Override | |||||
| protected void getDevice(final BrushDevice device) { | |||||
| //符合协议的设备 | |||||
| } | |||||
| //调用stopScan方法停止扫描 | |||||
| stopScan(); | |||||
| ``` | |||||
| ## 连接设备,断开设备 | |||||
| 与连接相关的API如下,详情参考BleProfileServiceReadyActivity类,具体使用参考demo。 | |||||
| ``` | |||||
| //调用startConnectService(String address)方法去连接设备 需要传入设备mac地址对象,可以在getDevice()方法中获得 。 | |||||
| startConnectService(String address)//连接设备 | |||||
| //在onStateChanged可以获取到连接状态 | |||||
| @Override | |||||
| public void onStateChanged(String deviceAddress, int state) { | |||||
| super.onStateChanged(deviceAddress, state); | |||||
| //state 具体状态看类说明 | |||||
| //连接成功:(state = BleProfileService.STATE_CONNECTED) | |||||
| //断开连接:(state = BleProfileService.STATE_DISCONNECTED) | |||||
| //校验成功:(state = BleProfileService.STATE_INDICATION_SUCCESS) | |||||
| } | |||||
| //调用PabulumService.PabulumBinder类中disconnect方法去断开连接 | |||||
| binder.disconnect() | |||||
| ``` | |||||
| 使用`startConnectService`方法连接,,使用`onStateChanged`方法监听连接的状态,使用`onError`方法监听连接过程中的异常,以便于进行额外的处理和问题排查。使用`isConnected`方法判断连接是否已经建立。 | |||||
| ## 连接成功,接受设备返回的数据 | |||||
| 以下方法或接口可直接在继承BleProfileServiceReadyActivity类后自动获得 | |||||
| ``` | |||||
| //onServiceBinded方法中获得PabulumService.PabulumBinder的实例 | |||||
| @Override | |||||
| protected void onServiceBinded(BleProfileService.LocalBinder binder) { | |||||
| this.binder = (ToothbrushService.ToothbrushBinder) binder; | |||||
| } | |||||
| //电量信息 | |||||
| @Override | |||||
| public void getBatteryState(BatteryState batteryState) { | |||||
| } | |||||
| //设备信息 | |||||
| @Override | |||||
| public void onResultChange(int type, final String result) { | |||||
| } | |||||
| //档位信息 | |||||
| @Override | |||||
| protected void getGears(GearBean gearBean) { | |||||
| } | |||||
| //设备状态信息 | |||||
| @Override | |||||
| public void getResultState(int type, byte state) { | |||||
| } | |||||
| //历史记录条数 | |||||
| @Override | |||||
| public void getHistoryCount(int count) { | |||||
| } | |||||
| //历史记录数据 | |||||
| @Override | |||||
| public void getHistoryData(HistoryData historyData) { | |||||
| } | |||||
| //工作数据(连接状态下刷牙完成会回调) | |||||
| @Override | |||||
| public void getWorkData(WorkData workData) { | |||||
| } | |||||
| //版本号 | |||||
| @Override | |||||
| protected void getVersion(String version) { | |||||
| } | |||||
| //设备授权,可以确认连接的设备是哪个 | |||||
| @Override | |||||
| public void getAuthState(byte state) { | |||||
| //1,用户授权 | |||||
| //2,待授权 | |||||
| //3,已授权 | |||||
| } | |||||
| //档位信息 | |||||
| @Override | |||||
| protected void getGears(GearBean gearBean) { | |||||
| } | |||||
| //OTA升级成功 | |||||
| @Override | |||||
| public void onOtaSuccess() { | |||||
| } | |||||
| //OTA进度百分比 | |||||
| @Override | |||||
| public void onProgressChange(final float progress) { | |||||
| } | |||||
| ``` | |||||
| > 注意:这些接口或方法部分需要APP给体脂下发命令才会有返回数据. | |||||
| ## APP给设备下发指令 | |||||
| 在BleProfileServiceReadyActivity.onServiceBinded(ToothbrushService.ToothbrushBinder binder)获得ToothbrushService.ToothbrushBinder的实例,调用binder里面方法 | |||||
| ``` | |||||
| 查询工作状态:queryWorkState(); | |||||
| 查询电池状态:queryBatteryState(); | |||||
| 设置工作参数:setWorkParam(WorkState workState) | |||||
| 切换工作状态(开/关):changeWorkState(); | |||||
| 设置设备SN:setDeviceSN(String deviceSN); | |||||
| 查询设备SN:queryDeviceSN(); | |||||
| 设置时间:setTime(); | |||||
| 查询时间:queryTime(); | |||||
| 请求同步历史数据(查/删):operateHistory(byte command) | |||||
| //ToothbrushBleConfig.QUERY_HISTORY //查询 | |||||
| //ToothbrushBleConfig.CANCEL_HISTORY //取消 | |||||
| //ToothbrushBleConfig.DELETE_HISTORY //删除 | |||||
| OTA开始升级:startUpdate(String fileName);//注意权限 | |||||
| 升级成功后,重启设备:reboot(); | |||||
| 请求配对:requestPair(long timeMills); | |||||
| 查询模式档位信息:queryGear(); | |||||
| 设置DID:setDid(int did); | |||||
| 获取电压:getVoltage(); | |||||
| 设置电压:setVoltage(boolean status, byte[] data); | |||||
| 设置设备名: setName(String name); | |||||
| ``` | |||||
| ## 类说明 | |||||
| #### WorkState(工作状态) | |||||
| | 类型| 参数名| 说明 | |||||
| | ---|---|--- | |||||
| | byte | workState | 工作状态: 0x00(初始化); 0x01(启动); 0x02(结束); 0x03(低电,禁止启动); 0x04(充电中,禁止启动) | |||||
| | byte | strength | 力度: 0x00(力度正常); 0x01(力度过大) | |||||
| | byte | workNode | 工作节点: 0x00(0节点); 0x01(1节点); 0x02(节点); 0x03(节点); 0x04(节点) | |||||
| | String | deviceSN | 设备SN | |||||
| | boolean | isSetTime | 是否设置时间 | |||||
| | byte | operateHistoryCmd | 操作历史数据的指令 | |||||
| ``` | |||||
| ToothbrushBleConfig.QUERY_HISTORY //查询 | |||||
| ToothbrushBleConfig.CANCEL_HISTORY //取消 | |||||
| ToothbrushBleConfig.DELETE_HISTORY //删除 | |||||
| ``` | |||||
| #### WorkData(工作数据) | |||||
| | 类型| 参数名| 说明 | |||||
| | ---|---|--- | |||||
| | String | startTime | 起始时间(yyyy-MM-dd HH:mm:ss) | |||||
| | int | workTime | 工作时长 | |||||
| | int | overPowerCount | 力度过大次数 | |||||
| | int | overPowerTime | 力度过大时长(s) | |||||
| | int | leftBrushTime | 左边刷牙时长(s) | |||||
| | int | rightBrushTime | 右边刷牙时长(s) | |||||
| #### HistoryData(历史记录数据) | |||||
| | 类型| 参数名| 说明 | |||||
| | ---|---|--- | |||||
| | WorkData | workData | 工作数据 | |||||
| | int | id | 序号(可用于计算进度) | |||||
| #### BatteryState(电池状态) | |||||
| | 类型| 参数名| 说明 | |||||
| | ---|---|--- | |||||
| | byte | chargerState | 电源状态: 0x00 未知; 0x01 未插入充电器; 0x02 插入充电器 | |||||
| | byte | batteryState | 电池状态: 0x00 未知; 0x01 电池未充满; 0x02 电池充满 | |||||
| | byte | powerState | 电量状态: 0x00 未知; 0x01 低电关机; 0x02 电量正常; 0x03 低电提醒 | |||||
| | double | battery | 电量: 设备电压值 | |||||
| | int | maxElectricity | 峰值电流 | |||||
| | double | capacity | 电池容量 | |||||
| ## 版本历史 | |||||
| |版本号|更新时间|作者|更新信息| | |||||
| |:----|:---|:-----|-----| | |||||
| | 1.2.5 | 2020/05/29 | 陈福行 | 切换到androidx,修复已知bug | |||||
| ## FAQ | |||||
| - 扫描不到蓝牙设备? | |||||
| 1.查看App权限是否正常,6.0及以上系统必须要定位权限,且需要手动获取权限 | |||||
| 2.查看手机的定位服务是否开启,部分手机可能需要打开GPS | |||||
| 3.拔掉电池重启秤 | |||||
| 4.是否被其他手机连接(秤未被连接时,秤盘上蓝牙图标会不断闪烁) | |||||
| 5.ToothbrushService是否在AndroidManifest中注册 | |||||
| ## 联系我们 | |||||
| 深圳市易连物联网有限公司 | |||||
| 电话:0755-81773367 | |||||
| 官网:www.elinkthings.com | |||||
| 邮箱:app@elinkthings.com |
| apply plugin: 'com.android.application' | apply plugin: 'com.android.application' | ||||
| android { | android { | ||||
| compileSdkVersion 28 | |||||
| compileSdkVersion 29 | |||||
| defaultConfig { | defaultConfig { | ||||
| applicationId "aicare.net.cn.aibrushdemo" | applicationId "aicare.net.cn.aibrushdemo" | ||||
| minSdkVersion 19 | minSdkVersion 19 | ||||
| targetSdkVersion 28 | |||||
| targetSdkVersion 29 | |||||
| versionCode 1 | versionCode 1 | ||||
| versionName "1.0" | |||||
| testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" | |||||
| versionName "1.2.5" | |||||
| testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" | |||||
| } | } | ||||
| buildTypes { | buildTypes { | ||||
| release { | release { | ||||
| dependencies { | dependencies { | ||||
| implementation fileTree(include: ['*.jar'], dir: 'libs') | implementation fileTree(include: ['*.jar'], dir: 'libs') | ||||
| implementation 'com.android.support:appcompat-v7:28.0.0' | |||||
| implementation 'com.android.support.constraint:constraint-layout:1.1.3' | |||||
| implementation 'androidx.appcompat:appcompat:1.1.0' | |||||
| implementation 'androidx.constraintlayout:constraintlayout:1.1.3' | |||||
| testImplementation 'junit:junit:4.12' | testImplementation 'junit:junit:4.12' | ||||
| androidTestImplementation 'com.android.support.test:runner:1.0.2' | |||||
| androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' | |||||
| implementation files('libs/ToothbrushLibrary.jar') | |||||
| androidTestImplementation 'androidx.test:runner:1.1.0' | |||||
| androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0' | |||||
| implementation 'com.github.elinkthings:ToothbrushSDKRepositoryAndroid:1.2.5' | |||||
| } | } |
| package aicare.net.cn.aibrushdemo; | package aicare.net.cn.aibrushdemo; | ||||
| import android.content.Context; | import android.content.Context; | ||||
| import android.support.test.InstrumentationRegistry; | |||||
| import android.support.test.runner.AndroidJUnit4; | |||||
| import androidx.test.InstrumentationRegistry; | |||||
| import androidx.test.runner.AndroidJUnit4; | |||||
| import org.junit.Test; | import org.junit.Test; | ||||
| import org.junit.runner.RunWith; | import org.junit.runner.RunWith; |
| import android.app.Application; | import android.app.Application; | ||||
| import aicare.net.cn.toothbrushlibrary.ToothbrushSDK; | |||||
| import aicare.net.cn.toothbrushlibrary.utils.FileUtils; | import aicare.net.cn.toothbrushlibrary.utils.FileUtils; | ||||
| /** | /** | ||||
| public void onCreate() { | public void onCreate() { | ||||
| super.onCreate(); | super.onCreate(); | ||||
| FileUtils.createFileDirectories(DIRECTORY_NAME); | FileUtils.createFileDirectories(DIRECTORY_NAME); | ||||
| ToothbrushSDK.getInstance().init(this,"b61478fd839f1cb9","009a898a677594af529dfb4fc3"); | |||||
| } | } | ||||
| } | } |
| import android.net.Uri; | import android.net.Uri; | ||||
| import android.os.Bundle; | import android.os.Bundle; | ||||
| import android.provider.Settings; | import android.provider.Settings; | ||||
| import android.support.annotation.NonNull; | |||||
| import android.support.v4.app.ActivityCompat; | |||||
| import android.text.TextUtils; | import android.text.TextUtils; | ||||
| import android.util.Log; | import android.util.Log; | ||||
| import android.view.View; | import android.view.View; | ||||
| import java.util.Date; | import java.util.Date; | ||||
| import java.util.List; | import java.util.List; | ||||
| import aicare.net.cn.aibrushdemo.R; | |||||
| import aicare.net.cn.aibrushdemo.DeviceDialog.OnDeviceScanListener; | import aicare.net.cn.aibrushdemo.DeviceDialog.OnDeviceScanListener; | ||||
| import aicare.net.cn.aibrushdemo.utils.AppUtils; | import aicare.net.cn.aibrushdemo.utils.AppUtils; | ||||
| import aicare.net.cn.aibrushdemo.utils.SPUtils; | import aicare.net.cn.aibrushdemo.utils.SPUtils; | ||||
| import aicare.net.cn.toothbrushlibrary.utils.L; | import aicare.net.cn.toothbrushlibrary.utils.L; | ||||
| import aicare.net.cn.toothbrushlibrary.utils.ParseData; | import aicare.net.cn.toothbrushlibrary.utils.ParseData; | ||||
| import aicare.net.cn.toothbrushlibrary.utils.ToothbrushBleConfig; | import aicare.net.cn.toothbrushlibrary.utils.ToothbrushBleConfig; | ||||
| import androidx.annotation.NonNull; | |||||
| import androidx.core.app.ActivityCompat; | |||||
| public class MainActivity extends BleProfileServiceReadyActivity implements View.OnClickListener, | public class MainActivity extends BleProfileServiceReadyActivity implements View.OnClickListener, | ||||
| OnDeviceScanListener { | OnDeviceScanListener { | ||||
| devicesDialog.show(); | devicesDialog.show(); | ||||
| devicesDialog.startScanDevice(); | devicesDialog.startScanDevice(); | ||||
| } else { | } else { | ||||
| new android.support.v7.app.AlertDialog.Builder(this).setTitle(this.getString(R.string.hint)).setMessage(this.getString(R.string.permissions_server)).setPositiveButton(this.getString(R.string.ok), new DialogInterface.OnClickListener() { | |||||
| new androidx.appcompat.app.AlertDialog.Builder(this).setTitle(this.getString(R.string.hint)).setMessage(this.getString(R.string.permissions_server)).setPositiveButton(this.getString(R.string.ok), new DialogInterface.OnClickListener() { | |||||
| @Override | @Override | ||||
| public void onClick(DialogInterface dialog, int which) { | public void onClick(DialogInterface dialog, int which) { | ||||
| //引导用户至设置页手动授权 | //引导用户至设置页手动授权 | ||||
| if (ActivityCompat.shouldShowRequestPermissionRationale(this, permissions[0])) { | if (ActivityCompat.shouldShowRequestPermissionRationale(this, permissions[0])) { | ||||
| //权限请求失败,但未选中“不再提示”选项 | //权限请求失败,但未选中“不再提示”选项 | ||||
| new android.support.v7.app.AlertDialog.Builder(this).setTitle(this.getString(R.string.hint)).setMessage(this.getString(R.string.permissions)).setPositiveButton(this.getString(R.string.ok), new DialogInterface.OnClickListener() { | |||||
| new androidx.appcompat.app.AlertDialog.Builder(this).setTitle(this.getString(R.string.hint)).setMessage(this.getString(R.string.permissions)).setPositiveButton(this.getString(R.string.ok), new DialogInterface.OnClickListener() { | |||||
| @Override | @Override | ||||
| public void onClick(DialogInterface dialog, int which) { | public void onClick(DialogInterface dialog, int which) { | ||||
| //引导用户至设置页手动授权 | //引导用户至设置页手动授权 | ||||
| } else { | } else { | ||||
| //权限请求失败,选中“不再提示”选项 | //权限请求失败,选中“不再提示”选项 | ||||
| // T.showShort(MainActivity.this, "获取权限失败"); | // T.showShort(MainActivity.this, "获取权限失败"); | ||||
| new android.support.v7.app.AlertDialog.Builder(this).setTitle(this.getString(R.string.hint)).setMessage(this.getString(R.string.permissions)).setPositiveButton(this.getString(R.string.ok), new DialogInterface.OnClickListener() { | |||||
| new androidx.appcompat.app.AlertDialog.Builder(this).setTitle(this.getString(R.string.hint)).setMessage(this.getString(R.string.permissions)).setPositiveButton(this.getString(R.string.ok), new DialogInterface.OnClickListener() { | |||||
| @Override | @Override | ||||
| public void onClick(DialogInterface dialog, int which) { | public void onClick(DialogInterface dialog, int which) { | ||||
| //引导用户至设置页手动授权 | //引导用户至设置页手动授权 | ||||
| } | } | ||||
| @Override | |||||
| protected void getVoltage(String s) { | |||||
| } | |||||
| @Override | |||||
| protected void getCloseBroadcastVoltage(int i, int i1) { | |||||
| } | |||||
| private class SingleChoiceListener implements DialogInterface.OnClickListener { | private class SingleChoiceListener implements DialogInterface.OnClickListener { | ||||
| private int which; | private int which; |
| import android.content.pm.PackageManager; | import android.content.pm.PackageManager; | ||||
| import android.os.Bundle; | import android.os.Bundle; | ||||
| import android.os.Handler; | import android.os.Handler; | ||||
| import android.support.v7.app.AppCompatActivity; | |||||
| import androidx.appcompat.app.AppCompatActivity; | |||||
| import android.widget.TextView; | import android.widget.TextView; | ||||
| import aicare.net.cn.aibrushdemo.utils.AppUtils; | import aicare.net.cn.aibrushdemo.utils.AppUtils; |
| repositories { | repositories { | ||||
| google() | google() | ||||
| jcenter() | jcenter() | ||||
| maven { url 'https://jitpack.io' } | |||||
| } | } | ||||
| } | } | ||||
| # http://www.gradle.org/docs/current/userguide/build_environment.html | # http://www.gradle.org/docs/current/userguide/build_environment.html | ||||
| # Specifies the JVM arguments used for the daemon process. | # Specifies the JVM arguments used for the daemon process. | ||||
| # The setting is particularly useful for tweaking memory settings. | # The setting is particularly useful for tweaking memory settings. | ||||
| android.enableJetifier=true | |||||
| android.useAndroidX=true | |||||
| org.gradle.jvmargs=-Xmx1536m | org.gradle.jvmargs=-Xmx1536m | ||||
| # When configured, Gradle will run in incubating parallel mode. | # When configured, Gradle will run in incubating parallel mode. | ||||
| # This option should only be used with decoupled projects. More details, visit | # This option should only be used with decoupled projects. More details, visit |