Android
1. Plugin Integration Steps
1.1 Import Plugin to Project
1.1.1 dependencyResolutionManagement Management
- Add jitpack support in setting.gradle under the project root directory
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)
repositories {
//Declare repository address
maven { url 'https://jitpack.io' }
//Other repository configurations
...
}
}
- Add dependency in build.gradle under App directory
dependencies {
//Declare dependency, X.Y.Z needs to be modified to the specific version number
implementation("com.github.YolandaQingniu:qnscalesdkX:X.Y.Z")
//Other third-party dependencies
...
}
1.1.2 allprojects Management
- Add jitpack support in build.gradle under the project root directory
allprojects {
repositories {
//Other repository configurations
maven { url 'https://jitpack.io' }
//Other repository configurations
...
}
}
- Add dependency in build.gradle under App directory
dependencies {
//Declare dependency, X.Y.Z needs to be modified to the specific version number
implementation("com.github.YolandaQingniu:qnscalesdkX:X.Y.Z")
//Other third-party dependencies
...
}
1.2 Configure Project Bluetooth Permission Usage Instructions
If app's targetSdk>30 and the phone's system version is Android 12 and above, the following permissions are required
android.permission.BLUETOOTH_ADVERTISE
android.permission.BLUETOOTH_SCAN
android.permission.BLUETOOTH_CONNECT
Otherwise, the following permissions are required
android.permission.BLUETOOTH
android.permission.BLUETOOTH_ADMIN
android.permission.ACCESS_COARSE_LOCATION
android.permission.ACCESS_FINE_LOCATION
1.3 Obfuscation Configuration
-keep class com.qingniu.scale.model.BleScaleData{*;}
-keep class com.jieli.** {*;}
1.4 Initialize Plugin
Can be initialized in the onCreate lifecycle method in BaseApplication
//Get the authorization file path in the project
String encryptPath = "file:///android_asset/123456789.qn";
//Instantiate plugin, QNBleApi is a singleton object
QNBleApi bleApi = QNBleApi.getInstance(this);
//Get plugin authorization
bleApi.initSdk("123456789", encryptPath, new QNResultCallback() {
@Override
public void onResult(int code, String msg) {
}
});
1.5 Listen to System Bluetooth State Changes(QNBleStateListener)
/*
enum QNBLEState {
Unknown,
setting,
Unsupported,
Unauthiorized,
PoweredOff,
PoweredOn;
*/
bleApi.setBleStateListener(new QNBleStateListener() {
@Override
public void onBleSystemState(QNBLEState qnbleState) {
}
});
1.6 Listen to SDK Logs (QNLogListener)
```plain text bleApi.setLogListener(new QNLogListener() { @Override public void onLog(String log) { //SDK output logs will be returned here } });
# 2\. Device Scanning
## 2\.1 Start Bluetooth Scanning
In the business interface where you need to obtain Bluetooth devices, call the following method
```plain text
bleApi.startBleDeviceDiscovery(new QNResultCallback() {
@Override
public void onResult(int code, String msg) {
//The callback result here only indicates whether the method call to scan surrounding devices was successful
}
});
2.2 Get Scanned Bluetooth Devices
After successfully calling the SDK's Bluetooth scanning method, the SDK will return the scanned devices through the QNBleDeviceDiscoveryListener listener callback, and this listener can also obtain the SDK's Bluetooth scanning related status
Developers can register the listener callback according to the following example. It is recommended to register this callback listener only once in business maintenance
bleApi.setBleDeviceDiscoveryListener(new QNBleDeviceDiscoveryListener() {
@Override
public void onDeviceDiscover(QNBleDevice device) {
//When scanning is started and a device is discovered, this function will be called back. Only supported devices are called back here
//Here you can determine whether the device object is the target device. If it is the target device, it can be cached in memory for subsequent Bluetooth connection initiation
}
@Override
public void onStartScan() {
//When scanning is started, this function will be called back
}
@Override
public void onStopScan() {
//When scanning is stopped, this function will be called back
}
@Override
public void onScanFail(int code) {
//Callback for scan failure
}
});
3. Device Connection
3.1 Start Device Connection
After obtaining the target device object through step 2 scanning, you can call the device connection method as follows
//Continuous Bluetooth scanning consumes power, it is recommended to stop scanning before performing connection
//Google official recommendation: https://developer.android.com/develop/connectivity/bluetooth/ble/find-ble-devices?hl=zh-cn
bleApi.stopBleDeviceDiscovery(new QNResultCallback() {
@Override
public void onResult(int code, String msg) {
}
});
//Set scale unit
QNConfig sdkConfig = bleApi.getConfig()
sdkConfig.unit = 0; //0-kg, 1-lb, 2-Jin, 3-st:lb, 4-st,
sdkConfig.save();
QNUserScaleConfig config = new QNUserScaleConfig();
//List of registered scale users. During this connection, the scale will delete scale users not included in this array
ArrayList<QNUser> deviceUserList = new ArrayList<>();
config.setUserlist(deviceUserList);
//The following 3 descriptions correspond to different business logic, integrators configure as needed
//1. If using guest mode (i.e., no need to register user on the scale, the scale will not save this user's information), note that this field is mutually exclusive with user.index, isVisitor takes priority
//When this value is set to true, it means guest mode is used, and config.setRegist(boolean) and config.setChange(boolean) settings will be ignored
config.setVisitor(true)
int index = 0;
//2. If not using guest mode, then you need to tell the scale whether to register a user or access a scale user during this connection
//If registering a scale user
//Before registering a user, you need to verify whether the scale's user slots are full (the scale can store up to 8 users), which can be determined by QNBleDevice.this.getRegisteredUserNum()
//After successfully registering a scale user, the SDK will get the user.index returned by the scale, see registerUserComplete callback function.
//After successfully registering a scale user, you can receive real-time weight data and result data from the scale
config.setVisitor(false)
config.setRegist(true)
config.setChange(false)
int index = 0;
//3. If accessing a scale user
//The user information passed in when accessing a user will update the corresponding information saved by the scale. For example, if the scale saved 170cm for this user's height, and 171cm is passed in this time, the scale will update it to 171cm
//After successfully accessing a scale user, you can receive real-time weight data and result data from the scale
config.setVisitor(false)
config.setRegist(false)
config.setChange(true)
int index = 1; //Valid range [1,8], represents which position on the scale the user is accessing. This value is returned by the scale when registering a user, see registerUserComplete callback function.
//Build user information for this connection
String userId = "";//User unique identifier in business logic, used to distinguish users in business
int height = 170;//User height, unit cm
String gender = "male"; //User gender, "male" for male, "female" for female
Date birthday = new Date(631199317000L);//User birthday
QNUser user = bleApi.buildUser(userId, height, gender, birthday, 0, UserShape.SHAPE_NONE, UserGoal.GOAL_NONE, 0, index, 0,
new QNResultCallback() {
@Override
public void onResult(int code, String msg) {
}
});
//lastHmac needs to be data from the previous measurement with body fat percentage greater than 0 and of the same type as hmac. Same type can be determined by whether QNScaleData.this.getNewEightModel() is consistent
user.setHmac(lastHmac);
config.setCurUser(user);
bleApi.connectUserScaleDevice(device, config, new QNResultCallback() {
@Override
public void onResult(int code, String msg) {
}
});
3.2 Device Connection Status Listener(QNBleConnectionChangeListener)
bleApi.setBleConnectionChangeListener(new QNBleConnectionChangeListener() {
@Override
public void onConnecting(QNBleDevice device) {
//Callback when device is connecting
}
//Connected
@Override
public void onConnected(QNBleDevice device) {
//Callback when device is connected
}
@Override
public void onServiceSearchComplete(QNBleDevice device) {
//Device service search complete
}
@Override
public void onDisconnecting(QNBleDevice device) {
//Callback when device is disconnecting
}
@Override
public void onDisconnected(QNBleDevice device) {
//Callback when device is disconnected
}
@Override
public void onConnectError(QNBleDevice device, int errorCode) {
//Callback when device connection error occurs
}
@Override
public void onStartInteracting(QNBleDevice device) {
//Device can interact, i.e., corresponding operation commands can be sent to the device
}
});
4. Device Data Acquisition
4.1 Device Data Interaction Listener(QNUserScaleDataListener)
After executing step 3.1 and establishing Bluetooth connection with the device, the related data acquisition example is as follows
bleApi.setDataListener(new QNUserScaleDataListener() {
//Successfully registered user on the scale, callback with the user slot assigned by the scale
@Override
public void registerUserComplete(QNBleDevice device, QNUser user) {
//When registering a user, the slot assigned by the scale. The app should associate and save this slot, user id, and device mac. This index will be needed for scale user access measurement when this user connects to this device next time
int index = user.index;
}
//Callback for real-time weight during device measurement, weight is real-time body weight, unit kg
@Override
public void onGetUnsteadyWeight(QNBleDevice device, double weight) {
}
//Callback for measurement data when device measurement is complete, data is measurement data
@Override
public void onGetScaleData(QNBleDevice device, QNScaleData data) {
//Get complete measurement data after measurement completion. For new solution eight-electrode devices, you can determine whether this measurement data is abnormal
if (data.getNewEightModel() == 1) {
//Whether this measurement is abnormal, 0-normal; 1-abnormal
int isAbnormal = data.getEightIsAbnormal();
if (isAbnormal == 1) {
//Reason for this measurement abnormality, 0-normal, 1-hand contact abnormal; 2-leg contact abnormal; 3-hand and foot contact both abnormal
int reasonMask = data.getEightReasonMask();
//According to the abnormal reason, you can prompt the user to re-measure
//Refer to [Appendix - Eight Electrode Abnormal Measurement Prompt]
return;
}
}
//This measurement has normal contact for all body parts (hands and feet)
Date measureDate = data.getMeasureTime();
double weight = data.getWeight();//Measured weight
List<QNScaleItemData> allTarget = data.getAllItem();
for (QNScaleItemData item: allTarget) {
item.getType() //Indicator type, see QNScaleType
item.getValue() //Indicator value
}
//The business layer can also perform a secondary judgment on this measurement data, such as whether body fat was measured
//Or compare with the user's previous measurement data's weight/body fat, if the difference is outside a certain business threshold, you can also prompt the user to re-measure
//If the measurement data needs to be saved ultimately, the business logic needs to additionally save the hmac and newEightModel fields in the QNScaleData class, for judgment and input parameters during the next connection measurement
}
// Current access user stored data and unknown stored callback, storedDataList is stored data list, determine whether it is unknown measurement data through isDataComplete in QNScaleStoreData object, false is unknown measurement data, true is known measurement data
//1. For guest mode, all are unknown stored data;
//2. For user management mode, there are known stored data (i.e., stored data belonging to the current access user) and unknown stored data
//3. For unknown measurement data, you can notify related APP users to claim it, i.e., let APP users choose whether this data is theirs
//4. Converting stored data to measurement data is divided into two steps:
//4.1 Set the owner of this stored data (known stored data does not need this step) [storeData setUser:<#(QNUser *)#>];
//4.2 Convert stored data to measurement data [storeData generateScaleDataWithLastEightHmac: lastValidEightHmac]
//Note: lastValidEightHmac is the hmac of the data owner's previous (measurement where body fat percentage > 0 and is of the same type of eight-electrode device) measurement data, same type can be determined by QNScaleData.newEightModel attribute
@Override
public void onGetStoredScale(QNBleDevice device, List<QNScaleStoreData> storedDataList) {
//For unknown measurement data, you can notify relevant users and let users choose whether this data belongs to them
ArrayList<QNScaleStoreData> unknowStoreDataList = new ArrayList<>();
for (QNScaleStoreData storeData : storedDataList) {
if(!storeData.isDataComplete()){
unknowStoreDataList.add(storeData);
} else {
//Known stored data for current user, this stored data can be directly attributed to this user
QNScaleData scaleData = storeData.generateScaleData();
for (QNScaleItemData item: scaleData.getAllItem()) {
item.type //Indicator type, see QNScaleType
item.value //Indicator value
}
}
}
}
//Connection status during measurement
@Override
public void onScaleStateChange(QNBleDevice device, int status) {
}
});
5. Device Disconnection
5.1 Disconnect Device Connection
//If needed, you can actively call the disconnect device connection method
bleApi.disconnectDevice(bleApi, new QNResultCallback() {
@Override
public void onResult(int code, String msg) {
}
});
6. Data Calculation
6.1 Unknown Data Calculation, Recalculation
//User information for the user to whom the unknown stored data belongs
String userId = "";
int height = 170;//User height, unit cm
String gender = "male"; //User gender, "male" for male, "female" for female
Date birthday = new Date(631199317000L);//User birthday
QNUser user = mQNBleApi.buildUser(userId, height, gender, birthday, new QNResultCallback() {
@Override
public void onResult(int code, String msg) {
}
});
//hmac is the hmac in this unknown measurement data, i.e., hmac in QNScaleStoreData
//lastHmac needs to be data from the previous measurement with body fat percentage greater than 0 and of the same type as hmac. Same type can be determined by whether QNScaleData.this.getNewEightModel() is consistent
QNScaleData scaleData = mQNBleApi.calculateScaleDataByHmac(user, hmac,lastHmac);