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);

results matching ""

    No results matching ""